Jos*_* D. 95 c string concatenation
以下代码编译没有问题:
int main() {
printf("Hi" "Bye");
}
Run Code Online (Sandbox Code Playgroud)
但是,这不编译:
int main() {
int test = 0;
printf("Hi" (test ? "Bye" : "Goodbye"));
}
Run Code Online (Sandbox Code Playgroud)
这是什么原因?
Sou*_*osh 134
根据C11标准,章节§5.1.1.2,相邻字符串文字的连接:
相邻的字符串文字标记是连接的.
发生在翻译阶段.另一方面:
printf("Hi" (test ? "Bye" : "Goodbye"));
Run Code Online (Sandbox Code Playgroud)
涉及条件运算符,它在运行时进行评估.因此,在编译时,在转换阶段,不存在相邻的字符串文字,因此连接是不可能的.语法无效,因此由编译器报告.
为了详细说明原因部分,在预处理阶段,相邻的字符串文字被连接起来并表示为单个字符串文字(标记).相应地分配存储,并且连接的字符串文字被视为单个实体(一个字符串文字).
另一方面,在运行时连接的情况下,目标应该有足够的内存来保存连接的字符串文字,否则将无法访问预期的连接输出.现在,在的情况下,字符串常量,它们已经在编译时分配的内存,并且不能扩展,以适应任何多个输入输入到或附加到原始内容.换句话说,无法将连接结果作为单个字符串文字进行访问(呈现).所以,这种结构固有地不正确.
只是FYI,对于运行时字符串(不是文字)连接,我们有strcat()
连接两个字符串的库函数.注意,描述中提到:
char *strcat(char * restrict s1,const char * restrict s2);
的
strcat()
功能追加字符串的副本指向s2
(包括终止空字符)到的端部指向的字符串s1
.最初的字符s2
覆盖了结尾处的空字符s1
.[...]
所以,我们可以看到,这s1
是一个字符串,而不是字符串文字.但是,由于内容s2
不会以任何方式改变,因此很可能是字符串文字.
Vla*_*cow 119
根据C标准(5.1.1.2翻译阶段)
1翻译语法规则的优先级由以下阶段指定.6)
- 相邻的字符串文字标记是连接的.
只有在那之后
- 分隔标记的空白字符不再重要.每个预处理令牌都转换为令牌.由此产生的标记在语法和语义上进行分析并翻译为翻译单元.
在这种结构中
"Hi" (test ? "Bye" : "Goodbye")
Run Code Online (Sandbox Code Playgroud)
没有相邻的字符串文字标记.所以这种结构无效.
Lig*_*ica 38
字符串文字串联由预处理器在编译时执行.这种连接没有办法知道test
在程序实际执行之前不知道的值.因此,这些字符串文字不能连接.
因为一般情况是你不会有这样的结构用于编译时已知的值,所以C标准被设计为将自动连接功能限制为最基本的情况:当文字确实是彼此并排的时候.
但即使它没有以这种方式说明这种限制,或者如果限制是不同构造的,如果不将连接作为运行时进程,你的例子仍然是不可能实现的.而且,为此,我们有库函数,如strcat
.
Uns*_*ned 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);
}
Run Code Online (Sandbox Code Playgroud)
请注意编译器在执行程序之前如何将两个字符串组合到单个静态数组中.
但是,你的第二个例子被"翻译"成这样的东西:
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));
}
Run Code Online (Sandbox Code Playgroud)
应该清楚为什么这不编译.?
当"字符串"不再存在时,三元运算符在运行时而不是编译时进行评估,但仅作为指针char
引用的简单数组char*
.与相邻的字符串文字不同,相邻的char指针只是语法错误.
Eri*_*ric 12
如果你真的想让两个分支产生在运行时选择的编译时字符串常量,你需要一个宏.
#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;
}
Run Code Online (Sandbox Code Playgroud)
use*_*414 10
这是什么原因?
使用三元运算符的代码有条件地在两个字符串文字之间进行选择.无论条件是已知还是未知,都无法在编译时进行评估,因此无法编译.即使这个陈述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
}
Run Code Online (Sandbox Code Playgroud)
在printf("Hi" "Bye");
你有烧焦的两个连续阵列,编译器可以使一个阵列.
在printf("Hi" (test ? "Bye" : "Goodbye"));
你有一个数组后跟一个指向char的指针(一个数组转换为指向其第一个元素的指针).编译器无法合并数组和指针.