在 C 语言里,void 不是一个“类型名”,而是一个“类型缺省符”。它告诉编译器:此处既不需要返回值,也不打算接收参数,甚至指针所指的对象类型暂时未知。用 void 标注的函数、指针或表达式,编译器不会为其生成对应类型的内存布局,从而避免不必要的类型检查与转换。
void 修饰函数返回值
当函数只需完成动作而不产生结果时,把返回类型写成 void。调用者不能也不应尝试接收返回值,否则编译器直接报错。这样既能提醒设计者“此函数无副作用”,也能让编译器做更激进的优化。
1 /* 只做打印,不返回任何值 */
2 void welcome(void)
3 {
4 printf("Hello, void!\n");
5 /* 这里写 return; 可以,但写 return 值; 会报错 */
6 }
void 修饰函数参数列表
在 ANSI C 中,int func(void) 明确表示“本函数不接受任何参数”;而旧式 int func() 则代表“参数信息未知”。养成写 void 的习惯,可让编译器帮你挡住误传实参的调用,降低调试成本。
1 /* 明确拒绝参数 */
2 int getRandom(void)
3 {
4 return rand() % 100;
5 }
void* 万能指针
void* 可以指向任意数据对象,但编译器不允许直接对其解引用,因为缺少类型与大小信息。必须先强制转换回具体类型,才能读写目标内容。标准内存函数 malloc、free、memcpy 都依赖这一机制,实现“类型无关”的内存管理。
void* 与内存分配
malloc 返回 void*,用户按需强转为目标指针类型。忘记转换在 C 里不会出错,但加上强制能让编译器做类型检查,同时提醒阅读代码的人“此处正在申请某种结构体内存”。
1 int *arr = (int*)malloc(10 * sizeof(int)); /* 申请 10 个 int */
2 if (!arr) { perror("malloc"); exit(EXIT_FAILURE); }
3 free(arr); /* 释放时仍用 void* 内部机制 */
void* 与回调接口
通用库常把回调参数声明为 void*,调用者把任意结构体地址传进去,再在回调内部转回实际类型,实现“一份库代码,适配多种用户数据”。
1 typedef void (*Task)(void *ctx);
2 void run(Task f, void *ctx) { f(ctx); }
3 /* 用户侧 */
4 typedef struct { int a,b; } Pair;
5 void printPair(void *p)
6 {
7 Pair x = (Pair)p;
8 printf("%d %d\n", x->a, x->b);
9 }
10 int main(void)
11 {
12 Pair t = {3,4};
13 run(printPair, &t);
14 return 0;
15 }
void 与指针算术的禁区
void* 没有具体大小,因此执行 p++ 或 p[i] 会直接报错。若需偏移,应强转为 char* 后再运算,因为 char 大小被定义为 1,偏移量即字节数。
1 void *raw = malloc(16);
2 /* raw += 4; 错误! */
3 char *tmp = (char*)raw;
4 tmp += 4; /* 正确,向后移 4 字节 */
常见误区速查
1. 把 void 当成“返回 0”——实际上函数体内根本没有返回值。2. 用 void* 接收后长期不转换——解引用前必须恢复真实类型。3. 误以为 void* 会自动记住大小——内存管理全靠程序员记录。
运行结果示例
Hello, void!
Random: 42
3 4