src
and dst
have size 0, which is one way of specifying flexible arrays in C. You usually only define them inside structures that are going to be dynamically allocated, for example:
struct buffer {
size_t len;
char bytes[0]
};
#define NBYTES 8
struct buffer* ptr = malloc(sizeof(struct buffer) + NBYTES * sizeof(char));
const char* src = "hello!";
ptr->len = strlen(src);
memcpy(ptr->bytes, src, ptr->len);
Basically, indexing any of those arrays in your example will end up in a buffer overflow (you are accessing beyond the limits of the array).
The difference between this and passing NULL
as parameters is that src
and dst
point to valid memory (main
function stack). In C a buffer overflow has no defined behaviour (undefined behaviour), so the compiler is free to do what it wants. If you use a memory sanitizer (compile with -fsanitize=address
) it will warn you about this problem and ask you to fix the error.
I recommend you using a debugger or add the following print statements in your copy function:
printf("%s: src: %p, dst: %p\n", __func__, src, dst);
See Array of zero length
Update: since you asked how to generate the abort error, the easiest and most convenient way for this scenario is using assertions.
#include <assert.h>
void function(void *addr) {
assert(addr != NULL);
}
will abort the execution if the condition addr != NULL
evaluates to false
.
Assertions are very useful to expose what conditions you assume will always be valid and for whom you don't want to pay the cost of checking them when you build the code for production, since these checks may have a performance impact. You can disable them by compiling with the flag -DNDEBUG
.
See also: When should we use asserts in C?
Another way is making the program to simply abort:
#include <cstdlib.h>
void function(void *addr) {
if(addr == NULL) abort();
}
or to set errno
variable to EINVAL
:
#include <errno.h>
void function(void *addr) {
if (addr == NULL) {
errno = EINVAL;
return;
}
}