char 数组在上次迭代时复制整个单词而不是单个字母


#include <iostream>
using namespace std;
void myStrcpy(char [], char []);  
int main(int argc, const char * argv[]) {
char c1[] = "htako";
char c2[] = "Mark";
myStrcpy(c1, c2);
cout << c2 << endl;
return 0;
}
void myStrcpy (char c1[], char c2[]) {
int i = 0;
while(c1[i] != '') {
c2[i] = c1[i];
cout << c1[i] << "  ::: " << i << endl;
i++;
}
}

当我逐步完成调试器时,一切正常,直到 i = 4,在第 4 次交互时,它从(第 3 次迭代)htak 变为 htakohtako。我不明白为什么它在上次迭代中复制了整个单词。当我在第四次迭代中打印出 c1[i] 时,它会按预期打印出一个 o,但最后这个额外的 htako 从何而来?

你的问题的答案与内存的布局有关。如果检查内存,您将看到:

c2              c1 
M, a, r, k, , h, t, a, k, o, 

您的代码中有 2 个问题。

  1. 您有一个缓冲区溢出c2只有 5 个字节(Mark + 空终止符),但您正在覆盖空终止符,因为c1的长度为 6 个字节(htako + 空终止符)
  2. 您不是空终止字符串

您看到字符串似乎在增长,因为您覆盖了 null 终止符。在其他情况下,您的程序可能会崩溃。

代码有两个问题:

  • 您正在使用基于 C 的字符串,而忘记将终止 null 字符写入c2
  • c2中没有足够的空间用于该空字符。

要解决第一个问题,请在 while 循环后添加c2[i] = 0;

要修复第二个问题,请使用char c2[6] = "Mark";请注意,这里需要6个才能容纳来自"htako"的 5 个字符 + 终止空字符。

更详细地说:char c1[] = "htako";实际上定义了长度为 6 的 char 数组:

{ 'h', 't', 'a', 'k', 'o', }.

char c2[] = "Mark";定义长度为 5 的较短字符数组:

{ 'M', 'a', 'r', 'k', }

因此,当您的 while 循环在将 c1 复制到 c2 后结束时,您有 c2 的位置:

{ 'h', 't', 'a', 'k', 'o' }

请注意,您没有终止空字符。此外,cince c2 被隐式声明为长度为 5,您没有空间添加它。因此,当您将修改后的 c2 输出到 cout 时,由于您使用的是 char[],cout 实际上接收没有长度的char *,并且根据 C 字符串约定,通过搜索终止空字符来确定字符串的长度。由于您没有该空字符,因此 cout 将超过搜索它的c2结束,并且访问有效 ares 之外的数据是C++程序的未定义行为。在您的特定机器上,堆栈会增长,因此 c1 恰好在 c2 之后,您会看到打印的"htakohtako"。但实际上,您的程序也可能因SEGV而失败或做任何令人不快的事情。这种类型的错误称为缓冲区溢出,是许多安全漏洞的根源。不惜一切代价避免这种情况。


但实际上,std::string在这里更合适和安全的选择,除非你只是在玩弄 C 字符串在内部的工作方式。

最新更新