理解C语言中指针的声明以及复杂声明的语法

昨天刚把《C程序设计语言》中“指针与数组”章节读完,最终把心中的疑惑彻底解开了。如今记录下我对指针声明的理解。顺便说下怎样在C语言中创建复杂声明以及读懂复杂声明

本文章中的内容參考自《C程序设计语言》

指针是什么就不具体说明了,用一句话来总结就是:“指针是一种保存变量地址的变量”。

1.声明简单的指针变量


先看看代码:

int i = 1;
int *p; //声明一个指向int类型数据的指针变量 p
p = &i; //&为取地址符,把变量i的地址赋值给指针 p
*p = 2; //此时 i 的值变成2了

这段代码声明了一个指针变量p。并把它指向变量i,通过*p能够訪问到变量i,并对i的值进行改动。如今对星号 * 的作用进行具体的解说。* 是一元运算符,它用在不同的地方将具有不同的作用。

1.1 星号 * 用于声明语句时的作用


上面的代码片段中的第2行代码“int *p;”就是星号 * 用于声明语句时的情况。对于指针的声明。我们首先要从 p 这里開始看起,这是C语言中“声明”的语法。以下会介绍到。

第一步:先看p的右边有没有其他符号(分号不算)。能够看到p的右边并没有符号。

第二步:看p的左边,在p的左边有一个星号 * 。关键的时候到了!在这里能够看到星号 * 作用于p上面。其产生的效果是:“声明变量 p 是一个指针 ”。

眼下为止。我们已经知道 p 是一个指针了,但还不知道该指针是指向什么类型数据的。

第三步:看星号 * 右边的类型说明符 int,在这里int的作用就是声明 p 指向的数据类型是int。至此。“int *p;”这句代码就全然解释完了。

注意。上面的 int 、* 这两个符号发挥作用是有先后的,并非组合在一起来发挥作用的。

先是 * 发挥作用:声明p是一个指针变量。然后是int发挥作用:声明p指向的数据类型是整型数据

1.2 星号 * 用于声明语句之外时作用

以上代码片段中,最后一句“ *p = 2; ”就是星号 * 用于声明语句之外时的作用。这句语句等效于“ i = 2;”。

用专业点的说法就是:假设指针p指向整形变量 i。那么在 i 出现的不论什么上下文中都能够使用 *p ,当 * 作用于指针变量p时,就是訪问指针变量p所指向的变量 i。

至此。已经说了怎样声明简单的指针变量,是不是认为非常easy呢?接下来将会依照复杂程度递增的顺序来介绍和指针有关的复杂声明

2.复杂声明和声明语法

2.1声明指向指针的指针变量

先看代码:

int i = 1;
int *p = &i; //声明指针变量p。p指向变量i
int **pp; //声明指针变量pp
pp = &p; //pp指向变量p,p是指针变量
**pp = 2; //此时 i 的值变成 2 了
//*(*pp) = 2;这个语句等效于上面的语句

代码段中的1、2行在上面已经介绍过了。如今主要介绍第2行之后的代码。

注意,这以下这两句是等效的,这是由于相似于 * 和++这种一元运算符遵循从右至左的结合顺序。

int **pp;   //声明方式1
int *(*pp); //声明方式2

如今開始解释声明方式2(和声明方式1是一样),同上面的简单声明一样。我们再次从变量名pp開始。

第一步:先看看pp右边有没有符号,能够看到pp的右边有一个右括号,而括号仅仅是强调结合顺序,所以不用管它。

第二步:看pp的左边,能够看到,pp的左边有一个星号 * (从右数起第一个星号),该星号的作用是:“声明变量pp是一个指针。此时,我们还不知道指针pp所指向的数据类型。并且把括号里面的 * 和 pp 两个符号都解释完了。如今看看括号外面的符号。

第三步:先看括号外面的右边,能够看到,括号外面的右边并没有符号。

