2

This is one of the questions that may end up on a test I'm going to have in a couple of days at school. I'm having trouble with understanding how char* s2 changes from "Message Service" to "ice" after the last strcpy() call. I think that this is some kind of consequence of pointers, but that's all I know.

char s1[] = "Short Message Service";
char *s2, *s3;

s2 = strchr(s1, 'M');

s3 = strrchr(s2, 'S');

strncpy(s1 + 1, s2, 1);

strcpy(s1 + 2, s3);  

I tried debugging the program and watching the variables and pointers as they change, but I still don't understand exactly how the last call even affects char *s2.

I expected the value of s2 to be "Message Service", but the actual output ends up as "ice" instead. Though, I do understand how and why s1 and s3 change.

  • 1
    s2 points to a substring of s1. So when changing memory of s1, you can potentially change s2 too. Here that's what happens. – Jean-François Fabre Jun 09 '19 at 19:42
  • 1
    `strchr` and `strrchr` each return a pointer that points somewhere inside the original string argument. If you subsequently change the contents of that string, then whatever those pointers point to can also be affected since they're part of the same string. You should read the manual pages for the string functions carefully. – lurker Jun 09 '19 at 19:44
  • @xing thank you for your advice, but this is a C theory question and I need to write down the results of s1, s2 and s3 after the strcpy and strchr commands are called. – Andrija Tošić Jun 09 '19 at 19:47
  • Thank you for the answers, guys, but I was wondering can someone explain what exactly happens when the function is called? I understand that this is because of pointers and that the substrings which are pointed at are changing, but I wanted to know why it exactly ends up as "ice". – Andrija Tošić Jun 09 '19 at 19:49

1 Answers1

4

If we show the array, with the corresponding pointers added, it will look something like this:

+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
| 'S' | 'h' | 'o' | 'r' | 't' | ' ' | 'M' | 'e' | 's' | 's' | 'a' | 'g' | 'e' | ' ' | 'S' | 'e' | 'r' | 'v' | 'i' | 'c' | 'e' | '\0' |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
^                                   ^                                               ^
|                                   |                                               |
s1                                 s2                                               s3

When you modify the contents of the array, the pointers s2 and s3 themselves doesn't change, they still point to the same locations in the array.

So after

strncpy(s1 + 1, s2, 1);

the array (with pointers) looks like

+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
| 'S' | 'M' | 'o' | 'r' | 't' | ' ' | 'M' | 'e' | 's' | 's' | 'a' | 'g' | 'e' | ' ' | 'S' | 'e' | 'r' | 'v' | 'i' | 'c' | 'e' | '\0' |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
^                                   ^                                               ^
|                                   |                                               |
s1                                 s2                                               s3

The single character that s2 is pointing to is copied to s1[1].

Then with

strcpy(s1 + 2, s3);  

the array looks like

+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
| 'S' | 'M' | 'S' | 'e' | 'r' | 'v' | 'i' | 'c' | 'e' | '\0' | 'a' | 'g' | 'e' | ' ' | 'S' | 'e' | 'r' | 'v' | 'i' | 'c' | 'e' | '\0' |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+
^                                   ^                                                ^
|                                   |                                                |
s1                                 s2                                                s3

The sub-string "Service" is copied over part of the string beginning on s1[2].

The pointer s2 is still pointing to the same location, but if we consider s2 as a pointer to the first character of a string then its contents have changed because you have overwritten that contents.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • I've been stressing over this question for 2 hours now. Thank you for this beautiful and clear answer, everything is clear now. – Andrija Tošić Jun 09 '19 at 19:56
  • 1
    @AndrijaTošić Next time you have problem with arrays and pointers, just pull out a pen and some paper and draw it out yourself. It can really help wonders. :) – Some programmer dude Jun 09 '19 at 20:03