为什么使用条件运算符时C不允许连接字符串?

问题描述 投票:95回答:9

下面的代码编译没有问题:

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

但是,这不能编译:

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

这是什么原因?

c string syntax concatenation conditional-operator
9个回答
121
投票

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

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

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

仅在此之后

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

在此结构中

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

没有相邻的字符串文字标记。因此此构造无效。


135
投票

根据C11标准,第§5.1.1.2章,相邻字符串文字的串联:

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

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

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

涉及条件运算符,它在运行时进行评估。因此,在编译时,在翻译阶段,不存在相邻的字符串文字,因此无法进行串联。语法无效,因此由编译器报告。


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

另一方面,在运行时串联的情况下,目标位置应具有足够的内存来容纳串联的字符串文字否则,将无法访问expected串联的输出。现在,对于字符串常量,它们已经在编译时分配了内存,并且不能extended来容纳更多的原始输入intoappend to内容。换句话说,将无法以单个字符串文字的方式访问(呈现)连接的结果。因此,这种结构本质上是不正确的。

仅供参考,对于运行时字符串非文字)串联,我们具有库函数strcat(),该函数将两个strings串联。注意,描述中提到:

strcat()

char *strcat(char * restrict s1,const char * restrict s2);函数附加由strcat()指向的字符串的副本(包括终止的空字符)到s2指向的字符串的结尾。初始字符s1的值覆盖s2末尾的空字符。 [...]

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


39
投票

String文字串联由预处理器在编译时执行。此串联无法知道s2的值,直到程序实际执行时才知道。因此,这些字符串文字不能串联。

因为一般情况是对于编译时已知的值,您不会有这样的构造,所以C标准旨在将自动连接功能限制为最基本的情况:当字面值在字面上恰好与彼此。

但是,即使没有以这种方式表达此限制,或者该限制的构造方式不同,如果不将串联作为运行时过程,您的示例仍将无法实现。而且,为此,我们具有诸如test的库函数。


30
投票

因为C没有strcat类型。字符串文字被编译为string数组,并由char指针引用。

与第一个示例一样,

C允许相邻的literals组合在编译时”。 C编译器本身对字符串有一些了解。但是此信息在运行时不存在,,因此无法发生串联。

在编译过程中,您的第一个示例已“翻译”为:

char*

请注意,在程序执行之前,编译器将这两个字符串如何组合为一个静态数组。

但是,您的第二个示例已“翻译”为类似这样的内容:

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


12
投票

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

char*

10
投票

这是什么原因?

您的使用三元运算符的代码有条件地在两个字符串文字之间进行选择。无论条件是已知的还是未知的,都无法在编译时进行评估,因此无法进行编译。即使该语句#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打印输出也会给人留下“仅在runtime之前”串联这些字符串的印象。

printf()

#include <stdio.h> int main() { int test = 0; printf("Hi %s\n", (test ? "Bye" : "Goodbye")); //specify format and print as result } 中,有两个连续的char数组,编译器可以将它们制成一个数组。

printf("Hi" "Bye");中,您有一个数组,后跟一个指向char的指针(将数组转换为指向其第一个元素的指针)。编译器不能merge

数组和指针。

要回答这个问题-我将转到printf的定义。函数printf期望const char *

作为参数。任何字符串文字(例如“ Hi”)都是const char *;但是,诸如printf("Hi" (test ? "Bye" : "Goodbye"));之类的表达式不是const char *,因为此类表达式的结果仅在运行时发现,因此在编译时不确定,这一事实会导致编译器抱怨。另一方面-这非常好(test)? "str1" : "str2"

由于printf函数的参数列表为,因此无法编译

printf("hi %s", test? "yes":"no")

(const char *format, ...)

不适合参数列表。

gcc试图通过想象来理解这一点

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

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


7
投票

#include <stdio.h> int main() { int test = 0; printf("Hi %s\n", (test ? "Bye" : "Goodbye")); //specify format and print as result } 中,有两个连续的char数组,编译器可以将它们制成一个数组。


0
投票

要回答这个问题-我将转到printf的定义。函数printf期望const char *


-4
投票

由于printf函数的参数列表为,因此无法编译

© www.soinside.com 2019 - 2024. All rights reserved.