resueltos nombres matriz funciones ejercicios ejemplos caracteres cadenas cadena arreglo almacenar c++ c char sizeof

c++ - nombres - ¿Por qué los literales de caracteres C son en vez de caracteres?



funciones de cadenas de caracteres en c++ (11)

En C ++, sizeof(''a'') == sizeof(char) == 1 . Esto tiene un sentido intuitivo, ya que ''a'' es un carácter literal, y sizeof(char) == 1 como lo define el estándar.

Sin embargo, en C, sizeof(''a'') == sizeof(int) . Es decir, parece que los literales de caracteres C en realidad son enteros. ¿Alguien sabe por qué? Puedo encontrar muchas menciones de esta peculiaridad C pero no hay explicación de por qué existe.


Cuando se escribía C, el lenguaje de ensamblaje MACRO-11 del PDP-11 tenía:

MOV #''A, R0 // 8-bit character encoding for ''A'' into 16 bit register

Este tipo de cosas es bastante común en lenguaje ensamblador: los 8 bits bajos mantendrán el código del carácter, otros bits se borrarán a 0. PDP-11 incluso tenía:

MOV #"AB, R0 // 16-bit character encoding for ''A'' (low byte) and ''B''

Esto proporcionó una manera conveniente de cargar dos caracteres en los bytes bajos y altos del registro de 16 bits. Luego puede escribirlos en otro lugar, actualizando algunos datos de texto o memoria de pantalla.

Entonces, la idea de que los personajes sean promocionados para registrar el tamaño es bastante normal y deseable. Pero, supongamos que necesita obtener ''A'' en un registro no como parte del código de operación codificado, sino desde algún lugar de la memoria principal que contenga:

address: value 20: ''X'' 21: ''A'' 22: ''A'' 23: ''X'' 24: 0 25: ''A'' 26: ''A'' 27: 0 28: ''A''

Si quiere leer solo una ''A'' de esta memoria principal en un registro, ¿cuál leería?

  • Algunas CPU solo admiten directamente la lectura de un valor de 16 bits en un registro de 16 bits, lo que significa que una lectura a 20 o 22 requeriría que los bits de ''X'' se borren, y dependiendo de la endianidad de la CPU, uno u otro necesitaría cambiar al byte de bajo orden.

  • Algunas CPU pueden requerir una lectura alineada con la memoria, lo que significa que la dirección más baja involucrada debe ser un múltiplo del tamaño de datos: es posible que pueda leer desde las direcciones 24 y 25, pero no desde 27 y 28.

Por lo tanto, un compilador que genera código para obtener una ''A'' en el registro puede preferir desperdiciar un poco de memoria adicional y codificar el valor como 0 ''A'' o ''A'' 0, dependiendo de la endianidad, y también asegurarse de que esté alineado correctamente ( es decir, no en una dirección de memoria impar).

Mi suposición es que C simplemente ha llevado este nivel de comportamiento centrado en CPU, pensando en las constantes de caracteres que ocupan los tamaños de registro de la memoria, llevando a cabo la evaluación común de C como un "ensamblador de alto nivel".

