处理通用交换函数中的NULL指针

时间:2015-06-12 19:08:59

标签: c pointers generics

我正在为genericSwap()中的乐趣/练习编写一个通用交换函数C。 (让我们不管这是否是一个好主意。)由于函数内部使用memcpy(),我需要处理传递指针NULL的所有情况。关于通用交换功能的其他问题这个问题似乎被忽略了。到目前为止我是这样做的:

#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);
}

这涉及genericSwap(NULL, NULL, sizeof(something))之类的(当然很奇怪)案例等等。如您所见(或测试您是否喜欢),当传递NULL指针时,该函数当前残忍地出错。这可能并不总是处理此类案件的首选方式。有时在程序中我可能希望函数安静地将控制权返回给调用者,在这种情况下调用者是main()。我的想法是在每个错误处理return之后使用空if条件,以便genericSwap()变为:

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);
}

我的问题是,到目前为止我概述的策略是否可以被认为是安全的。另外,我想听听您关于如何改进这些功能中的错误处理的建议。

3 个答案:

答案 0 :(得分:1)

这里没有魔力。传递废话数据通常是未定义的行为,即使在自定义&#34;库&#34;功能。打印一些东西是完全不起作用的(例如,SELECT t.NUMBER, CASE WHEN s.NUM_VAL IS NOT NULL THEN 'Y' ELSE 'N' END AS YES_NO FROM SOME_OTHER_TABLE t LEFT OUTER JOIN SOME_TABLE s ON s.NUM_VAL = t.NUMBER 可能会被关闭)。

如果你想要理智地检查你得到的东西,请使用stderr - 编译器在生成生产(即非开发代码)时可以/忽略这些东西并导致有问题的程序崩溃,通常提供其状态的快照,包括显示它如何到达的回溯。

答案 1 :(得分:1)

  

由于函数内部使用memcpy(),我需要处理传递指针为NULL的情况

为什么?

我的意思是,执行此操作不是错误,但您谈论的是参数检查,而不是实际的功能。简单地记录只有当指针参数是指向指定大小的对象表示的有效指针时才定义函数的行为并不是不合理的。您无法测试所指对象的大小是否正确,也无法测试指针参数是否无效,尽管不是NULL,因此您无法在任何情况下执行完整的参数检查。有了这样的话,将NULL指针作为一个特殊情况来表达它似乎有点奇怪。

虽然我正在使用它,但只要你的目标是C99或更高版本,并且你不需要支持交换巨大的对象,你可以通过在变量上分配临时空间来获得更好的性能 - length数组,而不是在堆上分配它:

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);
}

答案 2 :(得分:0)

您可以将该函数声明为int genericSwap(…)并返回TRUEFALSE(1或0),而不是“残酷的退出”,具体取决于成功。调用者现在可以执行以下操作并决定是以某种方式退出还是恢复:

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