cstringstrcpystrncpy

How strcpy changes the value of string


I got this homework to decide what the following code will do (on paper, without testing on computer).

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

When I wanted to check if I did it right, I ran it on computer and got this result:

s1 = SMservice 

s2 = ice

s3 = Service

I thought that s2 will be "Message Service" but it changes to "ice". Apparently it changes after strcpy(s1+2,s3) is called; can somebody explain why and how that function affects s2?


Solution

  • The answer is "undefined behaviour" — anything can happen. The arguments to strcpy() and strncpy() must not overlap. — yet here, the arguments to strcpy() do overlap.

    C11 §7.24.2.3 The strcpy function ¶2:

    The strcpy function copies the string pointed to by s2 (including the terminating null character) into the array pointed to by s1. If copying takes place between objects that overlap, the behavior is undefined.

    §7.24.2.4 The strncpy function ¶2

    The strncpy function copies not more than n characters (characters that follow a null character are not copied) from the array pointed to by s2 to the array pointed to by s1.308) If copying takes place between objects that overlap, the behavior is undefined.

    308) Thus, if there is no null character in the first n characters of the array pointed to by s2, the result will not be null-terminated.

    That means there is no reliable answer that can be given. You might decide that you'd then describe what would happen if the copy operations copy from the start of the source over the destination, which is what your instructor likely expects. But that is not guaranteed behaviour.

    Given the following code and the left-to-right copying assumption:

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

    We can deduce that s2 points to &s1[6] and s3 points to &s1[14] (and this is mandatory). The values in s1 at various stages are:

    s1 = "Short Message Service"   -- initial text
    s1 = "SMort Message Service"   -- after strncpy
    s1 = "SMService"               -- after strcpy (but this assumes UB works as expected)
    

    So the string starting at s2 now contains ice, as you found.

    However, it must be re-emphasized, this is not required behaviour.