Le code suivant se compile sans problème:
int main() {
printf("Hi" "Bye");
}
Cependant, cela ne compile pas:
int main() {
int test = 0;
printf("Hi" (test ? "Bye" : "Goodbye"));
}
Quelle est la raison de ceci?
Le code suivant se compile sans problème:
int main() {
printf("Hi" "Bye");
}
Cependant, cela ne compile pas:
int main() {
int test = 0;
printf("Hi" (test ? "Bye" : "Goodbye"));
}
Quelle est la raison de ceci?
"Hi"
et"Bye"
sont des chaînes littérales , pas des chaînes telles qu'utilisées dans la bibliothèque standard C. Avec les chaînes littérales , le compilateur concaténera"H\0i" "B\0ye"
. Pas la même chose avecsprintf(buf,"%s%s", "H\0i" "B\0ye");
a (some_condition ? + : - ) b
printf("Hi" ("Bye"));
ne fonctionnera pas - cela ne nécessite pas l'opérateur ternaire; la parenthèse est suffisante (maisprintf("Hi" test ? "Bye" : "Goodbye")
ne compilerait pas non plus). Il n'existe qu'un nombre limité de jetons pouvant suivre un littéral de chaîne. La virgule,
, le crochet ouvert, le crochet[
fermé]
(comme dans1["abc"]
- et oui, c'est horrible), le crochet rond)
fermé, le crochet bouclé}
(dans un initialiseur ou un contexte similaire) et le point;
- virgule sont légitimes (et une autre chaîne littérale); Je ne suis pas sûr qu'il y en ait d'autres.Réponses:
Selon la norme C (5.1.1.2 Phases de traduction)
Et seulement après ça
Dans cette construction
il n'y a pas de jetons littéraux de chaîne adjacents. Cette construction est donc invalide.
la source
(test ? "Bye" : "Goodbye")
évauler vers l'un des littéraux de chaîne faisant essentiellement"Hi" "Bye"
ou"Hi Goodbye"
? (ma question trouve une réponse dans les autres réponses)Selon la norme C11, chapitre §5.1.1.2, concaténation de littéraux de chaîne adjacents:
se passe en phase de traduction . D'autre part:
implique l'opérateur conditionnel, qui est évalué au moment de l' exécution . Ainsi, au moment de la compilation, pendant la phase de traduction, il n'y a pas de littéraux de chaîne adjacents présents, par conséquent la concaténation n'est pas possible. La syntaxe est invalide et donc signalée par votre compilateur.
Pour élaborer un peu sur la partie pourquoi , pendant la phase de prétraitement, les littéraux de chaîne adjacents sont concaténés et représentés sous la forme d'un littéral de chaîne unique (jeton). Le stockage est alloué en conséquence et le littéral de chaîne concaténé est considéré comme une seule entité (un littéral de chaîne).
D'autre part, en cas de concaténation au moment de l'exécution, la destination doit avoir suffisamment de mémoire pour contenir la chaîne de caractères concaténée , sinon, il n'y aura aucun moyen d' accéder à la sortie concaténée attendue . Maintenant, en cas de littéraux de chaîne , ils sont déjà attribués mémoire à la compilation et ne peuvent être étendues pour tenir dans une entrée plus entrant dans ou ajoutés au contenu original. En d'autres termes, il n'y aura aucun moyen que le résultat concaténé puisse être accédé (présenté) comme une chaîne littérale unique . Donc, cette construction est intrinsèquement incorrecte.
Juste Pour votre information, pour l' exécution chaîne ( non littéraux ) concaténation, nous avons la fonction de bibliothèque
strcat()
qui concatène deux chaînes . Remarquez, la description mentionne:Ainsi, nous pouvons voir que le
s1
est une chaîne , pas une chaîne littérale . Cependant, comme le contenu des2
n'est en aucun cas modifié, il peut très bien s'agir d'une chaîne littérale .la source
strcat
: le tableau de destination doit être suffisamment long pour recevoir les caractères des2
plus un terminateur nul après les caractères déjà présents.La concaténation de chaînes littérales est effectuée par le préprocesseur au moment de la compilation. Il n'y a aucun moyen pour cette concaténation de connaître la valeur de
test
, qui n'est connue que lorsque le programme s'exécute réellement. Par conséquent, ces littéraux de chaîne ne peuvent pas être concaténés.Parce que le cas général est que vous n'auriez pas une construction comme celle-ci pour les valeurs connues au moment de la compilation, le standard C a été conçu pour restreindre la fonction d'auto-concaténation au cas le plus élémentaire: lorsque les littéraux sont littéralement côte à côte .
Mais même s'il ne formulait pas cette restriction de cette manière, ou si la restriction était construite différemment, votre exemple serait toujours impossible à réaliser sans faire de la concaténation un processus d'exécution. Et, pour cela, nous avons les fonctions de bibliothèque telles que
strcat
.la source
Parce que C n'a pas de
string
type. Les littéraux de chaîne sont compilés enchar
tableaux, référencés par unchar*
pointeur.C permet aux littéraux adjacents d'être combinés au moment de la compilation , comme dans votre premier exemple. Le compilateur C lui-même a des connaissances sur les chaînes. Mais ces informations ne sont pas présentes au moment de l'exécution et la concaténation ne peut donc pas se produire.
Pendant le processus de compilation, votre premier exemple est "traduit" en:
Notez comment les deux chaînes sont combinées en un seul tableau statique par le compilateur, avant que le programme ne s'exécute.
Cependant, votre deuxième exemple est "traduit" en quelque chose comme ceci:
Il devrait être clair pourquoi cela ne compile pas. L'opérateur ternaire
?
est évalué à l'exécution, non à la compilation, lorsque les "chaînes" n'existent plus en tant que telles, mais uniquement en tant que simpleschar
tableaux, référencés par deschar*
pointeurs. Contrairement aux chaînes littérales adjacentes, les pointeurs char adjacents sont simplement une erreur de syntaxe.la source
static const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
êtrestatic const char *char_ptr_1 = "HiBye";
et de même pour le reste des pointeurs?static const char *char_ptr_1 = "HiBye";
le compilateur traduit la ligne enstatic const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
, donc non, elle ne doit pas être écrite "comme une chaîne". Comme le dit la réponse, les chaînes sont compilées en un tableau de caractères, et si vous affectiez un tableau de caractères dans sa forme la plus "brute", vous utiliseriez une liste de caractères séparés par des virgules, tout commestatic const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
static const char str[] = {'t', 'e', 's', 't', '\0'};
soit le même questatic const char str[] = "test";
, cestatic const char* ptr = "test";
n'est pas le même questatic const char* ptr = {'t', 'e', 's', 't', '\0'};
. Le premier est valide et compilera mais le second est invalide et fait ce que vous attendez.Si vous voulez vraiment que les deux branches produisent des constantes de chaîne au moment de la compilation à choisir au moment de l'exécution, vous aurez besoin d'une macro.
la source
Votre code utilisant l'opérateur ternaire choisit conditionnellement entre deux littéraux de chaîne. Quelle que soit la condition connue ou inconnue, cela ne peut pas être évalué au moment de la compilation, donc il ne peut pas compiler. Même cette déclaration
printf("Hi" (1 ? "Bye" : "Goodbye"));
ne serait pas compilée. La raison est expliquée en profondeur dans les réponses ci-dessus. Une autre possibilité de rendre une telle déclaration en utilisant un opérateur ternaire valide à compiler , impliquerait également une balise de format et le résultat de l'instruction d'opérateur ternaire formaté comme argument supplémentaire àprintf
. Même dans ce cas, l'printf()
impression donnerait l'impression d'avoir "concaténé" ces chaînes uniquement au moment de l' exécution et dès l' exécution .la source
printf
ne nécessite pas de spécificateur de format; si seulement la concaténation était faite au moment de la compilation (ce qui n'est pas le cas), l'utilisation de printf par OP serait valide.printf()
nécessiterait une balise de format, ce qui n'est absolument pas vrai. Corrigée!Dans
printf("Hi" "Bye");
vous avez deux tableaux consécutifs de char que le compilateur peut transformer en un seul tableau.Dans
printf("Hi" (test ? "Bye" : "Goodbye"));
vous avez un tableau suivi d'un pointeur vers char (un tableau converti en un pointeur vers son premier élément). Le compilateur ne peut pas fusionner un tableau et un pointeur.la source
Pour répondre à la question - j'irais à la définition de printf. La fonction printf attend const char * comme argument. Toute chaîne littérale telle que "Hi" est un const char *; cependant une expression telle que
(test)? "str1" : "str2"
n'est PAS un const char * parce que le résultat d'une telle expression est trouvé uniquement au moment de l'exécution et est donc indéterminé au moment de la compilation, un fait qui amène dûment le compilateur à se plaindre. D'autre part - cela fonctionne parfaitement bienprintf("hi %s", test? "yes":"no")
la source
(test)? "str1" : "str2"
n'est PAS unconst char*
... Bien sûr que ça l'est! Ce n'est pas une expression constante, mais son type l' estconst char *
. Ce serait parfaitement bien d'écrireprintf(test ? "hi " "yes" : "hi " "no")
. Le problème de l'OP n'a rien à voir avecprintf
,"Hi" (test ? "Bye" : "Goodbye")
c'est une erreur de syntaxe quel que soit le contexte de l'expression.Cela ne compile pas car la liste des paramètres de la fonction printf est
et
ne rentre pas dans la liste des paramètres.
gcc essaie de lui donner un sens en imaginant que
est une liste de paramètres, et se plaint que "Hi" n'est pas une fonction.
la source
printf()
liste d'arguments, mais c'est parce que l'expression n'est valide nulle part - pas seulement dans uneprintf()
liste d'arguments. En d'autres termes, vous avez choisi une raison beaucoup trop spécialisée pour le problème; le problème général est que ce"Hi" (
n'est pas valide en C, encore moins dans un appel àprintf()
. Je vous suggère de supprimer cette réponse avant qu'elle ne soit votée à la baisse.