繁体   English   中英

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

[英]Why does C not allow concatenating strings when using the conditional operator?

以下代码编译没有问题:

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

但是,这不会编译:

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

这是什么原因?

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

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

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

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

涉及在运行时计算的条件运算符。 因此,在编译时,在翻译阶段,没有相邻的字符串文字存在,因此连接是不可能的。 语法无效,因此由您的编译器报告。


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

另一方面,在运行时连接的情况下,目标应该有足够的内存来保存连接的字符串文字,否则将无法访问预期的连接输出。 现在,在字符串的情况下,它们已经在编译时分配的内存,并且不能在任何更传入的输入扩展到配合附加到原始内容。 换句话说,将无法将连接的结果作为单个字符串文字来访问(呈现)。 所以,这个结构本质上是不正确的。

仅供参考,对于运行时字符串不是文字)连接,我们有连接两个字符串的库函数strcat() 请注意,描述中提到:

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

strcat()函数将s2指向的字符串的副本(包括终止空字符)附加s1指向字符串的末尾。 s2的初始字符覆盖s1末尾的空字符。 [...]

所以,我们可以看到, s1是一个字符串,而不是字符串文字 然而,由于s2的内容没有以任何方式改变,它很可能是一个字符串文字

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

1 翻译的语法规则之间的优先级由以下阶段指定。6)

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

只有在那之后

  1. 分隔标记的空白字符不再重要。 每个预处理令牌都被转换为一个令牌。 生成的标记在句法和语义上进行分析并作为翻译单元进行翻译

在这个建设中

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

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

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

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

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

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

C 允许在 compile-time组合相邻的文字,如您的第一个示例。 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*指针引用。 与相邻的字符串文字不同,相邻的字符指针只是一个语法错误。

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

#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("Hi" "Bye"); 你有两个连续的 char 数组,编译器可以将它们组合成一个数组。

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

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

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

(const char *format, ...)

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

不符合参数列表。

gcc 试图通过想象来理解它

(test ? "Bye" : "Goodbye")

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM