c++ - que - constint
¿Por qué C++ me permite asignar un const char a un const char*? (8)
Para mi asombro, esto compila:
const char* c_str()
{
static const char nullchar = ''/0'';
return nullchar;
}
e introdujo un error en mi código. Afortunadamente, lo atrapé.
¿Esto es intencional por C ++ o un error del compilador? ¿Hay alguna razón por la cual el tipo de datos se ignora activamente?
Funcionó en Visual C ++ 2010 y GCC , pero no entiendo por qué debería funcionar, dada la aparente discrepancia del tipo de datos. (La static
tampoco es necesaria).
Como lo ha definido, nullchar
es una expresión constante entera con el valor 0.
El estándar C ++ 03 define una constante de puntero nulo como: "Una constante de puntero nulo es una expresión de constante integral (5.19) valor de r del tipo entero que se evalúa como cero". Para abreviar, su nullchar
es una constante de puntero nulo, lo que significa que puede convertirse implícitamente y asignarse a cualquier puntero.
Tenga en cuenta que todos esos elementos son necesarios para que la conversión implícita funcione. Por ejemplo, si ha usado ''/1''
lugar de ''/0''
, o si no ha especificado el calificador const
para nullchar
, no obtendría la conversión implícita: su asignación habría fallado.
La inclusión de esta conversión es intencional pero ampliamente conocida como indeseable. 0 como una constante de puntero nulo heredada de C. Estoy bastante seguro de que Bjarne y la mayoría del resto del comité estándar de C ++ (y la mayoría de la comunidad de C ++ en general) adorarían eliminar esta conversión implícita en particular, pero al hacerlo destruiría la compatibilidad con una gran cantidad de código C (probablemente cerca de todo).
Compila por la misma razón que esta compila
const char *p = 0; // OK
const int i = 0;
double *q = i; // OK
const short s = 0;
long *r = s; // OK
Las expresiones de la derecha tienen tipo int
y short
, mientras que el objeto que se inicializa es un puntero. ¿Te sorprende esto?
En el lenguaje C ++ (así como en C) las expresiones constantes integrales (ICE) con valor 0
tienen un estado especial (aunque las ICE se definen de manera diferente en C y C ++). Califican como constantes de puntero nulo . Cuando se utilizan en contextos de puntero, se convierten implícitamente en punteros nulos del tipo apropiado.
Type char
es un tipo integral, no muy diferente de int
en este contexto, por lo que un objeto const char
inicializado por 0
también es una constante de puntero nulo en C ++ (pero no en C).
Por cierto, escriba bool
en C ++ también es un tipo integral, lo que significa que un objeto const bool
inicializado por false
también es una constante de puntero nulo
const bool b = false;
float *t = b; // OK
Un informe de defectos posterior contra C ++ 11 ha cambiado la definición de constante de puntero nulo. Después de la corrección, la constante de puntero nulo solo puede ser "un literal entero con valor cero o un prvalue de tipo std :: nullptr_t". Las inicializaciones de puntero anteriores ya no están bien formadas en C ++ 11 después de la corrección.
Creo que podría ser el hecho de que el carácter nulo es común entre los tipos. Lo que está haciendo es establecer un puntero nulo cuando devuelve el carácter nulo. Esto fallaría si se usara cualquier otro carácter porque no está pasando la dirección del carácter al puntero, sino el valor del carácter. Nulo es un valor válido de puntero y carácter, por lo que se puede establecer un carácter nulo como puntero.
En resumen, null puede usarse por cualquier tipo para establecer un valor vacío, independientemente de si se trata de una matriz, un puntero o una variable.
Esta es una historia antigua: se remonta a C.
No hay palabra clave null
en C. Una constante de puntero nulo en C es:
- una expresión de constante integral con valor 0, como
0
,0L
,''/0''
(recuerde quechar
es un tipo integral),(2-4/2)
- dicha expresión se convierte en
void*
, like(void*)0
,(void*)0L
,(void*)''/0''
,(void*)(2-4/2)
La macro NULL
(¡no una palabra clave!) Se expande a dicha constante de puntero nulo.
En el primer diseño de C ++, solo se permitió la expresión de constante integral como una constante de puntero nulo. Recientemente std::nullptr_t
se agregó a C ++.
En C ++, pero no en C, una variable const
de tipo integral inicializada con una expresión de constante integral es una expresión de constante integral:
const int c = 3;
int i;
switch(i) {
case c: // valid C++
// but invalid C!
}
Entonces, un const char
inicializado con la expresión ''/0''
es una constante de puntero nulo:
int zero() { return 0; }
void foo() {
const char k0 = ''/0'',
k1 = 1,
c = zero();
int *pi;
pi = k0; // OK (constant expression, value 0)
pi = k1; // error (value 1)
pi = c; // error (not a constant expression)
}
¿Y crees que esto no es un diseño de lenguaje sano?
Actualizado para incluir partes relevantes del estándar C99 ... Según §6.6.6 ...
Una expresión constante entera tendrá un tipo entero y solo tendrá operandos que son constantes enteras, constantes de enumeración, constantes de caracteres,
sizeof
expresiones cuyos resultados son constantes enteras y constantes flotantes que son los operandos inmediatos de los moldes. Los operadores de transmisión en una expresión constante entera solo convertirán tipos aritméticos en tipos enteros, excepto como parte de un operando para el operadorsizeof
.
Algunas aclaraciones para programadores solo en C ++:
- C usa el término "constante" para lo que los programadores de C ++ conocen como "literal".
- En C ++,
sizeof
siempre es una constante de tiempo de compilación; pero C tiene matrices de longitud variable, por lo quesizeof
veces no es una constante de tiempo de compilación.
Entonces, vemos §6.3.2.3.3 estados ...
Una expresión constante entera con el valor 0, o una expresión de este tipo para escribir
void *
, se llama constante de puntero nulo . Si una constante de puntero nulo se convierte en un tipo de puntero , se garantiza que el puntero resultante, llamado puntero nulo , se compare desigual a un puntero a cualquier objeto o función.
Para ver qué tan antigua es esta funcionalidad, vea las partes duplicadas idénticas en el estándar C99 ...
§6.6.6
Una expresión constante entera tendrá un tipo entero y solo tendrá operandos que son constantes enteras, constantes de enumeración, constantes de caracteres,
sizeof
expresiones cuyos resultados son constantes enteras y constantes flotantes que son los operandos inmediatos de los moldes. Los operadores de transmisión en una expresión constante entera solo convertirán tipos aritméticos en tipos enteros, excepto como parte de un operando para el operadorsizeof
.
§6.3.2.3.3
Una expresión constante entera con el valor 0, o una expresión de este tipo para escribir
void *
, se llama constante de puntero nulo . Si una constante de puntero nulo se convierte en un tipo de puntero , se garantiza que el puntero resultante, llamado puntero nulo , se compare desigual a un puntero a cualquier objeto o función.
No está ignorando el tipo de datos. No es un error. Se está aprovechando de la configuración que pones allí y viendo que su valor es en realidad un número entero 0 (char es un tipo entero).
Entero 0 es una constante de puntero nulo válida (por definición), que se puede convertir a un tipo de puntero (se convierte en el puntero nulo).
Las razones por las que querría el puntero nulo es tener algún valor de puntero que "apunta a ninguna parte" y puede ser verificable (es decir, puede comparar un puntero nulo con un número entero 0, y se obtendrá a cambio).
Si sueltas la const, obtendrás un error. Si coloca el doble (como con muchos otros tipos no enteros, supongo que las excepciones son solo los tipos que pueden convertirse en const char * [a través de la sobrecarga de los operadores de conversión]), obtendrá un error (incluso sin el const). Etcétera.
Todo es que, en este caso, su implementación ve que está devolviendo una constante de ptr nula; que puedes convertir a un tipo de puntero.
Parece que mucha de la respuesta real a esta pregunta ha terminado en los comentarios. Para resumir:
El estándar C ++ permite que las variables
const
de tipo integral se consideren "expresiones constantes integrales". ¿Por qué? Es muy posible evitar el problema de que C solo permite que las macros y las enumeraciones ocupen el lugar de la expresión constante integral.Yendo (al menos) desde C89, una expresión integral constante con valor 0 es implícitamente convertible a (cualquier tipo de) puntero nulo. Y esto se usa a menudo en el código C, donde
NULL
es a menudo#define
''d as(void*)0
.Volviendo a K & R, el valor literal
0
se ha utilizado para representar punteros nulos. Esta convención se usa en todas partes, con un código como:if ((ptr=malloc(...)) {...} else {/* error */}
hay un elenco automático. si bien ejecutas este programa:
#include <stdio.h>
const char* c_str()
{
static const char nullchar = ''/0'';
return nullchar;
}
int main()
{
printf("%d" , sizeof(c_str()));
return 0;
}
la salida también será 4 en mi computadora -> del tamaño de un puntero.
el auto compilador lanza. aviso, al menos gcc da una advertencia (no sé sobre VS)
nullchar
es una expresión constante (en tiempo de compilación), con valor 0. Por lo tanto, es un juego justo para la conversión implícita a un puntero nulo.
Más detalladamente: estoy citando un borrador de norma de 1996 aquí.
char
es un tipo integral. nullchar
es const, por lo que es una expresión de constante integral (en tiempo de compilación), según la sección 5.19.1:
5.19 Expresiones constantes [expr.const]
1 En varios lugares, C ++ requiere expresiones que evalúen una constante global o de enumeración ... Una expresión constante integral puede involucrar ... variables const ...
Además, nullchar
evalúa a 0, lo que permite convertirlo implícitamente en un puntero, como se indica en la sección 4.10.1:
4.10 Conversiones del puntero [conv.ptr]
1 Una expresión de constante integral ( expr.const ) rvalue de tipo entero que se evalúa a cero (llamada constante de puntero nulo) se puede convertir a un tipo de puntero.
Quizás una razón intuitiva de "por qué" esto podría permitirse (solo fuera de mi cabeza) es que el ancho del puntero no está especificado, por lo que se permite la conversión de cualquier expresión constante de cualquier tamaño a un puntero nulo.
Actualizado con las partes relevantes del estándar (más nuevo) C ++ 03 ... Según §5.19.1 ...
Una expresión de constante integral puede implicar solo literales (2.13), enumeradores, variables
const
o miembros de datos estáticos de tipos integrales o de enumeración inicializados con expresiones constantes (8.5), parámetros de plantilla sin tipo de tipos integrales o de enumeración ysizeof
expresiones.
Luego, miramos a §4.10.1 ...
Una constante de puntero nulo es una expresión de constante integral (5.19) rvalor de tipo entero que se evalúa como cero. Una constante de puntero nulo se puede convertir a un tipo de puntero; el resultado es el valor de puntero nulo de ese tipo y se puede distinguir de cualquier otro valor de puntero a objeto o puntero a tipo de función. Dos valores de puntero nulo del mismo tipo se compararán iguales.