我想用下面的代码复制字符串,但它没有复制\'\\0\'。
\n\nvoid copyString(char *to, char *from)\n{\n do{\n *to++ = *from++;\n }while(*from);\n}\nint main(void)\n{\n char to[50];\n char from[] = "text2copy";\n copyString(to, from);\n printf("%s", to);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这是代码的输出:
\n\ntext2copy\xc3\x87\xe2\x96\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xe2\x95\x91kvu\xc2\xa1lvu\nRun Code Online (Sandbox Code Playgroud)\n\n每次我重新运行代码时,text2copy 之后的字符都会发生变化,因此while(*from)工作正常,但会复制随机内容而不是 \'\\0\'。
\n\ntext2copy\xc3\x96\xe2\x96\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xe2\x95\x91kvu\xc2\xa1lvu\ntext2copy\xe2\x95\xa8\xe2\x96\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xe2\x95\x91kvu\xc2\xa1lvu\ntext2copy\xe2\x95\xa1\xe2\x96\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xe2\x95\x91kvu\xc2\xa1lvu\n//etc\nRun Code Online (Sandbox Code Playgroud)\n\n为什么会发生这种情况?
\n问题是您永远不会复制'\0'字符串末尾的字符。要了解为什么考虑这一点:
传入的字符串是一个大小正好适合数据的常量字符串:
char from[] = "text2copy";
Run Code Online (Sandbox Code Playgroud)
记忆中是这样的:
----+----+----+----+----+----+----+----+----+----+ ----+----
其他记忆 | t | 电子| x| t | 2 | c | 哦| p| y | \0 | 其他记忆
----+----+----+----+----+----+----+----+----+----+ ----+----
^
从
现在让我们假设您已经执行了多次循环,并且位于循环顶部并from指向'y'text2copy 中的字符:
----+----+----+----+----+----+----+----+----+----+ ----+----
其他记忆 | t | 电子| x| t | 2 | c | 哦| p| y | \0 | 其他记忆
----+----+----+----+----+----+----+----+----+----+ ----+----
^
从
计算机执行*to++ = *from++;将字符复制'y'到to,然后递增to和from。现在内存看起来像这样:
----+----+----+----+----+----+----+----+----+----+ ----+----
其他记忆 | t | 电子| x| t | 2 | c | 哦| p| y | \0 | 其他记忆
----+----+----+----+----+----+----+----+----+----+ ----+----
^
从
计算机执行} while(*from);并意识到这*from是错误的,因为它指向'\0'字符串末尾的字符,因此循环结束并且该'\0'字符永远不会被复制。
现在你可能认为这可以解决这个问题:
void copyString(char *to, char *from)
{
do{
*to++ = *from++;
} while(*from);
*to = *from; // copy the \0 character
}
Run Code Online (Sandbox Code Playgroud)
它确实复制了'\0'角色,但仍然存在问题。该代码存在更根本的缺陷,因为正如 @JonathanLeffler 在评论中所说,对于空字符串,您会查看字符串末尾之后的内存内容,并且由于它没有分配给您访问,因此会导致未定义的行为:
----+----+----
其他记忆 | \0 | 其他记忆
----+----+----
^
从
计算机执行*to++ = *from++;将字符复制'\0'到to然后递增两者的操作to,from这使得从点到您不拥有的内存:
----+----+----
其他记忆 | \0 | 其他记忆
----+----+----
^
从
现在计算机执行}while(*from);并访问不属于您的内存。您可以from毫无问题地指向任何地方,但是from当它指向不属于您的内存时取消引用是未定义的行为。
我在评论中所做的示例建议将复制的值保存到临时变量中:
void copyString(char *to, char *from)
{
int test;
do{
test = (*to++ = *from++); // save the value copied
} while(test);
}
Run Code Online (Sandbox Code Playgroud)
我建议这种特定方式的原因是向您展示问题是您正在测试的内容,而不是之后测试循环条件。如果您保存复制的值,然后稍后测试该保存的值,则在测试之前会复制字符(因此 \0 被复制),并且您不会从递增的指针中读取(因此不存在未定义的行为)
但@JonathanLeffler 在他的评论中举的例子更短,更容易理解,也更惯用。它在不声明命名临时变量的情况下执行相同的操作:
void copyString(char *to, char *from)
{
while ((*to++ = *from++) != '\0')
;
}
Run Code Online (Sandbox Code Playgroud)
代码首先复制字符,然后测试复制的值(因此将'\0'复制),但递增的指针永远不会取消引用(因此不存在未定义的行为)。
| 归档时间: |
|
| 查看次数: |
419 次 |
| 最近记录: |