如果我使用strcpy
为数组分配字符串文字,我可以很容易地访问那些超出数组边界的字节:
char sequence[4];
strcpy(sequence, "String");
printf("&sequence == %pn", &sequence);
printf("&sequence[3] == %pn", &sequence[3]);
printf("The out-of-range values are '%c' and '%c'n", *(char*)0x000000000022FE50, *(char*)0x000000000022FE51);
&sequence == 000000000022FE4C
&sequence[3] == 000000000022FE4F
The out-of-range values are 'n' and 'g'
但是,如果我做同样的事情,只是在适当的位置初始化数组,这些内存字节是空的,或者有一些意外的值:
char sequence[4] = "String";
printf("&sequence == %pn", &sequence);
printf("&sequence[3] == %pn", &sequence[3]);
printf("The out-of-range values are '%c' and '%c'n", *(char*)0x000000000022FE50, *(char*)0x000000000022FE51);
&sequence == 000000000022FE4C
&sequence[3] == 000000000022FE4F
The out-of-range values are ' ' and 'r'
为什么strcpy
和直接存储有这样的区别?
免责声明:我知道这个节目是不正确的我知道什么是未定义的行为,我知道它很糟糕,永远不应该被视为解决方案。这个问题只涉及机制,只寻求教育目的。我从来不会把它应用到任何实际任务中(如果有人那么担心的话(。
如果我使用strcpy为数组分配字符串文字,我可以很容易地访问那些超出数组边界的字节:
不,不能。这是未定义的行为。它可能会起作用。可能不会。不要那样做。每当你"可以";访问越界元素,则表示您做错了什么。
使用strncpy(sequence, "String", sizeof(sequence))
可以避免这种情况
为什么strcpy和直接存储之间有这样的区别?
strcpy
不知道要传递给它的数组的大小,但编译器在编译过程中知道。strcpy
的一个实现可以是这样的:
char * strcpy ( char * destination, const char * source ) {
char *ret = destination;
while(source) {
*destination = *source;
destination++;
source++;
}
return ret;
}
如果初始化一个数组,这就是C标准所说的:
字符类型的数组可以由字符串文字或UTF-8字符串文字初始化,也可以用大括号括起来。字符串文字的连续字节(如果有空间或数组大小未知,则包括终止的null字符(初始化数组的元素。
如果字符串文字太长,则将忽略其余字符。请注意,这很可能会导致字符串没有零终止。因此,这不是一个有效的字符串,打印它将调用未定义的行为。然而,这个代码是可以的,即使它会发出警告:
#include <stdio.h>
int main(void)
{
char str[8]="Hi! This initializer is too long";
printf("%sn", str);
for(int i=0; i<sizeof(str); i++)
putchar(str[i]);
putchar('n');
}
它将打印:
Hi!
Hi!This