C语言难点之复杂声明与定义

看到这个标题,你可能觉得C语言的声明与定义有什么难点呢?下面来看一个式子:

  ( * (int * *( * ) (int * *,int * *))0)( int * *,int * *);

怎么样?是不是有点小迷糊,下面来仔细分析一下这个东西。

定义和声明的区别

首先来看定义和声明的区别,我觉得很多人都很糊涂,包括我实验室的老师,他编的程序连定义和声明都弄错了。例子如下,比如在一个工程文件中,你有许多的.c文件,你想要定义全局变量,实现所有文件的共享。比如a.c和b.c,定义了两个头文件a.h和b.h,在两个头文件中都有int c;的表达式,然后编译链接都不报错(VC6.0),用起来还挺好。你觉得对吗?
这其实是大错特错的,因为这相当于对于变量进行了两次定义,为什么没有报错呢?因为这是编译器在编译和链接的时候做出了一项抉择,首先是对于a.c和b.c生成目标文件(.o)文件,此时不会发现什么问题,因为都定义可一个全局变量而已。当进行链接时就会发现同名的全局变量,此时对于未初始化的全局变量,编译器都会认为是弱符号,会选取占据内存空间最大的变量予以保存。但如果给任何一个a.h或者b.h的c赋予初值不为0,编译器就会检查出这个错误。(不知道说明白没有,具体参见程序员的自我修养:链接、装载与库)
造成这个问题的原因就是声明与定义的区分没有弄清楚。其实它俩之间的本质区别就是是否分配内存,分配内存叫定义,不分配内存叫声明。那前面的例子的问题该如何解决呢?就是在a.c内定义int c;在a.h中声明 extern int c;b.c包含a.h的头文件。这才是真的全局变量!

复杂声明与定义

先来看几个简单的

int * f,g;
int * f();

这里就是定义了一个整型指针变量f和整型g,注意int *中的 *只是对f起作用,这也是一个易错点。第二个就是定义了一个函数,返回值为指向整型的指针。

int*f)();
int *(*f)();

第一个就是声明了一个函数指针,两对括号的含义不同,第一个是起到将*号先作用的聚合作用。第二个括号只是函数调用。
第二个声明了一个返回值为指向整型的指针的函数指针,有了前面的分析不难理解。

根据这几个稍微复杂一点的声明,其实我们就可以总结出对于理解复杂声明和定义的方法。首先就是找到定义的核心符号。比如int (*f)();它的核心就是f。接下来就是找结合,因为括号的作用, * 操作符先起作用,所以是一个指针。指针的类型是什么呢?在往外扩展,是一个括号,代表函数调用,而且这个函数返回值为int,所以可以得出这是一个函数指针,指向的函数是返回整型的函数。其实在复杂的声明都可以利用这个方法来分析了。

int f[];   //数组
int (*f[])(); //函数指针数组
int *(*g[])(int,float);//函数指针数组

用这个方法来理解指针数组与数组指针就相当容易了,再也不用死记硬背,还记不住了。

int (*a)[20];
int *b[20];

自己先来试试看!

第一个声明可以看到核心符号是a,然后找结合有 符号*,括号,[20],这时候就需要优先级的知识了,括号的优先级最高,所以a先与 结合,说明a是一个指针,指向什么元素呢?int [20],是一个20个整型的数组,所以这是一个数组指针的定义,定义了一个指向具有20个整型元素的数组。
第二个声明就是b,优先级[]是高于
的,所以b是一个数组,数组的元素是int *类型,是指向int 型的指针,所以b是一个指针数组,b是数组,数组中的元素是指针,指针的类型是指向int型的指针。

最复杂声明的揭晓

根据前面的分析相信你可以分析出开始那个复杂的声明了。

  ( * (int * *( * ) (int * *,int * *))0)( int * *,int * *);

找到核心符号 0 ,首先与(int * *( * ) (int * *,int * *))结合,这是个啥,分析一下这是个函数指针,指向了输入参数都为int * ,返回值为int **的函数,将0转换为了这样的函数指针,之后与最前面的结合,可以将(int * *( * ) (int * *,int * *))0简化一下为a,这样表达式变成

( * a)( int * *,int * *);

对于 *a就是取出0这个地址所指向的内容,因为是函数指针,就是相当于调用了函数,在加上后面的输入的参数。这下可以理解了吧。

欢迎关注我,一起成长,一起玩编程,回复C语言获取我的C语言学习资料。
在这里插入图片描述

发布了24 篇原创文章 · 获赞 3 · 访问量 930

猜你喜欢

转载自blog.csdn.net/HIT_zhanmusi/article/details/105498462