Следующий код компилируется без проблем:
int main() {
printf("Hi" "Bye");
}
Однако это не компилируется:
int main() {
int test = 0;
printf("Hi" (test ? "Bye" : "Goodbye"));
}
В чем причина этого?
Следующий код компилируется без проблем:
int main() {
printf("Hi" "Bye");
}
Однако это не компилируется:
int main() {
int test = 0;
printf("Hi" (test ? "Bye" : "Goodbye"));
}
В чем причина этого?
"Hi"
и"Bye"
являются строковыми литералами , а не строками, используемыми в стандартной библиотеке C. Со строковыми литералами компилятор будет объединять"H\0i" "B\0ye"
. Не то же самое сsprintf(buf,"%s%s", "H\0i" "B\0ye");
a (some_condition ? + : - ) b
printf("Hi" ("Bye"));
не сработает - для этого не требуется тернарный оператор; скобок достаточно (хотяprintf("Hi" test ? "Bye" : "Goodbye")
и не компилируется). Существует только ограниченное количество токенов, которые могут следовать за строковым литералом. Запятая,
, открытая квадратная скобка[
, закрывающая квадратная скобка]
(как в1["abc"]
- и да, это ужасно), закрывающая круглая скобка)
, закрывающая фигурная скобка}
(в инициализаторе или подобном контексте) и точка с запятой;
допустимы (и еще один строковый литерал); Я не уверен, что есть другие.Ответы:
Согласно стандарту C (5.1.1.2 Фазы перевода)
И только после этого
В этой конструкции
нет смежных токенов строковых литералов. Итак, эта конструкция неверна.
источник
(test ? "Bye" : "Goodbye")
переходить к одному из строковых литералов, по существу создающих"Hi" "Bye"
или"Hi Goodbye"
? (на мой вопрос дан ответ в других ответах)Согласно стандарту C11, глава §5.1.1.2, объединение смежных строковых литералов:
происходит в фазе перевода . С другой стороны:
включает условный оператор, который оценивается во время выполнения . Таким образом, во время компиляции, на этапе трансляции, отсутствуют смежные строковые литералы, следовательно, объединение невозможно. Синтаксис недействителен, о чем сообщает ваш компилятор.
Чтобы подробнее рассказать о причину , на этапе предварительной обработки смежные строковые литералы объединяются и представляются как один строковый литерал (токен). Хранилище распределяется соответственно, и конкатенированный строковый литерал рассматривается как единый объект (один строковый литерал).
С другой стороны, в случае конкатенации во время выполнения у места назначения должно быть достаточно памяти для хранения конкатенированного строкового литерала, иначе не будет возможности получить доступ к ожидаемому конкатенированному выводу. Теперь, в случае строковых литералов , они уже выделенная память во время компиляции и не могут быть расширены , чтобы вписаться в любом более входящем входе в или добавляются к первоначальному содержанию. Другими словами, не будет возможности получить доступ к объединенному результату (представить) в виде одного строкового литерала . Итак, эта конструкция по своей сути неверна.
Просто FYI, для времени выполнения строки ( не литералы ) конкатенации, мы имеем библиотечную функцию ,
strcat()
которая сцепляет две строки . Обратите внимание, в описании упоминается:Итак, мы видим, что
s1
это строка , а не строковый литерал . Однако, поскольку содержимоеs2
никоим образом не изменяется, оно вполне может быть строковым литералом .источник
strcat
: целевой массив должен быть достаточно длинным, чтобы принимать символы отs2
плюс нулевой терминатор после символов, уже присутствующих там.Конкатенация строковых литералов выполняется препроцессором во время компиляции. У этой конкатенации нет возможности узнать значение
test
, которое не известно, пока программа не выполнится. Следовательно, эти строковые литералы нельзя объединить.Поскольку в общем случае у вас не будет такой конструкции для значений, известных во время компиляции, стандарт C был разработан, чтобы ограничить функцию автоматической конкатенации самым основным случаем: когда литералы буквально расположены рядом друг с другом .
Но даже если бы это ограничение не было сформулировано таким образом или если бы ограничение было построено по-другому, ваш пример все равно было бы невозможно реализовать без преобразования конкатенации во время выполнения. И для этого у нас есть библиотечные функции, такие как
strcat
.источник
Потому что у C нет
string
типа. Строковые литералы компилируются вchar
массивы, на которые ссылаетсяchar*
указатель.C позволяет комбинировать смежные литералы во время компиляции , как в вашем первом примере. Сам компилятор C имеет некоторые знания о строках. Но эта информация отсутствует во время выполнения, и, следовательно, не может произойти конкатенация.
В процессе компиляции ваш первый пример «переводится» на:
Обратите внимание, как две строки объединяются компилятором в один статический массив до того, как программа будет запущена.
Однако ваш второй пример "переведен" примерно так:
Должно быть понятно, почему это не компилируется. Тернарный оператор
?
оценивается во время выполнения, а не во время компиляции, когда «строки» больше не существуют как таковые, а только как простыеchar
массивы, на которые ссылаютсяchar*
указатели. В отличие от смежных строковых литералов , указатели на соседние символы представляют собой просто синтаксическую ошибку.источник
static const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
бытьstatic const char *char_ptr_1 = "HiBye";
и аналогично для остальных указателей?static const char *char_ptr_1 = "HiBye";
компилятор переводит строку вstatic const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
, так что нет, это не должно быть написано «как строка». Как сказано в ответе, строки компилируются в массив символов, и если вы назначаете массив символов в его самой «сырой» форме, вы бы использовали список символов, разделенных запятыми, как напримерstatic const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
static const char str[] = {'t', 'e', 's', 't', '\0'};
это то же самоеstatic const char str[] = "test";
,static const char* ptr = "test";
но не то же самое, чтоstatic const char* ptr = {'t', 'e', 's', 't', '\0'};
. Первый действителен и будет компилироваться, но второй недействителен и делает то, что вы ожидаете.Если вы действительно хотите, чтобы обе ветви создавали строковые константы времени компиляции, которые будут выбираться во время выполнения, вам понадобится макрос.
источник
Ваш код, использующий тернарный оператор, условно выбирает между двумя строковыми литералами. Независимо от известного или неизвестного состояния, это не может быть оценено во время компиляции, поэтому оно не может компилироваться. Даже это заявление
printf("Hi" (1 ? "Bye" : "Goodbye"));
не компилируется. Причина подробно объяснена в ответах выше. Другая возможность сделать такой оператор, используя тернарный оператор, пригодный для компиляции , также будет включать тег формата и результат оператора тернарного оператора, отформатированный как дополнительный аргумент дляprintf
. Даже в этом случае приprintf()
распечатке создается впечатление, что эти строки «сцеплены» только во время выполнения и уже в самом начале .источник
printf
не требует спецификатора формата; если бы только конкатенация была выполнена во время компиляции (а это не так), OP использование printf было бы допустимым.printf()
требуется тег формата, что абсолютно неверно. Исправлено!У
printf("Hi" "Bye");
вас есть два последовательных массива char, которые компилятор может превратить в один массив.У
printf("Hi" (test ? "Bye" : "Goodbye"));
вас есть один массив, за которым следует указатель на char (массив, преобразованный в указатель на его первый элемент). Компилятор не может объединить массив и указатель.источник
Чтобы ответить на вопрос - я бы перешел к определению printf. Функция printf ожидает в качестве аргумента const char * . Любой строковый литерал, такой как «Hi», является const char *; однако такое выражение, как
(test)? "str1" : "str2"
НЕ является const char *, потому что результат такого выражения обнаруживается только во время выполнения и, следовательно, является неопределенным во время компиляции, факт, который должным образом заставляет компилятор жаловаться. С другой стороны - это прекрасно работаетprintf("hi %s", test? "yes":"no")
источник
(test)? "str1" : "str2"
НЕconst char*
... Конечно, есть! Это не выражение постоянной, но ее тип являетсяconst char *
. Было бы прекрасно написатьprintf(test ? "hi " "yes" : "hi " "no")
. Проблема OP не имеет ничего общегоprintf
,"Hi" (test ? "Bye" : "Goodbye")
это синтаксическая ошибка независимо от контекста выражения.Это не компилируется, потому что список параметров для функции printf
и
не соответствует списку параметров.
gcc пытается понять это, представив, что
представляет собой список параметров и жалуется, что «Hi» не является функцией.
источник
printf()
списку аргументов, но это потому, что выражение недействительно нигде - не только вprintf()
списке аргументов. Другими словами, вы выбрали слишком узкую причину проблемы; общая проблема в том, что"Hi" (
это недопустимо в C, не говоря уже о вызовеprintf()
. Я предлагаю вам удалить этот ответ, прежде чем он будет отклонен.