为什么C在使用三元运算符时不允许连接string?
下面的代码编译没有问题:
int main() { printf("Hi" "Bye"); }
但是,这不会编译:
int main() { int test = 0; printf("Hi" (test ? "Bye" : "Goodbye")); }
这是什么原因?
根据C标准(5.1.1.2翻译阶段)
1翻译的语法规则的优先顺序由以下几个阶段规定:6)
- 相邻的string文字标记是连接的。
只有在那之后
- 分隔令牌的空白字符不再重要。 每个预处理令牌都被转换成令牌。 由此产生的标记语法和语义分析翻译为翻译单元 。
在这个build设
"Hi" (test ? "Bye" : "Goodbye")
没有相邻的string文字标记。 所以这个build设是无效的。
根据C11标准§5.1.1.2,相邻string文字的连接:
相邻的string文字标记是连接的。
发生在翻译阶段 。 另一方面:
printf("Hi" (test ? "Bye" : "Goodbye"));
涉及在运行时评估的条件运算符。 所以,在编译时,在翻译阶段,不存在相邻的string,因此不可能连接。 该语法无效,因此由您的编译器报告。
为了详细说明为什么在预处理阶段,相邻string文字被连接在一起,并表示为单个string文字 (标记)。 存储器被相应的分配,连接的string文字被认为是一个单独的实体 (一个string文字)。
另一方面,在运行时级联的情况下,目标应该有足够的内存来保存连接的string,否则将无法访问预期的级联输出。 现在,在string文字的情况下,它们在编译时已经被分配了内存,并且不能被扩展以适应任何更多的input或附加到原始内容的input。 换句话说,连接的结果不可能作为单个string文字被访问(呈现)。 所以,这个构造本质上是不正确的。
只是FYI,对于运行时string ( 而不是文字 )串联,我们有库函数strcat()
连接两个string 。 注意,描述中提到:
char *strcat(char * restrict s1,const char * restrict s2);
strcat()
函数将s2
指向的string的副本(包括终止空字符)附加到由s1
指向的string的末尾。s2
的最初字符覆盖了s1
末尾的空字符。 […]
所以,我们可以看到, s1
是一个string ,而不是string文字 。 但是,由于s2
的内容没有任何改变,所以它可以很好地作为string文字 。
string文字连接由预处理器在编译时执行。 这种连接没有办法知道test
的价值,这是在程序实际执行之前是未知的。 因此,这些string文字不能连接。
因为一般情况下,编译时不会有这样的结构,所以C标准被devise为将自动拼接function限制在最基本的情况下:当文字在字面上彼此正确alignment时。
但即使没有这种限制,或者如果限制是不同的构造,你的例子仍然是不可能实现的,没有连接一个运行时的过程。 而且,为此,我们有像strcat
这样的库函数。
因为C没有string
types。 string文字被编译为char
数组,由char*
指针引用。
C允许在编译时将相邻的文字合并,如第一个例子。 C编译器本身有一些关于string的知识。 但是这个信息在运行时并不存在,因此串联不会发生。
在编译过程中,你的第一个例子被“翻译”为:
int main() { static const char char_ptr_1[] = {'H', 'i', 'B', 'y', 'e', '\0'}; printf(char_ptr_1); }
请注意,在程序执行之前,两个string是如何由编译器组合成一个静态数组的。
然而,你的第二个例子是“翻译”到这样的事情:
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)); }
这应该是清楚的为什么这不编译。 三元操作符?
是在运行时评估的,而不是编译时,当“string”不再像这样存在时,而只是被char*
指针引用的简单char
数组。 不像相邻的string文字 ,相邻的字符指针只是一个语法错误。
如果你真的想让两个分支产生编译时string常量,以便在运行时select,你需要一个macros。
#include <stdio.h> #define ccat(s, t, a, b) ((t)?(sa):(sb)) int main ( int argc, char **argv){ printf("%s\n", ccat("hello ", argc > 2 , "y'all", "you")); return 0; }
这是什么原因?
你的代码使用三元运算符有条件地select两个string文字。 无论已知条件还是未知条件,都无法在编译时进行评估,因此无法进行编译。 甚至这个声明printf("Hi" (1 ? "Bye" : "Goodbye"));
不会编译。 原因在上面的答案中有深入的解释。 使用三元运算符有效编译这种语句的另一种可能性还将涉及格式标记,并且三元运算符语句的结果被格式化为printf
附加参数 。 即使这样, printf()
打印输出也只会在运行时给出一个“已连接”这些string的印象。
#include <stdio.h> int main() { int test = 0; printf("Hi %s\n", (test ? "Bye" : "Goodbye")); //specify format and print as result }
在printf("Hi" "Bye");
你有两个连续的char数组,编译器可以将它们组合成一个数组。
在printf("Hi" (test ? "Bye" : "Goodbye"));
你有一个数组后面跟着一个指向char的指针(一个数组被转换为指向它的第一个元素的指针)。 编译器不能合并数组和指针。
这不会编译,因为printf函数的参数列表是
(const char *format, ...)
和
("Hi" (test ? "Bye" : "Goodbye"))
不符合参数列表。
gcc试图通过想象来理解它
(test ? "Bye" : "Goodbye")
是一个参数列表,并抱怨说“嗨”不是一个函数。