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 */
我是 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 */