Man*_*man 5 c standards c99 c11
假设有以下两段代码:
char *c = "hello world";
c[1] = 'y';
Run Code Online (Sandbox Code Playgroud)
上面那个不行。
char c[] = "hello world";
c[1] = 'y';
Run Code Online (Sandbox Code Playgroud)
这个可以。
关于第一个,我知道字符串“hello world”可能存储在只读内存部分,因此无法更改。然而,第二个在堆栈上创建一个字符数组,因此可以修改。
我的问题是 - 为什么编译器不检测到第一种类型的错误?为什么这不是 C 标准的一部分?这有什么特殊原因吗?
C 编译器不需要检测第一个错误,因为 C 字符串文字不是const.
参考C99标准N1256草案:
6.4.5 第 5 段:
在翻译阶段 7,值为零的字节或代码被附加到由一个或多个字符串文字产生的每个多字节字符序列。然后,使用多字节字符序列来初始化静态存储持续时间和长度足以包含该序列的数组。对于字符串文字,数组元素的类型为char,并使用多字节字符序列的各个字节进行初始化;[...]
第 6 段:
如果这些数组的元素具有适当的值,则未指定这些数组是否不同。如果程序尝试修改这样的数组,则行为是未定义的。
(C11 不会改变这一点。)
因此,字符串文字"hello, world"的类型是char[13]( not const char[13] ),在大多数上下文中都会转换为该类型char*。
尝试修改const对象具有未定义的行为,并且大多数尝试这样做的代码必须由编译器进行诊断(例如,您可以通过强制转换来解决这个问题)。尝试修改字符串文字也有未定义的行为,但不是因为它是const(它不是);这是因为标准明确指出该行为是未定义的。
例如,这个程序严格遵守:
#include <stdio.h>
void print_string(char *s) {
printf("%s\n", s);
}
int main(void) {
print_string("Hello, world");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果字符串文字为const,则传递"Hello, world"给采用(非const)的函数char*将需要诊断。print_string()该程序是有效的,但如果尝试修改 指向的字符串,它将表现出未定义的行为s。
原因是历史性的。ANSI C 之前的版本没有const关键字,因此无法定义接受 achar*并承诺不修改它指向的内容的函数。在 ANSI C (1989) 中创建字符串文字const会破坏现有代码,并且在标准的后续版本中没有很好的机会进行此类更改。
gcc-Wwrite-strings确实导致它将字符串文字视为const,但使 gcc 成为不合格的编译器,因为它无法对此发出诊断:
const char (*p)[6] = &"hello";
Run Code Online (Sandbox Code Playgroud)
("hello"is of type char[6],所以&"hello"is of type char (*)[6],与声明的类型不兼容p。With-Wwrite-strings被&"hello"视为是 type const char (*)[6]。)大概这就是既不包含-Wall也不-Wextra包含的原因-Wwrite-strings。
另一方面,触发警告的代码-Wwrite-strings无论如何都应该被修复。编写 C 代码并不是一个坏主意,这样无论有或没有-Wwrite-strings.
(请注意,C++ 字符串文字是 const,因为当 Bjarne Stroustrup 设计 C++ 时,他并不关心旧 C 代码的严格兼容性。)
Joo*_*gen -1
char* c = strdup("...");会变得c[1]明智。(删除了 C 上的咆哮)虽然智能编译器可以/确实对此发出警告,但 C 传统上是机器附近的,没有(边界/格式/...)检查和其他此类“不必要的”开销。
lint是检测此类错误的工具:aconst char*被分配给 a char*。它还会标记一个char c = c[30];(不再依赖于类型,但也会寻址错误。)因为将 c 声明为 会很好const char*。C 是一种古老的语言,具有宽容的传统,并且可以在许多平台上运行。
| 归档时间: |
|
| 查看次数: |
656 次 |
| 最近记录: |