第四步:看括号左边,这时。我们看到括号外面的左边有一个星号 * (从右数起。第二个星号 *),这个星号 * 的作用是:“声明指针变量pp所指向的数据类型是指针类型”。此时。我们已经知道了pp指向的数据类型是指针类型(即是代码片段中 p 的类型),但还不知道所指向的指针是指向什么类型数据的。比如,pp指向指针p,但却不知道p指向的是什么类型的数据。

第五步:看最左边的符号“int”,这个int的作用就是:声明pp所指向的指针所指向的数据类型是整型int。比如,pp指向指针p,如今我们知道了p指向的数据类型是int整型。

哈哈。是不是有点晕了,如今简单总结下:pp右边第一个星号 * 声明了pp是一个指针变量,第二个星号 * 声明了指针变量pp所指向的数据类型是指针类型,而类型说明符int则声明了pp所指向的指针指向的数据类型是int

看到这里。你可能会疑惑:一定要依照上面的步骤来解释声明吗?这是套路吗?

答:是的。

由于这是C语言中”声明“的语法,下一小节将会对该语法进行介绍。

之所以这么迟才介绍这个语法,是由于在用过后,会更easy理解它。

2.2 C语言中”声明“的语法

在这里。将会对声明的语法进行解说,为后面理解更加复杂的声明打基础。

在C语言中解释一个声明,要先从被声明的变量名開始解释。

并非从左到右,也不是从右到左解释,而是从中间開始。更准确来讲,是从被声明的变量名開始解释。

而声明总是由非常多符号和唯一的变量名相结合。这些符号和唯一的变量名结合就是声明符。

声明的形式为

”T D“的形式,当中 T代表类型,D代表声明符。

举个样例吧:

int *p;

int就是T,而*p就是D。

以下将以变量名p来对声明中用到的符号进行解释。

变量名p能够和非常多符号来结合成声明符,如[ ],(),*。

1.当p与符号 [ ] 结合时(结合在p的右边),符号 [ ]的作用就是声明变量p是一个数组类型[ ]里面的数字则决定了数组中元素的个数。如以下的声明代码:

int p[5];//声明变量p是一个整型数组。数组中有5个元素

须要注意的是,符号[ ]的优先级是比星号 * 的优先级高的。

2.当p与符号 ( )结合时(结合在p的右边)。符号( )的作用就是:声明p是一个函数。通过p()能够调用该函数,()中能够有參数列表或者无參数列表,如以下代码:

int p();//声明一个函数p,返回值由前面的int决定
int p(int a,int b); //声明一个带參数的函数p

相同。符号( )的优先级比星号 * 高。

3.当p与星号 * 相结合时(结合在左边),符号 * 的作用就是声明p是一个指针类型 。例如以下代码 :

int *p; //声明p是一个指针,该指针指向int类型数据

介绍完这三个符号后。能够继续介绍语法(套路)了。

在解释声明时,首先要决定声明的名字p是什么东西,而和p近期的符号则决定了p是什么东西。如以下声明:

int p();    //p是函数
int *p; //p是指针
int p[5]; //p是数组
int *p[5]; //p是数组。里面的5个元素为指向int类型的指针

这里的第4行代码要解释一下,由于[ ]的优先级比星号 * 高,所以[ ]先作用于p,故p是一个数组,如今我们知道p是一个数组了,但还不知道数组中的元素是什么类型,这时,就要看左边的星号 * ,星号 * 声明了数组中的元素是指针类型。

如今,我们知道了数组中的元素是指针类型,但还不知道那些指针是指向什么类型的,这时int发挥作用了。它声明了里面的指针是指向int类型数据的,所以这个声明的结果就是:声明了一个数组 ,数组有5个元素。每一个元素都是指向整型数据的指针

到这里,我们对这个语法(套路)有点头绪了!

原来解释声明就是要先从名字p開始,然后从p的右边開始看符号(由于优先级高的符号 [ ]和 ( ) 是放在右边的)。假设有符号,刚和p先结合。看完右边的符号(假设有的话)后,就决定了p是什么。这时。就到p左边的符号发挥作用了(左边要么是 * 。要么就什么都没有)。

