1
char a[3], b[3];
strcpy(a,"abc");
printf("a1 = %s\n", a);
strcpy(b,a);
printf("a2 = %s\n", a);
printf("b = %s\n", b);

From how I understand strcpy to work the output would be:

a1 = abc  
a2 = abc  
b = abc

Instead I obtain

a1 = abc  
a2 =  
b = abc

Why when I call strcpy the second time does it (apparently) erase the contents of a?

Thanks

ANG-MO
  • 81
  • 1
  • 2
  • 5

4 Answers4

3

This is a buffer overflow problem - your a and b are too short – they don't have room for the null terminator. What is happening is a is just after b in memory, so when strcpy(b,a) executes, the null terminator stored at the end of b is actually the same memory location as the first character of a. This makes a suddenly an empty string.

For starters, make the lengths of the arrays 4 instead of 3. This is okay in sandbox/play/learning mode, but consider in production code:

  • Use safer string functions (e.g. strncpy) to avoid buffer overflows.
  • Use character arrays/buffers that support variable size or pre-calculation of the size required to fit your data.
Richard Walters
  • 1,464
  • 8
  • 16
  • Thanks, I knew this was something to be aware of but I thought I would have gotten a different error if it was the root of the problem. Can you (or anybody else) give more explanation as to why this only causes a problem in that specific spot? (and not, for instance when I intially copy "abc" to the array 'a') – ANG-MO May 06 '13 at 17:52
  • @liam-fisher note that no error happens when you initially copy 'abc" to the array `a` because although it is still a buffer overrun, there are no other variables in your code, so nothing you are using is right after `a` in memory. However, this probably still does corrupt memory, and could lead to strange behavior after the code is run, such as strange crashes in code that follows. – Richard Walters May 06 '13 at 18:04
1

Since you arrays are too small and do not have room for the null terminator you are most likely overwriting a when you try to copy a to b since the strcpy does not know when to stop copying. This declaration would fix the problem for this particular program:

char a[4], b[4];

In the general case you need to ensure that your destination has enough to space to accommodate the source as well as the null terminator.

This example gives you a better idea of what is going on, this is just for demonstration purposes and you should use code like this for anything else but to learn. This works for me in ideone and you can see if live here but may not work properly in other compilers since we are invoking undefined behavior:

#include <stdio.h>
#include <string.h>

int main()
{
    char a[3], b[4];

    // a will have a lower address in memory than b
    printf("%p %p\n", a, b);

    // "abc" is a null terminated literal use a size of 4 to force a copy of null
    strncpy(a,"abc",4);
    // printf will not overrun buffer since we terminated it
    printf("a2 = %s\n", a);

    // explicitly only copy 3 bytes
    strncpy(b,a,3);
    // manually null terminate b
    b[3] = '\0' ;

    // So we can prove we are seeing b's contents
    b[0] = 'z' ;

    // This will overrun into b now since b[0] is no longer null
    printf("a2 = %s\n", a);
    printf("b = %s\n", b);
}
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
0

The first strcpy(a,"abc") is already wrong. Don't get confused with char array versus C-String... a C-String is a always a char array, but a char array is NOT always a C-String.

A C-String must have a '\0' char in the end. So when you do strcpy "abc" -> a[3] you are actually moving the following 4 bytes to your array { 'a', 'b', 'c', '\0' }

Because a and b were created together, b is right ahead a. When you print out a it goes fine IN THIS CASE because printf() still can find a '\0' to identify as the end of string, despite it's wrong... because your '\0' char is the the area reserved to b.

The following problems are all related to the same thing... The solution is: the buffer to your C-String must the the maximum size of your string + 1, so you can guarantee you will have room for the '\0' char. If you need for more details, google for "C-String" or "null-terminated string".

Wagner Patriota
  • 5,494
  • 26
  • 49
0

You've made a very common beginners mistake. In C, there is no string primitive; when we talk about strings, we're really talking about null-terminated character arrays (or buffers, I don't care what nomenclature you like). so your char[3] will hold a string of 2 letters, plus the null terminator. Another subtle issue is that in memory, they will be laid out on the stack as a[0]a[1]a[2]b[0]b[1]b[2]--and this is the reason you didn't crash when you deserved to. See "abc" is REALLY "abc\0", so a[3] == c and b[0] == \0, and since the behavior is undefined when strings overlap (as these do), I suspect that your implementation just copied chars until it copies a \0. That being the case, strcpy(a, b) will result in a being an empty string.

On the other hand, your program works as it was written to. What you wrote isn't what you meant :)

Ben Brammer
  • 988
  • 4
  • 11
  • I understand the idea of null-terminated strings - I assumed that initialising a char array as a[3] would include an additional 4th entry for the null character, so thanks to everybody for clarifying this. What I still don't understand is why 'a' becomes blank, and why b also prints as how I intended it to. If my implementation works as you say, then strcpy(b,a) would result in the memory of these two arrays containing abcabc\0. Wouldn't 'a' print as "abcabc", then? – ANG-MO May 06 '13 at 18:07
  • watch the actual memory change in a debugger so you can see for yourself what gets written where. Where is a? Where is b? Why? It will be easier when you see it for yourself :) – Ben Brammer May 06 '13 at 21:31