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



以下代码编译没有问题:

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"));

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


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

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

仅供参考,对于运行时字符串(不是文字(连接,我们有连接两个字符串的库函数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 允许在编译时组合相邻的文本,如第一个示例所示。C 编译器本身对字符串有一些了解。但是此信息在运行时不存在,因此无法进行串联。

在编译过程中,您的第一个示例被"翻译"为:

int main() {
    static const char char_ptr_1[] = {'H', 'i', 'B', 'y', 'e', ''};
    printf(char_ptr_1);
}
请注意,在

程序执行之前,编译器如何将这两个字符串组合成单个静态数组。

但是,您的第二个示例被"翻译"为如下所示的内容:

int main() {
    static const char char_ptr_1[] = {'H', 'i', ''};
    static const char char_ptr_2[] = {'B', 'y', 'e', ''};
    static const char char_ptr_3[] = {'G', 'o', 'o', 'd', 'b', 'y', 'e', ''};
    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("%sn", 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 %sn", (test ? "Bye" : "Goodbye")); //specify format and print as result
}

printf("Hi" "Bye");中,您有两个连续的 char 数组,编译器可以将其转换为单个数组。

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

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

div class="one_answers">

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

(const char *format, ...)

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

不符合参数列表。

海湾合作委员会试图通过想象来理解它

(test ? "Bye" : "Goodbye")
是一个

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

最新更新