You can use the = operator to copy integers, but you cannot use the = operator to copy strings in C. Strings in C are represented as arrays of characters with a terminating null-character, so using the = operator will only save the address (pointer) of a string.
#include <stdio.h>
int main(void)
{
int a = 10, b;
char c[] = "abc", *d;
b = a; /* Integer is copied */
a = 20; /* Modifying a leaves b unchanged - b is a 'deep copy' of a */
printf("%d %d\n", a, b); /* "20 10" will be printed */
d = c; /* Only copies the address of the string. there is still only one string stored in memory */
c[1] = 'x'; /* Modifies the original string - d[1] = 'x' will do exactly the same thing */
printf("%s %s\n", c, d); /* "axc axc" will be printed */
return 0;
}
The above example compiled because we used char *d rather than char d[3]. Using the latter would cause a compiler error. You cannot assign to arrays in C.
#include <stdio.h>
int main(void)
{
char a[] = "abc";
char b[8];
b = a; /* compile error */
printf("%s\n", b);
return 0;
}
Copying strings using standard functions strcpy()
To actually copy strings, strcpy() function is available in string.h. Enough space must be allocated for the destination before copying.
#include <stdio.h>
#include <string.h>
int main(void)
{
char a[] = "abc";
char b[8];
strcpy(b, a); /* think "b special equals a" */
printf("%s\n", b); /* "abc" will be printed */
return 0;
}
Version ≥ C99
snprintf()
To avoid buffer overrun, snprintf() may be used. It is not the best solution performance-wise since it has to parse the template string, but it is the only buffer limit-safe function for copying strings readily-available in standard library, that can be used without any extra steps.
#include <stdio.h>
#include <string.h>
int main(void)
{
char a[] = "012345678901234567890";
char b[8];
#if 0 strcpy(b, a); /* causes buffer overrun (undefined behavior), so do not execute this here! */
#endif
snprintf(b, sizeof(b), "%s", a); /* does not cause buffer overrun */
printf("%s\n", b); /* "0123456" will be printed */
return 0;
}
strncat()
A second option, with better performance, is to use strncat() (a buffer overflow checking version of strcat()) - it takes a third argument that tells it the maximum number of bytes to copy:
char dest[32];
dest[0] = '\0';
strncat(dest, source, sizeof(dest) - 1); /* copies up to the first (sizeof(dest) - 1) elements of source into dest, then puts a \0 on the end of dest */
Note that this formulation use sizeof(dest) - 1; this is crucial because strncat() always adds a null byte (good), but doesn't count that in the size of the string (a cause of confusion and buffer overwrites).
Also note that the alternative — concatenating after a non-empty string — is even more fraught. Consider:
char dst[24] = "Clownfish: ";
char src[] = "Marvin and Nemo";
size_t len = strlen(dst);
strncat(dst, src, sizeof(dst) - len - 1);
printf("%zu: [%s]\n", strlen(dst), dst);
The output is:
23: [Clownfish: Marvin and N]
Note, though, that the size specified as the length was not the size of the destination array, but the amount of space left in it, not counting the terminal null byte. This can cause big overwriting problems. It is also a bit wasteful; to specify the length argument correctly, you know the length of the data in the destination, so you could instead specify the address of the null byte at the end of the existing content, saving strncat() from rescanning it:
strcpy(dst, "Clownfish: ");
assert(len < sizeof(dst) - 1);
strncat(dst + len, src, sizeof(dst) - len - 1);
printf("%zu: [%s]\n", strlen(dst), dst);
This produces the same output as before, but strncat() doesn't have to scan over the existing content of dst before it starts copying.
strncpy()
The last option is the strncpy() function. Although you might think it should come first, it is a rather deceptive function that has two main gotchas:
- If copying via strncpy() hits the buffer limit, a terminating null-character won't be written.
- strncpy() always completely fills the destination, with null bytes if necessary.
(Such quirky implementation is historical and was initially intended for handling UNIX file names)
The only correct way to use it is to manually ensure null-termination:
strncpy(b, a, sizeof(b)); /* the third parameter is destination buffer size */ b[sizeof(b)/sizeof(*b) - 1] = '\0'; /* terminate the string */
printf("%s\n", b); /* "0123456" will be printed */
Even then, if you have a big buffer it becomes very inefficient to use strncpy() because of additional null padding.