GNU C中对void指针和函数指针的算术操作支持

来源:互联网 发布:传奇霸业端源码 编辑:程序博客网 时间:2024/05/20 23:55

原文地址:http://blog.sina.com.cn/s/blog_48c95a190101ai3i.html#commonComment

在近日的项目开发中,本博主遇到了一个之前从没想过的问题,那就是在C语言中一个声明为void *类型的变量是否可以进行算术操作?为了验证该问题,俺写了一个测试程序,在测试程序中对一个void *类型的变量进行了算术操作加一,代码如下。

#include <stdio.h>
int main() {
        void *test = NULL;
        int int_value[] = {12, 34, 56, 67, 27};
        char char_value[] = {'H', 'e', 'l', 'l', 'o'};

        test = (void *)char_value;
        printf("%c\n", *(char_value + 1));
        printf("%c\n", *((char *)(test + 1)));

        test = (void *)int_value;
        printf("%d\n", *(int_value + 1));
        printf("%d\n", *((int *)(test + 1)));
        return 0;
}
    使用GCC对该程序编译,编译结果是顺利通过,而且没有任务Warning,由此可知至少在语法上来看是完全可以通过的。这也是完全说得通的,学过C语言的人都知道指针本来就是可以加减的,伴随着加减指针对自动在内存空间中前后移动,所以编译通过至少在语法上也是顺理成章的。那么执行结果如何呢?执行结果如下:
e
e
34
570425344
    从以上的执行结果可以看到一点不同,那就是当void *变量指向char *变量时,加一之后的打印结果是正确的,但是当void *变量指向int *变量时,加一之后的打印结果确实不正确的,这是为何呢?此时我们就得抛开语法的合理性来探寻更深层次的原因,也就是当指针加一时究竟发生了什么?当char *类型的指针加一时,代表的意义是将指针移动到下一个char类型数据的内存起始位置,因为char的大小为1一个字节,所以char *类型的指针加一时,指针要向后移动1个字节。当int *类型的指针加一时,代表的意义是将指针移动到下一个int类型数据的内存起始位置。但因为int的大小为4个字节,所以int *类型的指针加一时,指针要向后移动4个字节。依此类推,当void *类型的指针加一时,代表的意义是将指针移动到下一个void类型数据的内存起始位置,但是void类型的大小是多少呢?众所周知,在C语言中不能声明一个类型为void的变量,那么void的大小是否就是0呢?不然,通过打印sizeof(void)可以知道,void的大小竟然是1,这也就解释了为什么当void *变量指向char *变量时,加一之后的打印结果是正确的,因为void的大小和char一样的!
   通过以上分析可知,GNU C中在语法上支持void指针的算术操作,但在执行过程中,void指针的算术操作可能会导致错误。关于这一点我们可以在GNU的说明文档上找到官方支持In GNU C, addition and subtraction operations are supported on pointers to void and on pointers to functions. This is done by treating the size of a void or of a function as 1. A consequence of this is that sizeof is also allowed on void and on function types, and returns 1. The option -Wpointer-arith requests a warning if these extensions are used(链接地址:http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Pointer-Arith.html#Pointer-Arith)。也就是说不光是void指针,就连函数指针也是支持加减操作的,而void的大小和函数的大小都是1。为了理论联系实际,俺又写了一个测试程序来验证上面的说明,代码如下。
#include <stdio.h>
int echo(char *string) {
        printf("%s", string);
}
int main() {
        printf("%d\n", sizeof(echo));
        printf("%d\n", sizeof(void));

        return 0;
}
    执行结果证明了以上说明的正确性,void的大小和函数的大小确实都是1。当然,上面的说明也同时给出了避免因void指针和函数指针加减导致执行错误的方法,那就是在编译时添加编译选项:-Wpointer-arith。俺使用该编译选项对第一个测试程序重新进行编译:gcc -Wpointer-arith test.c,而此时编译结果就不再是顺利通过而是给出了Warning信息如下。
test.c: In function 'main':
test.c:10: warning: pointer of type 'void *' used in arithmetic
test.c:14: warning: pointer of type 'void *' used in arithmetic
0 0
原创粉丝点击