最后发挥作用是则是类型说明符(由于它在最外面)。

总的来说。解释声明的套路就是不断问什么,然后从里往外看符号来解答什么的过程。说过无谓。再来看一段代码:

int *p();   //声明1,声明一个函数p,返回类型为指针类型
int (*p)(); //声明2,声明一个函数指针p,p指向一个函数

我们来依照套路来解释这两个声明

声明1:首先,要确定p是什么?从p右边的符号 ( ) 能够解得,p是一个函数。新的问题又来了,那函数p的返回值是什么类型?从p左边的星号 * 能够解得,函数p的返回值类型是指针类型。新的问题又来了,返回的指针指向的数据类型是什么啊?这时。依照套路,应该继续看外一层的右边(由于看符号的顺序为p的右左右左…,并且内层优先于外层),然而符号 ( ) 的右边没有符号了。转而看向外一层的左边。

这时发现外一层的左边是类型说明符int。因此解得,返回的指针指向的数据类型是int。整个声明就是:声明p为一个函数,函数的參数为空。函数的返回值为指针,指针指向的数据类型为int。

声明2:由于这个声明中,有个括号把星号 * 和 p包住了,所以 * 和 p属于内层。而括号里p的右边没有符号。故 * 和p先结合,则声明了p是一个指针。这回答了p是什么。新的问题来了,p指向的是什么?这时。内一层的符号看完了,继续看外一层的右边,外一层的右边是符号( ) ,则解得p指向的是一个函数。然后再看外一层的左边得。该函数的返回值是int类型。整个声明就是:声明p是一个指针。这个指针指向一个函数。这个函数的返回值为int

总结一下,解释声明的套路就是:先从最内层開始看符号。先从名字p的右边開始看,再到左边。

然后跳到外一层的右边開始,再到外一层的左边開始看。不断循环。直到没符号为止。

以下把这个套路应用到更加复杂的声明中去。

如以下这个声明:

char (*(*x())[])();

有没有头晕的感觉?不用怕,依照套路来非常easy的。

首先从最里面開始,x的右边是符号 ( ) ,所以x是一个函数。

再看x的左边,x的左边是一个星号 * ,所以 函数x的返回值类型为指针类型。继续跳到外一层的右边,发现外一层的右边是符号 [ ] 。所以指针指向的是一个数组。继续看左边,发现左边是一个星号 * ,所以数组中的元素的类型为指针类型。再看外一层的右边,发现符号 ( ),所以数组中的指针指向的是函数。最后看左边的类型说明符char,所以函数的返回值类型为char类型。整个声明就是:x是一个函数。函数的返回值类型是指针类型,这个指针指向的是一个数组,这个数组是指针数组(里面的元素是指针),数组中的指针是函数指针(指针指向函数),指针指向的函数的返回值类型是char类型。

再看这个声明:

char (*(*x[3])())[5];

该声明的结果为:x是一个数组。数组有3个元素。元素的类型为指针类型。这些指针都是函数指针,所指向的函数的返回值为指针 ,返回的指针指向数组,指向的数组有5个元素,元素的类型为char类型。

到此为止。我们已经学会了复杂声明的语法(套路)。这时我们也能够利用这个语法来声明我们想要的变量。

2.3 使用复杂声明

如今我们来声明一些复杂的声明。

比方,我们来声明一个函数x。函数x的返回值为指针类型,该指针指向一个数组。数组的元素类型为char

仅仅要按套路反过来即可了:

1. 函数x:

x()
  1. 函数x的返回值为指针类型: 。
*x()

3.该指针指向一个数组:

(*x())[]

4.数组的元素类型为char:

char (*x())[];

搞定!是不是非常easy?

这篇文章到这里就结束了。这是我的第一篇博客,假设有什么错误,请在评论中回复。

希望这篇文章能帮助到大家更好地理解C语言中的声明的语法。

上一篇:canvas 画布 文字描边


下一篇:Entity Framework 基础