1

I got this code from a textbook:

#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;
}

I ran this code, it will output p1=hello,world!p2= which I can understand.

But if I uncomment the for loop, the output shows here I got confused, why after the for loop, why it shows p1= instead of showing p1=hello,world!, and for pointer p2, even after the assignment in the for loop, it still shows p2=?

But after I uncomment p1=str1; p2=str2; this line, the output is p1=hello,world!, p2=hello,world!, why it works like that?

And what's the reason for writing this line *p2='\0';, it doesn't matter that this line is commented out or not, the previous outputs don't change.

can anyone tell me how the char pointer here is working?

Anubhav C
  • 2,631
  • 2
  • 18
  • 23
Allanqunzi
  • 3,230
  • 1
  • 26
  • 58
  • Please next time make sure your code is endented etc. Your making us struggle to read the code rather than focusing on the problem at hand. – Chris Condy May 03 '13 at 04:06
  • `*p2 = '\0';` is very important. Just because it works once doesn't mean it will always work. Undefined behaviour can do that. – chris May 03 '13 at 04:06
  • Thanks for replying. Can you tell me what exactly does `*p2='\0'` do here? – Allanqunzi May 03 '13 at 04:15
  • @chris I don't think there is anything technically wrong with *p2 = '\0'; The result of that operation is defined. – Ozraptor May 03 '13 at 04:24
  • @Ozraptor, You reversed my point. Without that statement, `str2` is left unterminated, and then output. That output causes undefined behaviour because it can't know exactly when the string ends. That's what the null terminator is for. – chris May 03 '13 at 04:26
  • @chris yes Ok, I agree that is correct! :-) – Ozraptor May 03 '13 at 04:38
  • @Ozraptor, My statement is very misleading. I should have been more specific. I also snuck an answer to the OP into my last comment. – chris May 03 '13 at 04:41

4 Answers4

2

The loop modifies p1 so that it points to the null terminator at the end of the string. That's the definition of an empty string. p2 likewise points to a null terminator at the end of a string.

If you reset p1 and p2 to their original values you can see the strings as they are.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Thanks for replying, but why the loop modifies the p1 pointer? – Allanqunzi May 03 '13 at 04:13
  • The for loop advances p1 and p2 in unison, and copies what p1 points at into the char / memory that p2 points at in each iteration, up until p1 points at a null-termination char, then the loop exits. – Ozraptor May 03 '13 at 04:30
1

Here is the output I see from VS2010 running that code with the commented parts uncommented:

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!

That's pretty much what I would have expected! Basically this code is copying the contents of str1 into the (uninitialised) char array str2 via direct pointer manipulation, by copying each character from str1 into str2 one at a time.

To answer your last question, the reason for

*p2='\0';

is so that the second string that is being "created" by the for loop will be correctly null terminated. Without that line, it will just be a char array that cannot be treated like a 'C' string.

Overall this is a pretty contrived / non robust example though, as it won't work once we exceed 20 characters in length for the first string, due to str2[] being declared to be only 20 chars in size.

Ozraptor
  • 564
  • 4
  • 11
  • Thanks. But I think `*p2='\0'` here is just the resetting of p2. It is this line `p1=str1; p2=str2;` that makes `p2=hello,world!` , but I got confused that why after the for loop, the `p1' ` can change? And I also changed `*p2='\0'` to `*(p2+12)='\0'`, with `p1=str1; p2=str2;` uncommented, the output still shows `p1= p2=`, why it is like that? – Allanqunzi May 03 '13 at 04:22
  • @user2345484 No - what that line is doing is ensuring that the character immediately after the last copied char in str2 will be '\0' - ie a null-termination. You could just as easily copy the terminator from str1 and then exit the loop, but this code doesit the other way around. – Ozraptor May 03 '13 at 04:26
  • @user2345484 the p1=str1; p2=str2 is just resetting the p1 and p2 *pointers* to point to the first char in the str1 and str2 char buffers, which then enables p1 and p2 to be treated like char[] types. Remember that *p2 = {blah] modifies what p2 POINTS at, not the value of p2 itself. – Ozraptor May 03 '13 at 04:28
  • @user2345484, After the loop, `p2` points to one after the last character in the string, so it's dereferenced right there. For the first to work, it would have to be pointing to the beginning, and you should at least have a better way of saying 12, such as `strlen(str1)`. – chris May 03 '13 at 04:35
  • @user2345484 because once the for loop has completed, p2 has *already* been advanced 12 times, so if you did what you suggest you would a) be writing past the boundary of the allocated str2 buffer (very bad) and b) even if the buffer was large enough, there would be 12 characters with undefined contents in between the '!' and the terminator in str2. – Ozraptor May 03 '13 at 04:36
  • Thanks for all your replying, now I understand. – Allanqunzi May 03 '13 at 04:46
