El siguiente código se compila sin problemas:
int main() {
printf("Hi" "Bye");
}
Sin embargo, esto no compila:
int main() {
int test = 0;
printf("Hi" (test ? "Bye" : "Goodbye"));
}
¿Cuál es la razón para eso?
c
string
syntax
concatenation
conditional-operator
José D.
fuente
fuente
"Hi"
y"Bye"
son cadenas literales , no cadenas como se usa en la biblioteca estándar de C. Con cadenas literales , el compilador concatenará"H\0i" "B\0ye"
. No es lo mismo consprintf(buf,"%s%s", "H\0i" "B\0ye");
a (some_condition ? + : - ) b
printf("Hi" ("Bye"));
funcionará, no requiere el operador ternario; el paréntesis es suficiente (aunqueprintf("Hi" test ? "Bye" : "Goodbye")
tampoco se compilaría). Solo hay un número limitado de tokens que pueden seguir a un literal de cadena. La coma,
, el corchete abierto, el corchete[
cerrado]
(como en1["abc"]
- y sí, es espantoso), el corchete redondo)
cerrado, el corchete cerrado}
(en un inicializador o contexto similar) y el punto y coma;
son legítimos (y otro literal de cadena); No estoy seguro de que haya otros.Respuestas:
Según el Estándar C (5.1.1.2 Fases de traducción)
Y solo despues de eso
En esta construcción
no hay tokens literales de cadena adyacentes. Entonces esta construcción no es válida.
fuente
(test ? "Bye" : "Goodbye")
evaluarse a cualquiera de los literales de cadena que esencialmente hacen"Hi" "Bye"
o"Hi Goodbye"
? (mi pregunta se responde en las otras respuestas)Según el estándar C11, capítulo §5.1.1.2, concatenación de cadenas literales adyacentes:
ocurre en la fase de traducción . Por otra parte:
involucra al operador condicional, que se evalúa en tiempo de ejecución . Entonces, en tiempo de compilación, durante la fase de traducción, no hay presentes literales de cadena adyacentes, por lo que la concatenación no es posible. La sintaxis no es válida y, por lo tanto, su compilador la informa.
Para desarrollar un poco la parte del por qué , durante la fase de preprocesamiento, los literales de cadena adyacentes se concatenan y representan como un literal de cadena única (token). El almacenamiento se asigna en consecuencia y el literal de cadena concatenado se considera como una sola entidad (un literal de cadena).
Por otro lado, en caso de concatenación en tiempo de ejecución, el destino debe tener suficiente memoria para contener la cadena literal concatenada; de lo contrario, no habrá forma de que se pueda acceder a la salida concatenada esperada . Ahora, en el caso de los literales de cadena , que están ya asignados de memoria en tiempo de compilación y no pueden ser extendidos a presión en cualquier entrada más entrante en o añadidos a los contenidos originales. En otras palabras, no habrá forma de que se pueda acceder (presentar) al resultado concatenado como un solo literal de cadena . Entonces, esta construcción es intrínsecamente incorrecta.
Solo para su información, para la concatenación de cadenas en tiempo de ejecución ( no literales ), tenemos la función de biblioteca
strcat()
que concatena dos cadenas . Aviso, la descripción menciona:Entonces, podemos ver que
s1
es una cadena , no una cadena literal . Sin embargo, dado que el contenido des2
no se modifica de ninguna manera, puede ser un literal de cadena .fuente
strcat
: la matriz de destino debe ser lo suficientemente larga para recibir los caracteres des2
más un terminador nulo después de los caracteres presentes allí ya.El preprocesador realiza la concatenación literal de cadena en tiempo de compilación. No hay forma de que esta concatenación tenga en cuenta el valor de
test
, que no se conoce hasta que el programa se ejecuta realmente. Por lo tanto, estos literales de cadena no se pueden concatenar.Debido a que el caso general es que no tendría una construcción como esta para valores conocidos en tiempo de compilación, el estándar C fue diseñado para restringir la función de concatenación automática al caso más básico: cuando los literales están literalmente uno al lado del otro. .
Pero incluso si no dijera esta restricción de esa manera, o si la restricción se construyera de manera diferente, su ejemplo aún sería imposible de realizar sin hacer de la concatenación un proceso en tiempo de ejecución. Y, para eso, tenemos las funciones de biblioteca como
strcat
.fuente
Porque C no tiene
string
tipo. Los literales de cadena se compilan enchar
matrices, referenciadas por unchar*
puntero.C permite combinar literales adyacentes en tiempo de compilación , como en su primer ejemplo. El propio compilador de C tiene algunos conocimientos sobre cadenas. Pero esta información no está presente en tiempo de ejecución y , por lo tanto, no puede ocurrir la concatenación.
Durante el proceso de compilación, su primer ejemplo se "traduce" a:
Observe cómo el compilador combina las dos cadenas en una única matriz estática, antes de que el programa se ejecute.
Sin embargo, su segundo ejemplo se "traduce" a algo como esto:
Debe quedar claro por qué esto no se compila. El operador ternario
?
se evalúa en tiempo de ejecución, no en tiempo de compilación, cuando las "cadenas" ya no existen como tales, sino solo comochar
matrices simples , referenciadas porchar*
punteros. A diferencia de los literales de cadena adyacentes , los punteros de caracteres adyacentes son simplemente un error de sintaxis.fuente
static const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
ser asístatic const char *char_ptr_1 = "HiBye";
y de manera similar para el resto de las sugerencias?static const char *char_ptr_1 = "HiBye";
el compilador traduce la línea astatic const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
, así que no, no debe escribirse "como una cadena". Como dice la Respuesta, las cadenas se compilan en una matriz de caracteres, y si estuviera asignando una matriz de caracteres en su forma más "cruda", usaría una lista de caracteres separados por comas, comostatic const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
static const char str[] = {'t', 'e', 's', 't', '\0'};
es lo mismo questatic const char str[] = "test";
, nostatic const char* ptr = "test";
es lo mismo questatic const char* ptr = {'t', 'e', 's', 't', '\0'};
. El primero es válido y se compilará, pero el segundo no es válido y hace lo que espera.Si realmente desea que ambas ramas produzcan constantes de cadena en tiempo de compilación para elegirlas en tiempo de ejecución, necesitará una macro.
fuente
Su código que usa el operador ternario elige condicionalmente entre dos literales de cadena. Independientemente de la condición conocida o desconocida, esto no se puede evaluar en tiempo de compilación, por lo que no se puede compilar. Incluso esta declaración
printf("Hi" (1 ? "Bye" : "Goodbye"));
no se compilaría. La razón se explica en profundidad en las respuestas anteriores. Otra posibilidad de hacer una declaración de este tipo utilizando un operador ternario válido para compilar , también implicaría una etiqueta de formato y el resultado de la declaración del operador ternario formateado como argumento adicional paraprintf
. Incluso entonces, laprintf()
impresión daría la impresión de "haber concatenado" esas cadenas solo en el tiempo de ejecución y tan pronto como éste .fuente
printf
no requiere un especificador de formato; si solo se hiciera la concatenación en tiempo de compilación (que no lo es), el uso de printf de OP sería válido.printf()
requeriría una etiqueta de formato, lo cual es absolutamente falso. ¡Corregido!En
printf("Hi" "Bye");
tiene dos matrices consecutivas de char que el compilador puede convertir en una sola matriz.En
printf("Hi" (test ? "Bye" : "Goodbye"));
tiene una matriz seguida de un puntero a char (una matriz convertida en un puntero a su primer elemento). El compilador no puede combinar una matriz y un puntero.fuente
Para responder a la pregunta, iría a la definición de printf. La función printf espera const char * como argumento. Cualquier literal de cadena como "Hola" es un carácter constante *; sin embargo, una expresión como
(test)? "str1" : "str2"
NO es un carácter constante * porque el resultado de dicha expresión se encuentra solo en tiempo de ejecución y, por lo tanto, es indeterminado en tiempo de compilación, un hecho que hace que el compilador se queje. Por otro lado, esto funciona perfectamente bienprintf("hi %s", test? "yes":"no")
fuente
(test)? "str1" : "str2"
NO es unconst char*
... ¡Por supuesto que lo es! No es una expresión constante, pero su tipo sí lo esconst char *
. Estaría perfectamente bien escribirprintf(test ? "hi " "yes" : "hi " "no")
. El problema del OP no tiene nada que verprintf
,"Hi" (test ? "Bye" : "Goodbye")
es un error de sintaxis sin importar cuál sea el contexto de la expresión.Esto no se compila porque la lista de parámetros para la función printf es
y
no se ajusta a la lista de parámetros.
gcc intenta darle sentido imaginando que
es una lista de parámetros y se queja de que "Hola" no es una función.
fuente
printf()
lista de argumentos, pero eso se debe a que la expresión no es válida en ningún lugar, no solo en unaprintf()
lista de argumentos. En otras palabras, ha elegido una razón demasiado especializada para el problema; el problema general es que"Hi" (
no es válido en C, mucho menos en una llamada aprintf()
. Le sugiero que elimine esta respuesta antes de que sea rechazada.