【问题标题】:C++, weird behavior about copying char arrays by using pointersC++,关于使用指针复制字符数组的奇怪行为
【发布时间】:2013-05-03 04:00:12
【问题描述】:

我从教科书中得到了这段代码:

#include <iostream>
using namespace std;
int main(){
    char str1[]="hello,world!", str2[20], *p1, *p2;
    p1=str1; p2=str2;
    /*
    for(;*p1!='\0';p1++,p2++){
      cout<<"p1="<<*p1<<endl;
      *p2=*p1;cout<<"p2="<<*p2<<endl;
    }
    *p2='\0';
    p1=str1; p2=str2;
    */   
    cout<<"p1="<<p1<<endl;
    cout<< "p2="<<p2<<endl;  

    return 0;
}

我运行了这段代码,它会输出p1=hello,world!p2= 我能理解。

但是如果我取消注释for循环,输出显示在这里我很困惑,为什么在for循环之后,为什么它显示p1=而不是显示p1=hello,world!,以及指针p2,即使在分配之后for 循环,它仍然显示p2=?

但是在我取消注释p1=str1; p2=str2;这一行之后,输出是p1=hello,world!, p2=hello,world!,为什么会这样?

而写这行*p2='\0';的原因是什么,这行注释掉与否无所谓,之前的输出不会改变。

谁能告诉我这里的 char 指针是如何工作的?

【问题讨论】:

  • 请下次确保您的代码已结束等。您让我们难以阅读代码而不是专注于手头的问题。
  • *p2 = '\0'; 非常重要。仅仅因为它工作一次并不意味着它会一直工作。未定义的行为可以做到这一点。
  • 感谢您的回复。你能告诉我*p2='\0'在这里具体做什么吗?
  • @chris 我认为 *p2 = '\0'; 在技术上没有任何问题;该操作的结果已定义。
  • @Ozraptor,你颠覆了我的观点。如果没有该语句,str2 将保持未终止状态,然后输出。该输出会导致未定义的行为,因为它无法确切知道字符串何时结束。这就是空终止符的用途。

标签: c++ pointers char


【解决方案1】:

循环修改p1,使其指向字符串末尾的空终止符。这就是空字符串的定义。 p2 同样指向字符串末尾的空终止符。

如果您将p1p2 重置为其原始值,您可以看到字符串的原样。

【讨论】:

  • 感谢回复,为什么循环修改了p1指针?
  • for 循环一致地推进 p1 和 p2,并将 p1 指向的内容复制到 p2 在每次迭代中指向的字符/内存中,直到 p1 指向一个空终止字符,然后循环退出。
【解决方案2】:

该代码用于将str1复制到str2

在 C++ 中,'\0' 用于结束字符串。当您尝试打印 char 指针(例如 ptr)时,编译器会打印从 *ptr(指针指向的字符)开始的字符串。当编译器找到'\0' 时,它会停止打印。

在开头,p1 指向str1 的第一个字符,p2 指向str2 的第一个字符。如果您不做任何其他事情就打印它们,编译器将完全打印出这两个字符串。所以输出将是p1=hello,world!p2=

for 循环使p1p2 前进通过str1str2。最后,p1 指向str1 末尾的\0,p2 指向str2 末尾的'\0'。所以如果你在for循环结束后直接打印p1p2,编译器会立即找到'\0'并停止打印。所以,你得到了输出p1=p2=

取消注释p1=str1; p2=str2; 将使两个字符串再次指向第一个字符,因此现在打印它们将导致打印整个字符串。所以你得到了输出p1=hello,world!p2=hello,world!(因为str1在for循环中被复制到str2)。

*p2 = '\0' 仅用于以 '\0' 结尾的 str2。如果您的代码在没有该行的情况下工作,则意味着编译器将str2 的所有字符自动初始化为'\0'。但是,编译器不能保证这样做,因此您应该始终在程序中使用'\0' 终止字符串。

