在第一章中,K& R引入了一个功能副本如下:
void copy(char to[], char from[]) {
/* copy from from[] to to[], assumes sufficient space */
int i = 0;
while ((to[i] = from[i]) != '\0') {
i++;
}
}
使用此功能稍微修改一下,我有一些意想不到的结果。 示例程序:
int main() {
char a[3] = {'h', 'a', '\n'};
char b[3];
printf("a: %s", a); // prints ha
copy(b, a);
printf("a: %s", a); // prints nothing
printf("b: %s", b); // prints ha
return 0;
}
现在回答我的问题:
为什么从a
到b
的复制有效,这就是为什么复制中的while循环终止,即使a
不包含'\ 0'
为什么a
发生变异?
答案 0 :(得分:2)
此copy
函数依赖于终止nullbyte来确定何时应该停止复制。当您使用字符串常量时,它会自动以null结尾。但是,正常数组不是以空值终止的,因此函数继续访问a
末尾后面的内存,直到其中一个字节恰好是\0
。当copy
函数停止复制时,取决于该内存区域的内容。你不知道是什么,如果有的话,那么你不知道copy
将继续复制或将会发生什么。
答案 1 :(得分:2)
您可能正在遇到缓冲区溢出。
由于a
未正确终止(\0
},copy
从a
复制到b
,只要它找不到\0
}。因此,有更多的字节写入b
,因为它可以包含,然后溢出到a
(平台相关的,未定义的行为)。
溢出的部分是\0
之后的a
,从而使a
为零长度字符串。
您的筹码可能如下所示:
b a
[ arbitrary memory ][ 0 0 0 ][ h a \n ][ 0 ? ? ? ? ]
?
表示未知数据,因为我们不知道那里有什么,而且没有指定。
但是我们知道必须一个0
,否则printf
会打印更多的垃圾。
copy
副本,直到开头a
后找到零。偶然的,有一个
0
结束后a
,然后将其复制到b
。由于b
已经填满了
来自a
,b
的内容溢出到a
,使您的堆栈看起来像这样:
b a
[ arbitrary memory ][ h a \n ][ 0 a \n ][ 0 ? ? ? ? ]
由于\0
开头有a
,printf
假定a
为空。
答案 2 :(得分:1)
即使您没有在变量'\0'
中明确添加a
,也很可能是a[3]
(技术上超出范围)恰好是零。
很多内存通常都填充了零/ \0
值,但绝对无保证。
这解释了为什么您成功将字符串复制到b
。
您的复制功能在“认为”其复制数组ints
:
void copy(int to[], int from[]) {
什么时候应该复制char
acters的数组:
void copy(char to[], int char[]) {
答案 3 :(得分:1)
它可以正常工作,因为你的while循环没有进行逻辑运算,它正在做一个对齐。 因此,只要asigned值为真,它就会经历循环。(并且只要它不为零)所以当达到'\ 0'(whats 0)时终止它。 即使没有'\ 0',它也可能会终止(具有未定义的行为),因为在离开你的边界后,任何零值字节的出现几率都很大。但在离开阵列边界后,程序的行为可能是任何东西。 (它甚至可以让鼻龙产卵;)) b不打印任何内容的原因可能是以字节顺序为基础,因为您将int值转换为字节数组,因此可能是Byteorder的第一个字节放在第一个字节中,因此从外部是0它将被视为'\ 0',即使你的目的是将整个int视为符号而不是4个单值。 p.s。:这甚至会破坏严格别名规则。
答案 4 :(得分:1)
最好在下面的while循环之前检查到或来自是否为NULL。
while ((to[i] = from[i]) != '\0'){
i++;
}
问题:
1.循环终止是因为在 a 之后存储在内存中的内容等于0(或'\ 0'),这是未定义的。
2. a 可能会在调用 copy 之后发生变化,这也是未定义的。两个变量都存储在 a 之后,位置 b 存储在效果中。在上面提到的nemo案例中, a 在执行复制功能后发生了变异。
为了安全起见,总是在char数组的末尾插入'\ 0'。