-1

In practicing for final exam in my high school, we got following question:

Find values of strings s1, s2 and s3 after code executed:

char s1[] = "Short Message Service", *s2, *s3;
s2 = strchr(s1, 'M');
s3 = strrchr(s2,'S');
strncpy(s1 + 1, s2, 1);
strcpy(s1 + 2, s3);

Whole class expected result to be:

s1 = SMService
s2 = Message Service
s3 = Service

When we tested it by executing code we were surprised to see result is:

s1 = SMService
s2 = ice
s3 = Service

The problem is nobody can figure out why s2 got shortened. While trying to figure it out, I found out s2 is remaining "Message Service" until the last line of code where "strcpy" function executes. I assume the problem might be in pointer addresses but I couldn't figure out how strcpy is affecting s2.

So my question is why s2 isn't what we expected it to be and why it got shortened?

  • It *appears* shortened because of the terminated dropped in with the final `strcpy`. This is *all* happening in the *same buffer* (`s1[]`), and a debugger will make that blatantly obvious. Look at where `s2` points: `s1+6`. Now look at what is at `s1+6` after the final `strcpy`: the tail characters of `SMService`, and that's `ice` and a terminator. – WhozCraig Jun 04 '18 at 23:40
  • @WhozCraig Thank you for that explanation. But if so is the case (that `s2` points: `s1+6`) does `s3` point to `s2+7` or straight to `s1+13`? And how in any of those two cases `s3` comes to be equal to `"Service"` in the end? – Stevan Popovic Jun 04 '18 at 23:53
  • The address stored in `s3` never changes after the `strrchr` result was assigned, which is `s1+14` Nothing every overwrote or changed an data at that address through the end of the buffer, so the render of `s3` appears as simply `Service` when rendered. – WhozCraig Jun 04 '18 at 23:56
  • @WhozCraig Thank you, your explanation was really helpful! – Stevan Popovic Jun 05 '18 at 00:05

1 Answers1

2

In your code s2 was pointing to the M in s1 and then got overwritten by s3 in your last strcpy:

char s1[] = "Short Message Service", *s2, *s3;
s2 = strchr(s1, 'M');   // s2 is pointing to s1 + 6 = Message Service
s3 = strrchr(s2, 'S');  // s3 is pointing to s1 + 14 = Service 
strncpy(s1 + 1, s2, 1); // Write M in to s1[1], s1 = SMort Message Service 
strcpy(s1 + 2, s3);     // Write Service into s1 + 2
                        // s1 = SMService but s2 is pointing to s1 + 6 = ice
StaticBeagle
  • 5,070
  • 2
  • 23
  • 34
  • 1
    `strcpy(s1 + 2, s3);` is UB as the source and destination overlap. Violates the `restrict` of `char *strcpy(char * restrict s1, const char * restrict s2);`. This renders the test case a flop. – chux - Reinstate Monica Jun 05 '18 at 03:30
  • 1
    @chux: I don't believe that to be true. `s3` has the value `s1+14` and the `strcpy` will copy 8 characters, including the terminating NUL, to locations `s1+2` through `s1+9`. There is no overlap. No objects accessed through the `strcpy` argument `s2` are modified (or even read) through the argument `s1`. The restriction imposed by `restrict` applies to the objects referenced by a pointer; for a pointer of type `char*` all of those objects have type `char`. In other words, `restrict` is not concerned with some extent surrounding the value referenced by the pointer; only to actual values. – rici Jun 05 '18 at 05:26
  • See the example in paragraphs 8 and 9 of 6.7.3.1. the call to `f(50, d + 50, d)` is valid precisely because the ranges referenced by the two pointer parameters in that call do not overlap. In the second invocation `f(50, d + 1, d)`, there is an overlap and the call is UB. – rici Jun 05 '18 at 05:30
  • 1
    @rici Agreed - it was late for me. Close to the edge is not over the edge. – chux - Reinstate Monica Jun 05 '18 at 10:41