If I want to reimplement the memcpy in C, how should I check for NULL argument?
1.Check if both are NULL
if (!dst && !src)
return (0);
2.Check if one of them is NULL
if (!dst || !src)
return (0);
Or actually, I don't need to check for NULL argument? I'm a bit confused since when I pass memcpy("Hello","",1), I get nothing. But when I pass memcpy("Hello",NULL,1), I get segmentation fault.
#include <stdio.h>
#include <string.h>
int main(void)
{
const char src[] = "";
char dest[50] = "Hello";
memcpy(dest,src,1);
printf("Result:'%s'\n", dest); //Result:''
}
#include <stdio.h>
#include <string.h>
int main(void)
{
//const char src[] = NULL;
char dest[50] = "Hello";
memcpy(dest,NULL,1);
printf("Result:'%s'\n", dest); //zsh: segmentation fault
}
CodePudding user response:
Generally, library or production-quality functions in C do not perform any input validation of parameters. The reason why is performance - the spirit of C is to always implement everything as fast as possible, often at the cost of safety/security.
Should you do a null pointer check for every parameter, then those need to be checked always, even when the caller is certain that they are passing valid pointers to the function. Therefore such error checks should be performed by the caller and you should tell them as much in your source code documentation.
Specifically, to check a pointer against null introduces a branch in the code, which in turn is a performance bottleneck. memcpy
is extremely real-time critical, probably more so than any other function in C.
You could however in theory declare memcpy
like this:
void* my_memcpy (uint8_t dst[static restrict 1],
const uint8_t src[static restrict 1],
size_t n);
Very modern compilers will then at least perform a compile-time check against NULL on the caller side and produce a warning:
my_memcpy(foo, NULL, 100);
warning: argument 2 to 'uint8_t {aka const unsigned char}[static 1]' is null where non-null expected [-Wnonnull]
But this is almost useless safety and it creates strange-looking code in the process, so I don't really recommend it.
CodePudding user response:
Passing a NULL
to memcpy(s1, s2, n)
is undefined behavior UB even is n == 0
.
Since it it UB, your code can do what ever it wants.
memcpy()
, is well understood by compilers and since quite important to many a C program, compilers are allowed to replace a function call with equivalent functionality via in-line code. Your replacement function will likely not get that advantage, so expect a performance impact - hopefully not severe.
As an alternative, consider testing parameters in debug mode.
void *my_memcpy(void * restrict s1, const void * restrict s2, size_t n);
#ifndef NDEBUG
if (s1 == NULL || s2 == NULL) return s1; // continue on
#endif
// or maybe
assert(s1 != NULL && s2 != NULL); // abort
...
}
If still looking for what parameter tests make sense:
Testing for src == NULL
and testing for dst == NULL
makes some in debug mode.
IMO, if src
or dst
are NULL
, the only time code should consider returning is when n == 0
and then it should return dst
.
Otherwise or always, simply stop.
if (src == NULL || dst == NULL) {
if (sz == 0) return dst; // or just stop
fprintf(stderr, "memcpy parameters %p %p %zu\n", dst, src, sz);
abort(EXIT_FAILURE); // or the like
}