Handling NULL Pointers in a Common Swap Function

I am writing a general swap function genericSwap()

for fun / exercise in C

. (Don't leave aside the question of whether this is a good idea or not.) Since the function internally uses memcpy()

, I need to handle cases where pointers that are passed NULL

, and in all other questions about common swap functions, this problem seems to be ignored. This is how I do it so far:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void genericSwap(void *x, void *y, size_t size);

int main(void)
{
        int num1;
        int num2;

        num1 = 1;
        num2 = 2;

        printf("%d\t%d\n", num1, num2);

        genericSwap(&num1, &num2, sizeof(num1));

        printf("%d\t%d\n", num1, num2);

        exit(EXIT_SUCCESS);
}

void genericSwap(void *x, void *y, size_t size)
{
        void *temp;

        if (!x || !y) {
                fprintf(stderr, "Trying to pass NULL pointer\n");
                exit(EXIT_FAILURE);
        }

        temp = malloc(size);
        if(!temp) {
                fprintf(stderr, "Memory allocation failed\n");
                exit(EXIT_FAILURE);
        }

        memcpy(temp, x, size);
        memcpy(x, y, size);
        memcpy(y, temp, size);

        free(temp);
}

      

This goes for (of course, weird) cases like genericSwap(NULL, NULL, sizeof(something))

etc. As you can see (or check if you like) the function is currently severely mistaken when a pointer is passed NULL

. This may not always be the preferred way to handle such cases. Sometimes in a program I may want the function to quietly return control to the caller, which in this case main()

. My idea would be to use an empty -condition return

after each error if

handling to genericSwap()

become:

void genericSwap(void *x, void *y, size_t size)
{
        void *temp;

        if (!x || !y) {
                fprintf(stderr, "Trying to pass NULL pointer\n");
                return;
        }

        temp = malloc(size);
        if(!temp) {
                fprintf(stderr, "Memory allocation failed\n");
                return;
        }

        memcpy(temp, x, size);
        memcpy(x, y, size);
        memcpy(y, temp, size);

        free(temp);
}

      

My question is whether the strategies I have outlined so far can be considered safe. Also, I would like to hear your suggestions on how to improve error handling in these functions.

+3


source to share


3 answers


There is no magic here. Passing data crap typically undefined behavior even in custom "library" functions. Printing of some things is a complete non-starter (for example, it stderr

can be closed).



If you want to check what you got, use assert()

- this stuff can be / ignored / by the compiler when generating production (i.e. non-development code) and causes the program in question to crash, a snapshot of its state, including the backline showing how he got there.

+1


source


Since the function internally uses memcpy (), I need to handle cases where pointers are passed that are NULL

Why?

I mean it isn't, but you're talking about argument validation, not actual functionality. It's unwise to just document that the behavior of your function is only defined if the pointer arguments appear to be valid, pointing to representations of objects of the specified size. You cannot check whether the referents are sized correctly, nor whether the pointer arguments are invalid despite the fact that they are not NULL

, so you cannot do full validation of the arguments anyway. That being said, it seems a bit odd to call pointers NULL

as a special case to catch.



As long as I'm at it, as long as you're targeting C99 or later, and you don't need to support the exchange of huge objects, you'll get much better performance by allocating temporary space on the stack, an array of length, than you would by allocating it on the heap:

void genericSwap(void *x, void *y, size_t size)
{
    unsigned char temp[size];

    memcpy(temp, x, size);
    memcpy(x, y, size);
    memcpy(y, temp, size);
}

      

+1


source


Instead of a "hard exit", you can declare the function as int genericSwap(…)

and return TRUE

either FALSE

(1 or 0) depending on success. The caller can now do the following and decide how to exit or recover:

if (!GenericSwap(p1, p2, sizeof(*p1))) { // handle error

      

0


source







All Articles