我不确定为什么strcat在这种情况下对我有用:
char* foo="foo";
printf(strcat(foo,"bar"));
它为我成功打印了“ foobar”。
但是,根据此处有关stackoverflow的先前讨论的主题:I just can't figure out strcat
它说,上述内容不起作用,因为foo被声明为字符串文字。相反,需要将其声明为缓冲区(预定大小的数组,以便可以容纳我们尝试连接的另一个字符串)。
在那种情况下,为什么上面的程序对我成功?
答案 0 :(得分:5)
此代码调用未定义行为(UB),这意味着您不能保证会发生什么情况(此处失败)。
原因是字符串文字是不可变的。这意味着它们是不可变的,并且任何这样做的尝试都会调用UB。
请注意,由于UB可能会工作(今天和在您的系统中),因此UB可能会引起困难的逻辑错误,但仍然是错误的,这很可能使您可能会错过该错误并相处得很好。一切都很好。
PS:在此 Live Demo 中,我很幸运地遇到了细分错误。我说很幸运,因为此段错误将使我调查并调试代码。
值得注意的是,海湾合作委员会(GCC)不会发出任何警告,而来自Clang的警告也无关紧要:
p
rog.c:7:8: warning: format string is not a string literal (potentially insecure) [-Wformat-security]
printf(strcat(foo,"bar"));
^~~~~~~~~~~~~~~~~
prog.c:7:8: note: treat the string as an argument to avoid this
printf(strcat(foo,"bar"));
^
"%s",
1 warning generated.
答案 1 :(得分:1)
字符串文字是不可变的,并不是在尝试修改它们时必然会出错。用法文来说,这是“未定义的行为”,因此任何事情都可能发生,就标准而言,这很好。
现在,在现代平台和现代编译器上,您确实具有额外的保护:在具有内存保护的平台上,字符串表通常放置在只读内存区域中,因此修改它会导致运行时错误。 / p>
仍然,您可能拥有不提供任何运行时强制检查的编译器,或者是因为您是针对没有内存保护的平台进行编译的(例如80386 x86之前的版本,所以几乎所有用于DOS的C编译器,例如作为Turbo C,大多数微控制器在RAM而不是闪存上运行时...),或使用默认情况下不利用此硬件功能以与旧版本兼容的旧编译器(长期使用VC ++),或使用已显式启用此选项的现代编译器,以再次兼容旧代码(例如,带有-fwritable-strings
的gcc)。在所有这些情况下,通常不会出现任何运行时错误。
最后,还有一个曲折的极端情况:current-day optimizers actively exploit undefined behavior-即他们认为它永远不会发生,并相应地修改代码。一个特别聪明的编译器可以生成只会丢弃这种写操作的代码并非不可能,因为在这种情况下,法律上允许它做最喜欢的任何事情。
这可以通过一些简单的代码看到,例如:
int foo() {
char *bar = "bar";
*bar = 'a';
if(*bar=='b') return 1;
return 0;
}
此处,启用了优化:
return 0
;没有内存写入,没有段错误,它“似乎可以正常工作”(https://godbolt.org/g/cKqYU1); return 1
(https://godbolt.org/g/ejbqDm); return 1
(https://godbolt.org/g/rnUDYr)-因此,对内存进行了修改,但是随后的代码认为尚未对其进行修改;这尤其令人震惊,on AVR,其中没有内存保护,并且写入成功。长话短说:不要碰碰运气,要谨慎行事。始终将字符串文字分配给const char *
(不是纯char *
),并让类型系统帮助您避免此类问题。