Der folgende Code wird problemlos kompiliert:
int main() {
printf("Hi" "Bye");
}
Dies wird jedoch nicht kompiliert:
int main() {
int test = 0;
printf("Hi" (test ? "Bye" : "Goodbye"));
}
Was ist der Grund dafür?
c
string
syntax
concatenation
conditional-operator
José D.
quelle
quelle
"Hi"
und"Bye"
sind Stringliterale , nicht Strings , wie in der C - Standardbibliothek verwendet. Bei String-Literalen wird der Compiler verkettet"H\0i" "B\0ye"
. Nicht dasselbe mitsprintf(buf,"%s%s", "H\0i" "B\0ye");
a (some_condition ? + : - ) b
printf("Hi" ("Bye"));
nicht einmal funktioniert - es ist kein ternärer Operator erforderlich. Die Klammer ist ausreichend (printf("Hi" test ? "Bye" : "Goodbye")
würde aber auch nicht kompiliert werden). Es gibt nur eine begrenzte Anzahl von Token, die einem Zeichenfolgenliteral folgen können. Komma,
, offene eckige Klammer[
, enge eckige Klammer]
(wie in1["abc"]
- und ja, es ist grausam), enge runde Klammer)
, enge geschweifte Klammer}
(in einem Initialisierer oder einem ähnlichen Kontext) und Semikolon;
sind legitim (und ein anderes Zeichenfolgenliteral); Ich bin mir nicht sicher, ob es noch andere gibt.Antworten:
Nach dem C-Standard (5.1.1.2 Übersetzungsphasen)
Und erst danach
In dieser Konstruktion
Es gibt keine benachbarten String-Literal-Token. Diese Konstruktion ist also ungültig.
quelle
(test ? "Bye" : "Goodbye")
einem der String-Literale entziehen, die im Wesentlichen machen"Hi" "Bye"
oder"Hi Goodbye"
? (Meine Frage wird in den anderen Antworten beantwortet)Gemäß dem C11-Standard, Kapitel §5.1.1.2, Verkettung benachbarter String-Literale:
geschieht in der Übersetzungsphase . Andererseits:
bezieht den bedingten Operator mit ein, der zur Laufzeit ausgewertet wird . Während der Übersetzungsphase sind während der Übersetzungsphase keine benachbarten Zeichenfolgenliterale vorhanden, daher ist die Verkettung nicht möglich. Die Syntax ist ungültig und wird daher von Ihrem Compiler gemeldet.
Um den Warum- Teil etwas näher zu erläutern , werden während der Vorverarbeitungsphase die benachbarten Zeichenfolgenliterale verkettet und als einzelnes Zeichenfolgenliteral (Token) dargestellt. Der Speicher wird entsprechend zugewiesen und das verkettete Zeichenfolgenliteral wird als eine einzelne Entität (ein Zeichenfolgenliteral) betrachtet.
Auf der anderen Seite sollte das Ziel im Falle einer Laufzeitverkettung über genügend Speicher verfügen, um das verkettete Zeichenfolgenliteral aufzunehmen. Andernfalls kann auf die erwartete verkettete Ausgabe nicht zugegriffen werden. Bei Zeichenfolgenliteralen wird ihnen bereits zur Kompilierungszeit Speicher zugewiesen, und sie können nicht erweitert werden , um weitere eingehende Eingaben in den ursprünglichen Inhalt aufzunehmen oder an diesen anzuhängen . Mit anderen Worten, es gibt keine Möglichkeit, auf das verkettete Ergebnis als einzelnes Zeichenfolgenliteral zuzugreifen (dargestellt) . Dieses Konstrukt ist also von Natur aus falsch.
Nur zur Info, für Laufzeit Zeichenfolge ( nicht Literale ) Verkettung, haben wir die Library - Funktion ,
strcat()
die zwei verkettet Strings . Beachten Sie, dass in der Beschreibung Folgendes erwähnt wird:Wir können also sehen, dass dies
s1
eine Zeichenfolge ist , kein Zeichenfolgenliteral . Da der Inhalt vons2
jedoch in keiner Weise geändert wird, kann es sich durchaus um ein String-Literal handeln .quelle
strcat
: Das Zielarray muss lang genug sein, um die Zeichen vons2
plus einem Nullterminator nach den dort bereits vorhandenen Zeichen zu empfangen .Die Verkettung von Zeichenfolgenliteralen wird vom Präprozessor zur Kompilierungszeit durchgeführt. Es gibt keine Möglichkeit für diese Verkettung, den Wert von zu kennen
test
, der erst bekannt ist, wenn das Programm tatsächlich ausgeführt wird. Daher können diese Zeichenfolgenliterale nicht verkettet werden.Da der allgemeine Fall ist, dass Sie für Werte, die zur Kompilierungszeit bekannt sind, keine solche Konstruktion haben würden, wurde der C-Standard entwickelt, um die automatische Verkettungsfunktion auf den grundlegendsten Fall zu beschränken: wenn die Literale buchstäblich direkt nebeneinander liegen .
Aber selbst wenn diese Einschränkung nicht auf diese Weise formuliert würde oder wenn die Einschränkung anders aufgebaut wäre, wäre Ihr Beispiel immer noch nicht zu realisieren, ohne die Verkettung zu einem Laufzeitprozess zu machen. Und dafür haben wir die Bibliotheksfunktionen wie
strcat
.quelle
Weil C keinen
string
Typ hat. String-Literale werden zuchar
Arrays kompiliert , auf die durch einenchar*
Zeiger verwiesen wird .Mit C können benachbarte Literale wie in Ihrem ersten Beispiel zur Kompilierungszeit kombiniert werden . Der C-Compiler selbst hat einige Kenntnisse über Zeichenfolgen. Diese Informationen sind jedoch zur Laufzeit nicht vorhanden, sodass keine Verkettung auftreten kann.
Während des Kompilierungsprozesses wird Ihr erstes Beispiel "übersetzt" in:
Beachten Sie, wie die beiden Zeichenfolgen vom Compiler zu einem einzigen statischen Array kombiniert werden, bevor das Programm jemals ausgeführt wird.
Ihr zweites Beispiel wird jedoch in etwa so "übersetzt":
Es sollte klar sein, warum dies nicht kompiliert wird. Der ternäre Operator
?
wird zur Laufzeit und nicht zur Kompilierungszeit ausgewertet, wenn die "Zeichenfolgen" nicht mehr als solche existieren, sondern nur noch als einfachechar
Arrays, auf die durchchar*
Zeiger verwiesen wird . Im Gegensatz zu benachbarten Zeichenfolgenliteralen sind benachbarte Zeichenzeiger einfach ein Syntaxfehler.quelle
static const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
seinstatic const char *char_ptr_1 = "HiBye";
und in ähnlicher Weise für den Rest der Zeiger?static const char *char_ptr_1 = "HiBye";
übersetzt der Compiler die Zeile instatic const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
, also nein, sie sollte nicht "wie eine Zeichenfolge" geschrieben werden. Wie die Antwort sagt, werden Zeichenfolgen zu einem Array von Zeichen kompiliert, und wenn Sie ein Array von Zeichen in seiner "rohesten" Formstatic const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
static const char str[] = {'t', 'e', 's', 't', '\0'};
es dasselbe ist wiestatic const char str[] = "test";
,static const char* ptr = "test";
ist es nicht dasselbe wiestatic const char* ptr = {'t', 'e', 's', 't', '\0'};
. Ersteres ist gültig und wird kompiliert, letzteres ist jedoch ungültig und macht das, was Sie erwarten.Wenn beide Zweige wirklich Zeichenfolgenkonstanten zur Kompilierungszeit erzeugen sollen, die zur Laufzeit ausgewählt werden sollen, benötigen Sie ein Makro.
quelle
Ihr Code, der den ternären Operator verwendet, wählt bedingt zwischen zwei Zeichenfolgenliteralen. Unabhängig davon, ob eine Bedingung bekannt oder unbekannt ist, kann dies zum Zeitpunkt der Kompilierung nicht ausgewertet werden, sodass keine Kompilierung möglich ist. Auch diese Aussage
printf("Hi" (1 ? "Bye" : "Goodbye"));
würde nicht kompiliert. Der Grund wird in den obigen Antworten ausführlich erläutert. Eine andere Möglichkeit , eine solche Anweisung unter Verwendung eines ternären Operators zum Kompilieren gültig zu machen , würde auch ein Format-Tag und das Ergebnis der ternären Operator-Anweisung beinhalten, die als zusätzliches Argument für formatiert sindprintf
. Selbst dannprintf()
würde der Ausdruck den Eindruck erwecken, diese Zeichenfolgen erst zur Laufzeit und bereits zur Laufzeit "verkettet" zu haben .quelle
printf
nicht benötigen einen Formatbezeichner; Wenn nur die Verkettung zur Kompilierungszeit erfolgen würde (was nicht der Fall ist), wäre die Verwendung von printf durch OP gültig.printf()
ein Format-Tag erforderlich sein, was absolut nicht wahr ist. Korrigiert!In haben
printf("Hi" "Bye");
Sie zwei aufeinanderfolgende Arrays von char, die der Compiler zu einem einzigen Array machen kann.In haben
printf("Hi" (test ? "Bye" : "Goodbye"));
Sie ein Array, gefolgt von einem Zeiger auf char (ein Array, das in einen Zeiger auf sein erstes Element konvertiert wurde). Der Compiler kann ein Array und einen Zeiger nicht zusammenführen .quelle
Um die Frage zu beantworten, würde ich zur Definition von printf gehen. Die Funktion printf erwartet const char * als Argument. Jedes String-Literal wie "Hi" ist ein const char *; Ein Ausdruck wie z. B.
(test)? "str1" : "str2"
ist KEIN const char *, da das Ergebnis eines solchen Ausdrucks nur zur Laufzeit gefunden wird und daher zur Kompilierungszeit unbestimmt ist. Dies führt dazu, dass sich der Compiler ordnungsgemäß beschwert. Auf der anderen Seite - das funktioniert einwandfreiprintf("hi %s", test? "yes":"no")
quelle
(test)? "str1" : "str2"
ist NICHT einconst char*
... Natürlich ist es! Es ist kein konstanter Ausdruck, aber sein Typ istconst char *
. Es wäre vollkommen in Ordnung zu schreibenprintf(test ? "hi " "yes" : "hi " "no")
. Das Problem des OP hat nichts damit zu tunprintf
, es"Hi" (test ? "Bye" : "Goodbye")
ist ein Syntaxfehler, unabhängig vom Ausdruckskontext.Dies wird nicht kompiliert, da die Parameterliste für die Funktion printf lautet
und
passt nicht in die Parameterliste.
gcc versucht es zu verstehen, indem es sich das vorstellt
ist eine Parameterliste und beschwert sich, dass "Hi" keine Funktion ist.
quelle
printf()
Argumentliste übereinstimmt , aber das liegt daran, dass der Ausdruck nirgendwo gültig ist - nicht nur in einerprintf()
Argumentliste. Mit anderen Worten, Sie haben einen viel zu speziellen Grund für das Problem ausgewählt. Das allgemeine Problem ist, dass"Hi" (
es in C nicht gültig ist, geschweige denn in einem Aufruf vonprintf()
. Ich schlage vor, dass Sie diese Antwort löschen, bevor sie abgelehnt wird.