我很困惑为什么编译器会给出
const char s[]="hello";
s[2]='t'; // Compile Time Error
char *t = "hello";
*(t+2)='u'; // Run time Error
我猜两种情况下编译器都应该给出编译时错误。任何人都可以告诉我这种方式的特殊原因吗?
答案 0 :(得分:6)
在第一种情况下,您正在写一个const
并且编译器注意到并且可以拒绝它。
在第二种情况下,t
是指向非const char
的指针,因此您可以取消引用它并在*(t+2)
处写入。但是,由于t
使用指向只读段的指针进行初始化,因此您在运行时会遇到分段违规。
您可能会痛苦地配置链接器以将所有数据放入可写段中。这是丑陋的,非标准的。
P.S。一些复杂的静态分析器(可能Frama-C)可能会在不运行程序的情况下捕获这两个错误。也可以想象扩展GCC,例如使用MELT添加此类检查(但这是非常重要的工作,可能很难获得资助)。
答案 1 :(得分:6)
向后兼容性。
您无法修改const char。这很明显。
不显而易见的是,字符串文字的类型实际上是指向常量字符的指针,而不是指向字符的指针。因此,第二个声明实际上是错误的类型。但是,由于历史原因,这得到了支持。
请注意,上述内容有点谎言。字符串文字实际上是char[]
类型而不是指针。
特别是,字符串文字的类型是char[]
而不是C89和C99中的const char[]
[我认为C11,但不确定]。那时实际上不是错误,但是数据存储在只读段中,所以尝试写入它是未定义的行为。
另外,对于它的价值,你可以使用-Wwrite-strings
和gcc(g ++已经包含它)来警告这一点。
答案 2 :(得分:3)
执行:char *t = "hello";
时, t 是指向代码部分中的内存的指针,因此您无法更改它。因为它是只读,所以在运行时会出现分段错误。
当你这样做时:const char s[]="hello";
然后 s 是一个在堆栈上的字符数组,但它是const
,所以你可以不要改变它并且你得到一个编译错误(编译器知道它是 const 所以他不允许你改变它。)
当您不希望更改String时使用const
是好的,因为这会产生编译错误,这比运行时错误更好。
答案 3 :(得分:2)
考虑以下一系列陈述:
char *t = "hello";
char s[5];
t = s;
*(t+2)='u';
这一系列语句不会产生运行时错误,因为语句*(t+2)='u';
无效。它试图在您的情况下修改const(只读)内存位置,但编译器无法知道是否会发生访问冲突。