当前位置: C语言 -- 专题 -- void类型

void类型

根据ISO/IEC 9899:2018标准第6.2.5 Types节,void类型包含一组空值,是一种不完整对象类型(incomplete object type)。本文主要讨论void类型在表达式、指针、函数中的应用。


一、表达式

任何表达式被评估为void类型表达式,其值或者指示符将被丢弃。void类型表达式评估的意义在于其副作用,例如:逗号运算符的左操作数、调用没有返回值的函数等。

a = 5, b = 6;

其中左操作数a = 5认为是一个void类型表达式。


assert(i < 0);    // void类型表达式。
rewind(pFile);    // void类型表达式。

其中宏assertvoid类型的函数式宏,函数rewind为返回类型为void类型的函数。


(void)类型转换用于表达式时可以显式地表示丢弃表达式的值,例如:

(void)printf("Hello World!\n");

上述语句中printf函数的返回值为13,使用(void)类型转换可以显式地告诉编译器丢弃这个返回值。


此外ISO/IEC 9899:2018标准第6.3.2.2 void节还规定:不能以任何方式使用void类型表达式的值,并且不能对此类表达式使用隐式类型转换或者显式类型转换(void除外)。


二、指针

void类型指针也称为通用指针。void类型指针可以转换为指向任何对象类型的指针;指向对象类型的指针也可以转换为void类型指针。指向对象类型的指针转换为void类型指针,再从void类型指针转换为指向对象类型的指针,结果应与原始指针相等。void类型指针未与任何数据类型相关联,可以保存任何对象类型的地址。

int a = 3;
float b = 3.14f;
void *ptr = NULL;

ptr = &a;    //void类型指针保存int类型对象地址。
ptr = &b;    //void类型指针保存float类型对象地址。

其中void类型指针ptr既能保存int类型对象的地址,也能保存float类型对象的地址;这与int类型指针只能保存int类型对象的地址、float类型指针只能保存float类型对象的地址不同。


void类型指针用于函数声明时,如果用于函数的返回类型,则表示返回一个通用指针,例如:内存分配函数;

void *calloc(size_t nmemb, size_t size);
void *malloc(size_t size);
void *realloc(void *ptr, size_t size);

上述例子分配的内存适用于各种数据。


如果void类型指针用于函数参数,则表示函数可以使用各种类型的指针参数,例如:qsort函数中的比较函数。qsort函数的函数原型为:

void qsort(void *base, size_t nmemb, size_t size,
     int (*compar)(const void *, const void *));

应用范例:

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
/*void类型指针应用范例*/

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

/*处理数据类型的宏。*/
#define TYPE(x) _Generic((x),  \
                         float: (float *)x,  \
                         default:(int *)x  \
                         )

int funcCompare(const void *x, const void *y)
{
    if(*TYPE(x) > *TYPE(y))
        return 1;
    else if(*TYPE(x) < *TYPE(y))
        return -1;
    else
        return 0;
}

int main(void)
{
    int arrInt[] = {10, -5, 0, 25, 3};
    float arrFloat[] = {3.14f, 7.26f, 10.33f, 88.88f, 96.32f, 47.25f};

    qsort(arrInt, sizeof(arrInt)/sizeof(int), sizeof(int), funcCompare);
    puts("The order of arrInt:");
    for(int i=0; i<sizeof(arrInt)/sizeof(int); ++i)
        printf("%d ", arrInt[i]);
    putchar('\n');

    qsort(arrFloat, sizeof(arrFloat)/sizeof(float), sizeof(float), funcCompare);
    puts("The order of arrFloat:");
    for(int i=0; i<sizeof(arrFloat)/sizeof(float); ++i)
        printf("%.2f ", arrFloat[i]);

    return 0;
}

上述代码将输出:

The order of arrInt:

-5 0 3 10 25

The order of arrFloat:

3.14 7.26 10.33 47.25 88.88 96.32

在这个例子中,分别使用int类型的指针参数和float类型的指针参数作为比较函数的参数。


使用void类型指针有两点需要注意:

1ISO/IEC 9899:2018标准禁止使用void类型指针进行算术运算,详细请参阅标准第6.5.6 Additive operators节,标准要求算术运算的指针应为指向完整对象类型的指针。实现中一些编译器可能允许void类型指针进行算术运算。


2void类型指针无法解除引用。

float a = 3.14f;
void *ptr = NULL;

ptr = &a;
printf("%.2f\n", *ptr);    //编译时会出错。

上述代码编译时,编译器会给出类似error: invalid use of void expression的出错信息,因为编译器不知道void类型指针指向对象的实际类型。

如果使用下述语句一切都正常了。

printf("%.2f\n", *(float *)ptr);

 

三、函数

void类型用于函数声明时,如果用于函数的返回类型,则表示函数没有返回值,例如:setbufrewindclearerr等函数,其函数原型分别如下所示:

void setbuf(FILE * restrict stream, char * restrict buf);
void rewind(FILE *stream);
void clearerr(FILE *stream);

如果void类型用于函数参数,则表示该函数没有参数,例如:randabortclock等函数,其函数原型分别如下所示:

int rand(void);
_Noreturn void abort(void);
clock_t clock(void);

应用范例:

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
/*void类型在函数中应用范例*/

#include <stdio.h>

void printMessage(const char *str)
{
    printf("%s\n", str);
}

void printHello(void)
{
    printf("Hello World!\n");
}

int main(void)
{
    const char arr[] = "Quality matters more than quantity.";

    printMessage(arr);
    printHello();

    return 0;
}

上述代码将输出:

Quality matters more than quantity.

Hello World!

其中printMessage函数是没有返回值的函数;printHello函数是既没有参数,也没有返回值的函数。