Следующий код компилируется без проблем:
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 Фазы перевода)
1 Приоритет синтаксических правил перевода определяется следующими этапами 6)
- Смежные токены строкового литерала объединяются.
И только после этого
- Пробелы, разделяющие токены, больше не имеют значения. Каждый токен предварительной обработки преобразуется в токен. Полученные токены анализируются синтаксически и семантически и переводятся как единица перевода .
В этой конструкции
"Hi" (test ? "Bye" : "Goodbye")
нет смежных токенов строковых литералов. Итак, эта конструкция неверна.
(test ? "Bye" : "Goodbye")
переходить к одному из строковых литералов, по существу создающих "Hi" "Bye"
или "Hi Goodbye"
? (на мой вопрос дан ответ в других ответах)
Согласно стандарту C11, глава §5.1.1.2, объединение смежных строковых литералов:
Смежные токены строкового литерала объединяются.
происходит в фазе перевода . С другой стороны:
printf("Hi" (test ? "Bye" : "Goodbye"));
включает условный оператор, который оценивается во время выполнения . Таким образом, во время компиляции, на этапе трансляции, отсутствуют смежные строковые литералы, следовательно, объединение невозможно. Синтаксис недействителен, о чем сообщает ваш компилятор.
Чтобы подробнее рассказать о причину , на этапе предварительной обработки смежные строковые литералы объединяются и представляются как один строковый литерал (токен). Хранилище распределяется соответственно, и конкатенированный строковый литерал рассматривается как единый объект (один строковый литерал).
С другой стороны, в случае конкатенации во время выполнения у места назначения должно быть достаточно памяти для хранения конкатенированного строкового литерала, иначе не будет возможности получить доступ к ожидаемому конкатенированному выводу. Теперь, в случае строковых литералов , они уже выделенная память во время компиляции и не могут быть расширены , чтобы вписаться в любом более входящем входе в или добавляются к первоначальному содержанию. Другими словами, не будет возможности получить доступ к объединенному результату (представить) в виде одного строкового литерала . Итак, эта конструкция по своей сути неверна.
Просто FYI, для времени выполнения строки ( не литералы ) конкатенации, мы имеем библиотечную функцию , strcat()
которая сцепляет две строки . Обратите внимание, в описании упоминается:
char *strcat(char * restrict s1,const char * restrict s2);
strcat()
Функция добавляет копию строки , указаннойs2
(включая нулевой символ завершающего) до конца строки , на которую указываетs1
. Начальный символs2
заменяет нулевой символ в концеs1
. [...]
Итак, мы видим, что s1
это строка , а не строковый литерал . Однако, поскольку содержимое s2
никоим образом не изменяется, оно вполне может быть строковым литералом .
strcat
: целевой массив должен быть достаточно длинным, чтобы принимать символы от s2
плюс нулевой терминатор после символов, уже присутствующих там.
Конкатенация строковых литералов выполняется препроцессором во время компиляции. У этой конкатенации нет возможности узнать значение test
, которое не известно, пока программа не выполнится. Следовательно, эти строковые литералы нельзя объединить.
Поскольку в общем случае у вас не будет такой конструкции для значений, известных во время компиляции, стандарт C был разработан, чтобы ограничить функцию автоматической конкатенации самым основным случаем: когда литералы буквально расположены рядом друг с другом .
Но даже если бы это ограничение не было сформулировано таким образом или если бы ограничение было построено по-другому, ваш пример все равно было бы невозможно реализовать без преобразования конкатенации во время выполнения. И для этого у нас есть библиотечные функции, такие как strcat
.
Потому что у C нет string
типа. Строковые литералы компилируются в char
массивы, на которые ссылается char*
указатель.
C позволяет комбинировать смежные литералы во время компиляции , как в вашем первом примере. Сам компилятор C имеет некоторые знания о строках. Но эта информация отсутствует во время выполнения, и, следовательно, не может произойти конкатенация.
В процессе компиляции ваш первый пример «переводится» на:
int main() {
static const char char_ptr_1[] = {'H', 'i', 'B', 'y', 'e', '\0'};
printf(char_ptr_1);
}
Обратите внимание, как две строки объединяются компилятором в один статический массив до того, как программа будет запущена.
Однако ваш второй пример "переведен" примерно так:
int main() {
static const char char_ptr_1[] = {'H', 'i', '\0'};
static const char char_ptr_2[] = {'B', 'y', 'e', '\0'};
static const char char_ptr_3[] = {'G', 'o', 'o', 'd', 'b', 'y', 'e', '\0'};
int test = 0;
printf(char_ptr_1 (test ? char_ptr_2 : char_ptr_3));
}
Должно быть понятно, почему это не компилируется. Тернарный оператор ?
оценивается во время выполнения, а не во время компиляции, когда «строки» больше не существуют как таковые, а только как простые 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'};
. Первый действителен и будет компилироваться, но второй недействителен и делает то, что вы ожидаете.
Если вы действительно хотите, чтобы обе ветви создавали строковые константы времени компиляции, которые будут выбираться во время выполнения, вам понадобится макрос.
#include <stdio.h>
#define ccat(s, t, a, b) ((t)?(s a):(s b))
int
main ( int argc, char **argv){
printf("%s\n", ccat("hello ", argc > 2 , "y'all", "you"));
return 0;
}
В чем причина этого?
Ваш код, использующий тернарный оператор, условно выбирает между двумя строковыми литералами. Независимо от известного или неизвестного состояния, это не может быть оценено во время компиляции, поэтому оно не может компилироваться. Даже это заявление printf("Hi" (1 ? "Bye" : "Goodbye"));
не компилируется. Причина подробно объяснена в ответах выше. Другая возможность сделать такой оператор, используя тернарный оператор, пригодный для компиляции , также будет включать тег формата и результат оператора тернарного оператора, отформатированный как дополнительный аргумент для printf
. Даже в этом случае при printf()
распечатке создается впечатление, что эти строки «сцеплены» только во время выполнения и уже в самом начале .
#include <stdio.h>
int main() {
int test = 0;
printf("Hi %s\n", (test ? "Bye" : "Goodbye")); //specify format and print as result
}
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
(const char *format, ...)
и
("Hi" (test ? "Bye" : "Goodbye"))
не соответствует списку параметров.
gcc пытается понять это, представив, что
(test ? "Bye" : "Goodbye")
представляет собой список параметров и жалуется, что «Hi» не является функцией.
printf()
списку аргументов, но это потому, что выражение недействительно нигде - не только в printf()
списке аргументов. Другими словами, вы выбрали слишком узкую причину проблемы; общая проблема в том, что "Hi" (
это недопустимо в C, не говоря уже о вызове printf()
. Я предлагаю вам удалить этот ответ, прежде чем он будет отклонен.