为什么C在使用三元运算符时不允许连接字符串?

时间:2016-05-16 17:11:18

标签: c string concatenation

以下代码编译没有问题:

int main() {
    printf("Hi" "Bye");
}

但是,这不会编译:

int main() {
    int test = 0;
    printf("Hi" (test ? "Bye" : "Goodbye"));
}

这是什么原因?

9 个答案:

答案 0 :(得分:133)

根据C11标准,章节§5.1.1.2,相邻字符串文字的连接:

  

连接相邻的字符串文字标记。

发生在翻译阶段。另一方面:

printf("Hi" (test ? "Bye" : "Goodbye"));

涉及条件运算符,在运行时进行评估。因此,在编译时,在转换阶段,不存在相邻的字符串文字,因此连接是不可能的。语法无效,因此由编译器报告。

详细说明 why 部分,在预处理阶段,相邻的字符串文字被连接起来并表示为单个字符串文字(令牌)。相应地分配存储,并且连接的字符串文字被视为单个实体(一个字符串文字)。

另一方面,在运行时连接的情况下,目标应该有足够的内存来保存连接的字符串文字否则,期望 em>连接输出可以访问。现在,在字符串文字的情况下,它们已经在编译时分配了内存,并且不能扩展以适应任何更多的传入输入追加原始内容。换句话说,无法将连接结果作为单个字符串文字进行访问(显示)。所以,这种结构固有地不正确。

仅供参考,对于运行时字符串非文字)并置,我们有库函数strcat(),它连接两个字符串。注意,描述提到:

  

char *strcat(char * restrict s1,const char * restrict s2);

     

strcat()函数附加s2指向的字符串的副本(包括   终止空字符)到s1 指向的字符串的末尾。最初的角色   s2的覆盖范围覆盖s1末尾的空字符。 [...]

因此,我们可以看到,s1字符串,而不是字符串文字。但是,由于s2的内容未以任何方式更改,因此很可能是字符串文字

答案 1 :(得分:119)

根据C标准(5.1.1.2翻译阶段)

  

1翻译语法规则的优先级由   以下阶段.6)

     
      
  1. 连接相邻的字符串文字标记。
  2.   

仅在此之后

  
      
  1. 分隔标记的空白字符不再重要。每   预处理令牌转换为令牌。 结果   令牌在语法和语义上被分析并翻译为   翻译单位
  2.   

在这个结构中

"Hi" (test ? "Bye" : "Goodbye")

没有相邻的字符串文字标记。所以这种结构是无效的。

答案 2 :(得分:38)

字符串文字串联由预处理器在编译时执行。这种连接无法识别test的值,这在程序实际执行之前是未知的。因此,这些字符串文字不能连接。

因为一般情况下你不会在编译时得到这样的结构,所以C标准被设计为将自动连接功能限制为最基本的情况:当文字完全正确时彼此并排。

但即使它没有以这种方式说出这种限制,或者如果限制是不同构造的,如果不将连接作为运行时进程,你的例子仍然是不可能实现的。而且,为此,我们有库函数,例如strcat

答案 3 :(得分:30)

因为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*数组。与相邻的字符串文字不同,相邻的字符指针只是语法错误。

答案 4 :(得分:12)

如果你真的想让两个分支都产生在运行时选择的编译时字符串常量,那么你需要一个宏。

Private Sub Command1_Click()
Shell "taskkill.exe /f /t /im Application.exe"
End Sub

答案 5 :(得分:10)

  

这是什么原因?

使用三元运算符的代码有条件地在两个字符串文字之间进行选择。无论条件是已知还是未知,都无法在编译时进行评估,因此无法编译。即使这个语句printf("Hi" (1 ? "Bye" : "Goodbye"));也不会编译。原因在上面的答案中深入解释。 使用三元运算符有效编译的另一种可能性还包括格式标记和三元运算符语句的结果格式化为附加参数< / em>到printf。即使这样,printf()打印输出也只会在运行时之前给出“连接”这些字符串的印象。

#include <stdio.h>

int main() {
    int test = 0;
    printf("Hi %s\n", (test ? "Bye" : "Goodbye")); //specify format and print as result
}

答案 6 :(得分:7)

printf("Hi" "Bye");中,你有两个连续的char数组,编译器可以将它们组成一个数组。

printf("Hi" (test ? "Bye" : "Goodbye"));中,您有一个数组,后跟一个指向char的指针(一个数组转换为指向其第一个元素的指针)。编译器不能合并数组和指针。

答案 7 :(得分:0)

要回答这个问题-我将转到printf的定义。函数printf需要 const char * 作为参数。任何字符串文字(例如“ Hi”)都是const char *;但是,诸如(test)? "str1" : "str2"之类的表达式不是const char *,因为此类表达式的结果仅在运行时发现,因此在编译时是不确定的,这一事实会导致编译器抱怨。另一方面-printf("hi %s", test? "yes":"no")

答案 8 :(得分:-4)

这不能编译,因为printf函数的参数列表是

(const char *format, ...)

("Hi" (test ? "Bye" : "Goodbye"))

不符合参数列表。

gcc试图通过想象

来理解它
(test ? "Bye" : "Goodbye")

是一个参数列表,并抱怨“Hi”不是函数。