Home > Mobile >  If I want to reimplement the memcpy in C, should I check for NULL argument?
If I want to reimplement the memcpy in C, should I check for NULL argument?

Time:03-17

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
}
  • Related