omn_1
omn_1

Reputation: 355

Unexpected results when using memcpy

Hi i came across with this simple c program but i cant understand how this code works:

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

char *a = "\0hey\0\0";   /*  6 */
char *b = "word\0up yo"; /* 10 */
char *c = "\0\0\0\0";    /*  4 */

int main(void)
{
    char z[20];
    char *zp = z;
    memcpy(zp, a, strlen(a)+1);
    memcpy(zp, b, strlen(b)+1);
    memcpy(zp, c, strlen(c)+1);
    /* now z contains all 20 bytes, including 8 NULLs */
    int i;

for(i = 0; i < 20; i++){
    if (z[i] == 0){
      printf("\\0");
    }

    printf("%c", z[i]);}
    return 0;
}

I was expecting that printing z the output would be :

\0hey\0\0\0word\0up yo\0\0\0

But instead I am getting :

\0ord\0\0\0\0\0\0\0\0\0\0\0\0???Z

Finally , when i print a instead of z i get the right output. Can anyone explain to me why this happens ? Thanks in advance.

EDIT: How i could concatenate such strings?

Upvotes: 1

Views: 573

Answers (1)

kfx
kfx

Reputation: 8537

Strings in C are zero-terminated; the functions in the standard C library assume this property. In particular, the function strlen returns the number of non-zero characters from the start of the string. In your example, strlen(a) is equal to 0, already as the first character of a is zero.

The code will have the following effect:

 memcpy(zp, a, strlen(a)+1);

Now zp still contains \0, because strlen(a) is 0, so 1 character is copied.

 memcpy(zp, b, strlen(b)+1);

Now zp contains word\0: five characters copied.

 memcpy(zp, c, strlen(c)+1);

Now just the first character of zp is overwritten, so it contains \0ord\0.

Finally , when i print a instead of z i get the right output. Can anyone explain to me why this happens ? Thanks in advance.

That's because a, b, and c happen to be allocated sequentially in the memory. When you print "20 bytes starting from the start of a", you're actually looking at the memory past the latest byte of a. This memory happens to contain b. So you actually start reading b. Same goes for b and c. Note that this is by no means guaranteed. Looking past the memory allocated for a char * is in fact an instance of undefined behaviour.

How i could concatenate such strings?

In general, there is no way how to find the length of such "strings" in the runtime. I would not call them strings as such, since "string" has a specific meaning in the C language - it refers to zero terminated strings, while your's are simply regions of memory.

However, since you know the size at compile time, you can use that. To avoid magic numbers in the code, it's better to use char arrays instead of char pointers, because then you can use the sizeof operator. However, note that all string literals in C are implicitly zero terminated! To fit the result in the 20-byte buffer, you'll want to use sizeof(x) - 1:

char a[] = "\0hey\0\0";   /*  6 */
char b[] = "word\0up yo"; /* 10 */
char c[] = "\0\0\0\0";    /*  4 */

memcpy(zp, a, sizeof(a) - 1);
zp += sizeof(a) - 1;
memcpy(zp, b, sizeof(b) - 1);
zp += sizeof(b) - 1;
memcpy(zp, c, sizeof(c) - 1);

Upvotes: 1

Related Questions