(Véase 6.3.3 en la página 6-25 de http://www.dmv.net/dec/pdf/macro.pdf )


Este es el comportamiento correcto, llamado "promoción integral". También puede suceder en otros casos (principalmente operadores binarios, si no recuerdo mal).

EDITAR: Solo para estar seguro, revisé mi copia de Expert C Programming: Deep Secrets , y confirmé que un char literal no comienza con un tipo int . Inicialmente es de tipo char pero cuando se usa en una expresión , se promueve a un int . Lo siguiente es citado del libro:

Los literales de caracteres tienen tipo int y llegan siguiendo las reglas de promoción de tipo char. Esto se cubre demasiado brevemente en K & R 1, en la página 39 donde dice:

Cada carácter en una expresión se convierte en un int ... Tenga en cuenta que todos los elementos flotantes de una expresión se convierten a doble ... Como un argumento de función es una expresión, las conversiones de tipo también tienen lugar cuando los argumentos se pasan a funciones: en en particular, char y short se convierten en int, float se vuelve doble.


Esto es solo tangencial a la especificación del idioma, pero en el hardware, la CPU generalmente solo tiene un tamaño de registro, 32 bits, digamos, así que cada vez que funciona en un char (al sumarlo, restarlo o compararlo) hay una conversión implícita a int cuando se carga en el registro. El compilador se encarga de enmascarar y desplazar correctamente el número después de cada operación para que, si agrega, digamos, 2 a (carácter sin signo) 254, se ajuste a 0 en lugar de a 256, pero dentro del silicio es realmente una int hasta que lo guarde de nuevo en la memoria.

Es una especie de punto académico porque el lenguaje podría haber especificado un tipo literal de 8 bits de todos modos, pero en este caso la especificación del lenguaje refleja más de cerca lo que realmente está haciendo la CPU.

(x86 wonks puede notar que hay, por ejemplo, un addh op nativo que agrega los registros cortos anchos en un solo paso, pero dentro del núcleo RISC esto se traduce en dos pasos: agregar los números, luego extender signo, como un par agregar / extsh en el PowerPC)


La pregunta original es "¿por qué?"

La razón es que la definición de un carácter literal ha evolucionado y cambiado, al tratar de permanecer compatible con el código existente.

En los días oscuros de principios de C no había tipos en absoluto. Cuando aprendí por primera vez a programar en C, se introdujeron los tipos, pero las funciones no tenían prototipos que le dijeran a la persona que llama cuáles eran los tipos de argumento. En su lugar, se estandarizó que todo pasaba como un parámetro o bien sería del tamaño de un int (esto incluía todos los punteros) o sería un doble.

Esto significaba que cuando escribía la función, todos los parámetros que no eran dobles se almacenaban en la pila como ints, sin importar cómo los declarara, y el compilador ponía código en la función para manejar esto por usted.

Esto hizo que las cosas fueran un tanto inconsistentes, así que cuando K & R escribió su famoso libro, establecieron la regla de que un carácter literal siempre sería promovido a un int en cualquier expresión, no solo un parámetro de función.

Cuando el comité ANSI estandarizó por primera vez a C, cambiaron esta regla para que un literal de carácter simplemente fuera un int, ya que esto parecía una forma más simple de lograr lo mismo.

Cuando se diseñaba C ++, se requería que todas las funciones tuvieran prototipos completos (esto todavía no se requiere en C, aunque se acepta universalmente como una buena práctica). Debido a esto, se decidió que un carácter literal podría almacenarse en un char. La ventaja de esto en C ++ es que una función con un parámetro char y una función con un parámetro int tienen diferentes firmas. Esta ventaja no es el caso en C.

Es por eso que son diferentes. Evolución...


No he visto una razón para ello (los literales C char son tipos enteros), pero esto es algo que Stroustrup tuvo que decir al respecto (de Design and Evolution 11.2.1 - Fine-Grain Resolution):

En C, el tipo de un carácter literal como ''a'' es int . Sorprendentemente, dar char ''a'' en C ++ no causa ningún problema de compatibilidad. Excepto por el ejemplo patológico sizeof(''a'') , cada construcción que se puede expresar tanto en C como en C ++ da el mismo resultado.

Entonces, en su mayor parte, no debería causar problemas.


No lo sé, pero supongo que fue más fácil implementarlo de esa manera y realmente no importaba. No fue hasta C ++ cuando el tipo podría determinar qué función se llamaría que necesitaba ser reparada.


No sé las razones específicas por las que un carácter literal en C es de tipo int. Pero en C ++, hay una buena razón para no ir por ese camino. Considera esto:

void print(int); void print(char); print(''a'');

Es de esperar que la llamada a imprimir seleccione la segunda versión tomando una char. Tener un carácter literal siendo un int lo haría imposible. Tenga en cuenta que en C ++ literales que tienen más de un carácter todavía tienen tipo int, aunque su valor es la implementación definida. Entonces, ''ab'' tiene tipo int , mientras que ''a'' tiene tipo char .


Recuerdo leer K & R y ver un fragmento de código que leería un personaje a la vez hasta que llegara a EOF. Dado que todos los caracteres son caracteres válidos para estar en una secuencia de archivo / entrada, esto significa que EOF no puede tener ningún valor de char. Lo que hizo el código fue poner el carácter de lectura en un int, luego probar EOF, luego convertir a un char si no fuera así.

Me doy cuenta de que esto no responde exactamente a su pregunta, pero tendría sentido que el resto de los literales de los caracteres fueran sizeof (int) si el literal EOF fuera.

int r; char buffer[1024], *p; // don''t use in production - buffer overflow likely p = buffer; while ((r = getc(file)) != EOF) { *(p++) = (char) r; }


Usando gcc en mi MacBook, intento:

#include <stdio.h> #define test(A) do{printf(#A":/t%i/n",sizeof(A));}while(0) int main(void){ test(''a''); test("a"); test(""); test(char); test(short); test(int); test(long); test((char)0x0); test((short)0x0); test((int)0x0); test((long)0x0); return 0; };

que cuando se ejecuta da:

''a'': 4 "a": 2 "": 1 char: 1 short: 2 int: 4 long: 4 (char)0x0: 1 (short)0x0: 2 (int)0x0: 4 (long)0x0: 4

lo que sugiere que un personaje tiene 8 bits, como sospecha, pero un carácter literal es un int.


Yo no sabía esto de hecho. Antes de que existieran los prototipos, cualquier cosa más estrecha que una int se convertía en una int cuando se usaba como un argumento de función. Eso puede ser parte de la explicación.


discusión sobre el mismo tema

"Más específicamente, las promociones integrales. En K & R C era virtualmente (?) Imposible utilizar un valor de carácter sin que se promocionara a int primero, por lo que hacer que el carácter sea constante int en primer lugar eliminó ese paso. Hubo y aún hay carácter múltiple constantes como ''abcd'' o cualquiera que encaje en una int. "