Este podría ser un problema muy básico, pero no pude hacerlo. Aquí es con lo que estoy trabajando.

#include <stdio.h>

int main(void)
{
    char c1, c2;
    int s;
    c1 = 128;
    c2 = -128;

    s = sizeof(char);

    printf("size of char: %d\n", s);
    printf("c1: %x, c2: %x\n", c1, c2);
    printf("true or false: %d\n", c1 == c2);
}

El resultado es así.

size of char: 1
c1: ffffff80, c2: ffffff80
true or false: 1

Asigné el valor 128 al tipo de char con signo (normal), pero no se desbordó.

Además, c1 y c2 parecen contener 4 bytes, y -128 y 128 son el mismo valor.

¿Cómo puedo entender estos hechos? Necesito tu ayuda. Muchas gracias.

-2
agongji 15 jun. 2020 a las 19:44

4 respuestas

La mejor respuesta

En c1 = 128;, 128 no cabe en el char de ocho bits firmado que usa su implementación en C. 128 se convierte a char por C 2018 6.5.16.1 2: "el valor del operando correcto se convierte al tipo de la expresión de asignación ..."

La conversión está definida por la implementación, según 6.3.1.3 3: "De lo contrario, el nuevo tipo está firmado y el valor no puede representarse en él; el resultado está definido por la implementación o se genera una señal definida por la implementación". Su implementación en C convirtió 128, que es 10000000 2 como un número binario sin signo, a −128, que se representa con los mismos bits cuando se usa el complemento de dos para binario con signo. Por lo tanto, el resultado es que c1 contiene el valor −128.

En printf("c1: %x, c2: %x\n", c1, c2);, c1 se convierte en int. Esto se debe a que las reglas para llamar a funciones con parámetros ... son aplicar las promociones de argumento por defecto a los argumentos correspondientes, según 6.5.2.2 7: "Las promociones de argumento por defecto se realizan en argumentos finales".

Las promociones de argumento por defecto incluyen las promociones de enteros, según 6.5.2.2 6. Cuando el rango de char es más estrecho que int, como lo es en la mayoría de las implementaciones de C, las promociones de enteros convierten un {{X2} } a un int, según 6.3.1.1 2: "Si un int puede representar todos los valores del tipo original ..., el valor se convierte en un int ..."

Por lo tanto, en printf("c1: %x, c2: %x\n", c1, c2);, se pasa un valor int de −128 como segundo argumento. Su implementación C utiliza el complemento de dos de 32 bits para int, en el que −128 se representa con los bits 11111111111111111111111110000000, que podemos expresar en hexadecimal como ffffff80.

La cadena de formato especifica una conversión usando %x. El tipo de argumento adecuado para %x es unsigned int. Sin embargo, su implementación en C ha aceptado int y ha reinterpretado sus bits como unsigned int. Por lo tanto, los bits 11111111111111111111111110000000 se convierten a la cadena "ffffff80".

Esto explica por qué se imprime "ffffff80". No es porque c1 tenga cuatro bytes sino porque se convirtió a un tipo de cuatro bytes antes de pasar a printf. Además, la conversión de un valor negativo a ese tipo de cuatro bytes dio como resultado cuatro bytes con muchos bits establecidos.

Con respecto a c1 == c2 evaluando a verdadero (1), esto es simplemente porque c1 recibió el valor −128 como se explicó anteriormente, y c2 = -128; también asigna el valor −128 a c2 , entonces c1 y c2 tienen el mismo valor.

5
Eric Postpischil 15 jun. 2020 a las 17:17

Aquí se explica: https://en.wikipedia.org/wiki/Signed_number_representations

Si -128 y 128 y todos los números intermedios se representaran con un byte, tendríamos 257 números en ese conjunto. Sin embargo, no lo hacemos, son solo 256.

Su mapeado es el siguiente decimal: [0..127, -128 ..- 1] => [0b00000000..0b11111111]. Tenga en cuenta que el primer bit se convierte en 1 a -128, feliz accidente;).

Además, el formato de su cadena es incorrecto, su compilador debería advertirle que% x espera 4 bytes. Si tiene en cuenta lo que dije antes, verá que 0x80 es de hecho 0b10000000.

0
Troepje 15 jun. 2020 a las 16:58

El tipo char puede comportarse como el tipo signed char o como el tipo unsigned char según una opción del compilador o la configuración predeterminada del compilador.

En su caso, el tipo char se comporta como el tipo signed char. En este caso, CHAR_MIN es igual a -128 y CHAR_MAX es igual a 127.

Por lo tanto, un objeto del tipo char no puede contener el número positivo 128. Internamente, este valor tiene la siguiente representación hexadecimal 0x80. Por lo tanto, almacenado en un objeto del tipo char, se interpreta como un valor negativo porque se establece el bit de signo. Este valor negativo es -128.

Entonces, después de estas declaraciones

c1 = 128;
c2 = -128;

Ambos objetos tienen el mismo valor igual a -128.

Y la salida

c1: ffffff80, c2: ffffff80

De esta llamada

printf("c1: %x, c2: %x\n", c1, c2);

Muestra que los dos objetos c1 y c2 promovidos al tipo int tienen la misma representación de un valor negativo.

Preste atención a que es un comportamiento definido por la implementación asignar un objeto del tipo firmado con un valor positivo que no se puede representar en el objeto.

2
Vlad from Moscow 15 jun. 2020 a las 17:32

128 y -128 tienen exactamente la misma representación binaria. Es por eso que (en este caso) 128 == -128 es cierto. La asignación de 128 a caracteres firmados puede o no generar advertencia de tiempo de compilación. Depende del compilador y su configuración. La razón por la que ve 4 bytes cuando se utiliza el especificador printf %x es porque los argumentos variables 'más pequeños' que int se promueven a int en C y C ++. Entonces, el signo de 1 byte 0x80 extendido a 4 bytes es 0xFFFFFF80.

-3
pvc 15 jun. 2020 a las 17:01