两者之间存在巨大差异:
const char s[] = "abcd";
和
const char* t = "abcd";
其中第一个声明s
为从字符串“abcd”初始化的数组对象。s
将具有与程序中任何其他对象不同的地址。字符串本身可能是编译时工件;初始化是一个副本,因此如果编译器可以找到执行初始化的其他方式(例如存储立即操作),则字符串不需要在运行时出现。
第二个声明声明t
为指向字符串常量的指针。字符串常量现在必须在运行时出现,因为像t+1
字符串中的指针这样的表达式是有效的。语言标准不保证程序中字符串文字的每一次出现都是唯一的,也不保证所有出现都被合并(尽管好的编译器会尝试做第二次。)但是,它保证它们具有静态生命周期.
因此,这是未定义的行为,因为数组的生命周期s
在函数返回时结束:
const char *gimme_a_string() {
const char s[] = "abcd";
return s;
}
但是,这很好:
const char *gimme_a_string() {
const char *s = "abcd";
return s;
}
还:
const char s[] = "abcd";
const char t[] = "abcd";
printf("%d\n", s == t);
保证打印0
,而
const char* s = "abcd";
const char* t = "abcd";
printf("%d\n", s == t);
可能会打印0
或1
,具体取决于实现。(如所写,它几乎肯定会打印1
。但是,如果两个声明位于不同的编译单元中并且 lto 未启用,则很可能会打印0
。)
由于数组形式是使用副本初始化的,因此非常量版本很好:
char s[] = "abcd";
s[3] = 'C';
但是 char 指针版本必须是 aconst
以避免未定义的行为。
// Will produce a warning on most compilers with compile option -Wall or equivalent
char* s = "abcd";
// *** UNDEFINED BEHAVIOUR *** Can cause random program breakage
s[3] = 'C';
从技术上讲,非 const 声明s
是合法的(这就是编译器只发出警告的原因),因为它试图修改 UB 常量。但是您应该始终注意编译器警告;最好将声明/初始化视为错误,因为它是。