1

The code is for copying str1 to str2.

In C++, '\0' is used to end a string. When you try to print a char pointer (say ptr), the compiler prints the string starting from *ptr (the character pointed to by the pointer). When the compiler finds '\0', it stops printing.

In the beginning, p1 points to the first char of str1 and p2 points to the first char of str2. If you print them without doing anything else, the compiler will print both the strings out completely. So the output will be p1=hello,world!p2=.

The for loop makes p1 and p2 advance through str1 and str2. At the end, p1 points to the \0 at the end of the str1 and p2 points to the '\0' at the end of str2. So if you print p1 or p2 directly after the for loop ends, the compiler will immediately find '\0' and stop printing. So, you get the output p1=p2=.

Uncommenting p1=str1; p2=str2; will make both strings point to the first characters again, so printing them now will cause the whole string to be printed. So you get the output p1=hello,world!p2=hello,world! (because str1 got copied to str2 in the for loop).

The *p2 = '\0' is just for ending str2 with '\0'. If your code works without that line, it means that the compiler initialized all the characters of str2 to '\0' automatically. However, the compiler isn't guaranteed to do that, so you should always terminate strings with '\0' in your programs.

Anubhav C
  • 2,631
  • 2
  • 18
  • 23
  • "If you print them without doing anything else, the compiler will print both the strings out completely. So the output will be `p1=hello,world!p2=`." - You're missing the undefined behaviour from `str2` having uninitialized elements. It could be leftover memory (i.e., not the compiler), or just the fact that it's undefined behaviour, found a value of 'a' there and decided it wanted to print nothing instead. – chris May 03 '13 at 04:31
  • I realized that, but it seemed tangential to the point. Seeing as the program prints `p2=hello, world!` even if he leaves out the `*p2='\0'` after the loop, it's obvious that his compiler is initializing every character of `str2` to `'\0'`. – Anubhav C May 03 '13 at 04:38
  • 1
    Thanks for such a detailed answer, now I understand. Thank you very much!!!! By the way, I am using g++ 4.6.1 on Ubuntu. – Allanqunzi May 03 '13 at 04:39
0

A c++ string is a char * under the hood, p1 and p2 are both pointing to the same string, as they are incremented they go through the characters of the string "*p2='\0';" sets the string to the null character it has no effect on the program because it is being reset anyway in the line after.

aaronman
  • 18,343
  • 7
  • 63
  • 78
  • "A c++ string is a char * under the hood". No, it's a char[], and [arrays are not pointers](http://stackoverflow.com/a/1641963/1321855). Also, p1 and p2 aren't pointing to the same string. – Anubhav C May 03 '13 at 04:16
  • They are pointing to the same string during the loop p2 is repeatedly set to p1, also I was a little confused because p1 and p2 are pointers. You have to admit this code is terribly written – aaronman May 03 '13 at 04:24
  • `p1` and `p2` point to different strings. One is being copied into the other, character by character, via pointers. Anyway, a C++ string (`std::string`) has to use a `char *` sometime, since it needs to be able to dynamically allocate memory. The only option for a `char[]` is SSO as far as I'm aware. – chris May 03 '13 at 04:27