I am trying to override part of my first string with my second one with an offset of 2.
Here is my second attempt that works:
#include <stdio.h>
#include <string.h>
int main() {
char s[] = "Hello, World!";
char a[] = "aaa";
printf("%d\n", &s);
printf("%d\n", &s[2]);
memcpy(&s[2], a, sizeof(a)-1);
printf("%s\n", s);
return 0;
}
$ gcc -o test test2.c && ./test
520783106
520783108
Heaaa, World!
And here is my first attempt, that does not work, raises a warning at compilation and segfaults:
#include <stdio.h>
#include <string.h>
int main() {
char s[] = "Hello, World!";
char a[] = "aaa";
printf("%d\n", &s);
printf("%d\n", sizeof(char)*2);
printf("%d\n", &s + (sizeof(char)*2));
memcpy(&s + (sizeof(char)*2), a, sizeof(a)-1);
printf("%s\n", s);
return 0;
}
$ gcc -o test test1.c && ./test
test1.c: In function ‘main’:
test1.c:12:4: warning: ‘memcpy’ writing 3 bytes into a region of size 0 overflows the destination [-Wstringop-overflow=]
12 | memcpy(&s + (sizeof(char)*2), a, sizeof(a)-1);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test1.c:5:9: note: at offset 28 into destination object ‘s’ of size 14
5 | char s[] = "Hello, World!";
| ^
1547566642
2
1547566670
Hello, World!
Segmentation fault
Can someone explain to me why test1 is not working ?
Why is the sum of 1547566642 and 2 equal 1547566670 and not 1547566644 ?
I assume it's a type problem. The return of sizeof is size_t but what is the type of &s ?
Thank you in advance for your help. ;)
Your example has turned a bit complicated, likely by accident. The thing you need to focus on understanding is "array decay", which means that whenever an array is used in most expressions, it "decays" into a pointer to its first element.
For example in the expression s[2]
, s
decays to a pointer to char
. The []
operator is defined to be 100% equivalent to pointer arithmetic, meaning that s[2]
is identical to writing *(s+2)
(but the former is best since it is more readable). In either case you end up dealing with a pointer and not an array.
Now there are some exceptions when array decay does not happen and you've encountered some of them here. When the address-of operator is used on array, decay does not happen. So &s
does not give the address of the first element, but of the array itself. Although since both the array and its first element has the same address, you get the same address no matter if you print &s
or &s[0]
. But they are different types. &s
gives a pointer to array of type char (*)[14]
in this case, but &s[0]
gives a plain char*
.
Now these types matter when you start to do pointer arithmetic, because when doing that, the addition works on sizeof(pointed-at-item)
basis. And so &s + 2
gives an increase of 2*14 = 28 bytes, whereas &s[0] + 2
gives an increase of 2 bytes. The former is obviously wrong here.
Another exception to the array decay is the sizeof
operator - as you noticed, you can use it on arrays and get the array size in bytes. It wouldn't work on a decayed/pointed-at array, so if we do char* ptr = s; ... sizeof(ptr)
we don't get the size of the array but the pointer itself, which isn't likely useful.
Other things of note:
sizeof(char)
is by definition guaranteed to always be 1
so there's no point of multiplying anything with sizeof(char)
- it just adds clutter to the code.printf
, you are supposed to use %p
. Partially because %d
might not be large enough to correctly represent an address on the given system (it is not on x86_64 for example), partially because addresses are always written in hex and %p
takes care of that. Pedantically, we should also cast the pointer to (void*)
, so printf("%p\n", (void*)&s[0]);
would be the most correct way to print the address of the first item.