【讨论】:

  • "如果你打印它们而不做任何其他事情,编译器将完全打印出两个字符串。所以输出将是p1=hello,world!p2=。" - 您错过了 str2 具有未初始化元素的未定义行为。它可能是剩余的内存(即不是编译器),或者只是它是未定义的行为,在那里找到了一个值 'a' 并决定它不想打印任何东西。
  • 我意识到了这一点,但这似乎与重点无关。看到程序打印p2=hello, world!,即使他在循环后省略了*p2='\0',很明显他的编译器正在将str2的每个字符初始化为'\0'
  • 谢谢你这么详细的回答,现在我明白了。非常感谢!!!!顺便说一句,我在 Ubuntu 上使用的是 g++ 4.6.1。
【解决方案3】:

这是我从 VS2010 运行该代码时看到的输出,其中注释部分未注释:

p1=h
p2=h
p1=e
p2=e
p1=l
p2=l
p1=l
p2=l
p1=o
p2=o
p1=,
p2=,
p1=w
p2=w
p1=o
p2=o
p1=r
p2=r
p1=l
p2=l
p1=d
p2=d
p1=!
p2=!
p1=hello,world!
p2=hello,world!

这几乎是我所期望的!基本上,这段代码是通过直接指针操作将 str1 的内容复制到(未初始化的)char 数组 str2 中,每次将 str1 中的每个字符复制到 str2 中。

回答你最后一个问题,原因

*p2='\0';

以便 for 循环“创建”的第二个字符串将正确地以 null 终止。如果没有该行,它将只是一个 char 数组,不能被视为“C”字符串。

总体而言,这是一个非常人为/不可靠的示例,因为一旦我们的第一个字符串的长度超过 20 个字符,它将无法工作,因为 str2[] 被声明为只有 20 个字符的大小。

【讨论】:

  • 谢谢。但我认为*p2='\0'这里只是p2的重置。正是这一行 p1=str1; p2=str2; 使得 p2=hello,world! ,但我很困惑为什么在 for 循环之后, p1' 可以改变?而且我还把*p2='\0'改成了*(p2+12)='\0',把p1=str1; p2=str2;注释掉了,输出还是显示p1= p2=,为什么会这样?
  • @user2345484 否 - 该行正在做的是确保 str2 中最后一个复制字符之后的字符将是 '\0' - 即空终止。您可以轻松地从 str1 复制终止符,然后退出循环,但这段代码却反过来。
  • @user2345484 p1=str1; p2=str2 只是将 p1 和 p2 指针 重置为指向 str1 和 str2 字符缓冲区中的第一个字符,然后将 p1 和 p2 视为 char[] 类型。请记住,*p2 = {blah] 修改的是 p2 指向的位置,而不是 p2 本身的值。
  • @user2345484,在循环之后,p2 指向字符串中最后一个字符之后的一个,因此它在此处被取消引用。要使第一个起作用,它必须指向开头,并且您至少应该有更好的表示 12 的方式,例如strlen(str1)
  • @user2345484 因为一旦 for 循环完成, p2 已经 被推进了 12 次,所以如果你按照你的建议去做,你会 a) 越过分配的str2缓冲区(非常糟糕)和b)即使缓冲区足够大,'!'之间也会有12个未定义内容的字符和 str2 中的终止符。
【解决方案4】:

C++ 字符串实际上是一个 char *,p1 和 p2 都指向同一个字符串,随着它们的递增,它们会遍历字符串 "*p2='\0';" 的字符将字符串设置为空字符,它对程序没有影响,因为它无论如何都会在后面的行中被重置。

【讨论】:

  • “C++ 字符串在底层是一个 char *”。不,它是一个 char[] 和 arrays are not pointers。另外,p1 和 p2 没有指向同一个字符串。
  • 在循环中它们指向同一个字符串 p2 被反复设置为 p1,我也有点困惑,因为 p1 和 p2 是指针。你不得不承认这段代码写的很糟糕
  • p1p2 指向不同的字符串。一个正在通过指针逐个字符地复制到另一个中。无论如何,C++ 字符串 (std::string) 有时必须使用 char *,因为它需要能够动态分配内存。据我所知,char[] 的唯一选择是 SSO。
猜你喜欢
  • 1970-01-01
  • 2012-09-21
  • 1970-01-01
  • 2020-11-28
  • 2012-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多