C:带有空指针的可变参数函数

C: variadic functions with void pointers

我是 C 的新手,正在尝试创建一个接受 void 指针的可变参数函数。但是,我遇到了一些我不太理解的意外行为...

token.h

typedef struct {
    int (*funcOne) (void*, ...);
    ...
} symbols_t;

main.c

typedef struct {
    char* name;
} job_t;

int job_create(void* objPtr, ...) {
    char* name;
    int i = 0;

    va_list args;
    va_start(args, objPtr);

    while(&objPtr[i]) {
        void* arg = va_arg(args, void*);
        if (arg == NULL) {
            break;
        }

        printf("arg %u: %s\n\n", i, arg);
        i++;
    }
    va_end(args);

    return EXIT_SUCCESS;
}

int main(void) {
    symbols_t symbolTable;
    symbolTable.funcOne = *job_create;

    job_t myJob;

    // See output one
    symbolTable.funcOne(&myJob, "hey1", "hey2", "hey3", "hey4", "hey5", "hey6", "hey7", "hey8);

    // See output two
    symbolTable.funcOne(&myJob, "hey1", "hey2");

    // See output three
    symbolTable.funcOne(&myJob, "hey1");

    // See output four
    symbolTable.funcOne(&myJob);

    return EXIT_SUCCESS;
}

输出一:

输出二:

输出三:

输出四:

如果能深入了解这里发生的事情,我们将不胜感激!

您的代码查找 va_arg 到 return NULL 以退出循环,只有当您实际显式传递 NULL 作为参数时才会发生这种情况.编译器不会神奇地添加它。因此,在 运行 通过实际传递的参数后,循环继续读取不存在的参数,这是未定义的行为(可能的结果是您读取未使用的寄存器或内存中的任何垃圾)。

所以你的电话应该看起来更像

/* still not quite right, see below */
symbolTable.funcOne(&myJob, "hey1", "hey2", NULL);
symbolTable.funcOne(&myJob, NULL); /* pass no strings */

但是,有几点可以使您的程序更正确:字符串文字的类型为 char *,而不是 void *,因此实际传递的类型与您请求的类型不匹配来自 va_arg。通常,这会导致未定义的行为。您的 job_create 函数应该改为 char * arg = va_arg(args, char*);.

那么,将 NULL 作为最后一个参数传递是不太正确的。允许将宏 NULL 定义为 (void *)0 或简单地 0。这两种情况都会导致相同的不匹配:您的 va_arg 现在期望 char *,但传递的是 void *int。后一种情况在 int 小于指针的机器上特别危险。

您需要传递一个char *类型的空指针。所以调用这个函数的正确方法是:

symbolTable.funcOne(&myJob, "hey1", "hey2", (char *)NULL);
symbolTable.funcOne(&myJob, (char *)NULL); /* pass no strings */