ProgramaciónProgramador de sistemas en C

Explique las características de la conversión de tipos entre números con diferentes signos (signed/unsigned) en C, qué trampas pueden surgir, cómo evitar consecuencias imprevistas al realizar operaciones aritméticas y comparaciones de tipos signed/unsigned, y cómo esto afecta la portabilidad de los programas.

Supere entrevistas con el asistente de IA Hintsage

Respuesta

En C, la conversión automática de tipos funciona según el principio de "conversaciones aritméticas usuales". Al participar en una expresión números declarados con diferentes signos (signed/unsigned), se producen transformaciones según las siguientes reglas:

  • Si uno de los operandos es unsigned y el otro es signed, el signed se convierte automáticamente a unsigned.
  • Esto puede conducir a desbordamientos inesperados, especialmente al comparar o realizar operaciones aritméticas.
  • El tamaño de los tipos también influye: si uno unsigned es mayor en bits, el signed se convierte a unsigned.

Ejemplo de aritmética peligrosa:

int a = -1; // signed unsigned int b = 1; printf("%d\n", a < b); // siempre false, ya que 'a' se convierte en un unsigned muy grande

El resultado: -1, al ser convertido a unsigned, se convierte en un número positivo muy grande.

Lo que es importante recordar:

  • Siempre convertir explícitamente los tipos si hay posibilidad de confusión con el signo.
  • Prestar atención a los tamaños de los tipos (int, long, uint32_t, etc.) para que las conversiones sean predecibles.
  • Separar la lógica de trabajo con variables signed y unsigned, especialmente en verificaciones de límites y aritmética.

Pregunta capciosa

Pregunta: ¿Qué resultado devolverá la expresión (int)(unsigned)-1?

Respuesta inesperadamente incorrecta: "-1, ya que -1 se convierte a int."

Respuesta correcta: En la expresión (unsigned)-1, primero se realiza la conversión de -1 a unsigned (en una plataforma de 32 bits esto es 0xFFFFFFFF), luego se convierte de nuevo a signed int, lo cual también depende de la implementación, pero a menudo esto resulta nuevamente en -1 (si se usa el complemento a dos). Sin embargo, es más correcto decir: El resultado depende de los estándares de representación de números signed, pero en la mayoría de las implementaciones será -1.

Ejemplo:

int x = (int)(unsigned)-1; // x == -1 en la mayoría de las plataformas

Ejemplos de errores reales debido al desconocimiento de los matices del tema


Historia

En el manejador de cadenas se utilizó una función para comparar tamaños: si la longitud de la cadena puede ser negativa, el programa reportaría un error. Sin embargo, la longitud era de tipo size_t (unsigned), y la comparación if(length < 0) siempre devuelve false, lo que llevó a un bucle infinito y desbordamiento de memoria.


Historia

Al analizar un protocolo, los paquetes de red contenían campos como unsigned, mientras que las variables locales eran signed. Debido al desbordamiento unsigned al procesar ciertos valores, se produjeron cálculos incorrectos de longitud del paquete, lo que resultó en una vulnerabilidad de desbordamiento de búfer.


Historia

El módulo de comparación de fechas en los registros almacenaba la fecha como unsigned int, mientras buscaba el rango de fechas en int. Algunos valores límite, en lugar de generar la excepción esperada, llevaron a un filtrado incorrecto de los registros y pérdida de registros importantes.