栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > C/C++/C#

一篇文章让你彻底搞懂c语言指针以及变量声明的问题

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

一篇文章让你彻底搞懂c语言指针以及变量声明的问题

c语言初学者很多都对指针懵逼,觉得很难学,但其实上指针是很容易学的,最重要的是和内存地址联系在一起。废话不多说,先说结论:

指针其实是一个值为内存地址的变量!

先看一张图:

其实指针就是上图所示的这点内容。

首先,声明一个变量,就会在内存中开辟空间,上图中声明:
int a = 10;
则就会在内存中开辟一个4字节(int占4字节)大小的空间,同时,内存中,每个内存都有所对应的地址,并且地址是十六进制表示的。上图中假设地址是001(为了方便起见并没有用16进制表示)。

那么问题来了,为什么要用地址?无缘无故弄出个地址有什么用?
举个最简单的例子吧,如果你想要在一个函数中修改从main()函数传来的数值(在这里假设初学者已经学过了函数,毕竟一般来说指针的内容会放在函数后面)。假设函数是:
void change(int a){
a = 4;
}
int main(void){
int a = 2;
change(4);
printf(“%d”,a);
return 0;
}
初学者可能会以为a的值被修改成了4,结果会输出4。但是这其实是不对的,学过函数的应该知道,如果在函数中传递一个数值,那么这个数值是原来值的拷贝。就相当于你有一个克隆体,和你完全一样,但是假如克隆体出现了意外,却不会对你本体产生任何影响。所以复制体产生的影响对本体不造成任何影响。
那么这里如果要修改a的值,有两种方法。
首先是改写一下change函数:
int change(int a){
a = 4;
return a;
}
int main(void){
int a = 2;
a = change(4);
printf(“%d”,a);
return 0;
}
这样利用返回值的形式覆盖原来a的值。

那么第二种方法就是利用指针,我们先讲指针然后再讨论这个问题。

还是这个图,刚才我们说到地址为001的内存空间已经被int类型的值为10的变量a所占用了,那么,既然int类型的值可以通过声明一个int类型的变量来存储在内存中,那么这个变量的内存地址是否也可以通过声明一个特殊的变量来保存呢?答案是可以的,这种变量叫指针变量。

这里有一个及其容易混淆的概念,先给出结论:
指针变量是一个变量,在64位操作系统中,任何一个指针变量,它所占的字节大小固定是8!

那么如何声明指针变量呢?其实很简单,根据上图,声明如下:

**tips:这里很抱歉,作图的时候很匆忙,所以变量名字不对,上图中的*a改成*p,**a改成**p1。

int *p = &a;
&是个取址符,就是取得一个变量所在的内存中的地址,上图中则为001。
*p是个指针变量,它存储一个地址值。

这里,指针变量也是一个变量,所以内存也会为其开辟地址,且变量开辟地址是在茫茫多的内存空间中随机找到一个空闲的地址。在上图中,该地址为004。这个004地址里面的内容是001(即变量a的地址)。

注意,这里可能有初学者作如下声明:
int* a,b;
他想把a和b都声明为指针变量,但这种方法是错误的!因为他可能认为在int变量的声明中,int a,b;可以声明a和b两个整型变量,那么指针也可以这么声明。其实,这种思路确实没问题,但是问题的关键是不存在int*类型的变量。

我们刚才也已经提到过:
指针变量是一个变量,在64位操作系统中,任何一个指针变量,它所占的字节大小固定是8!
因此,指针固定是占8字节大小,不管它内部是存储int、char、double、float等等类型的值,都是占8字节,和它内部存储的值的类型无关!

你可以这样理解,指针类型是一种与int、char、double、float等等类型不同的类型。指针类型占8字节,类似于int类型占4字节,char类型占1字节。指针内部存储的值的类型不是指针的类型!

并且,指针的声明里*应该紧贴变量名,且和类型间距一个空格,即int *a;星号与类型紧贴在一起也是初学者容易弄混的原因。因此,正确的声明应为:
int *a,*b;

那么这里就可以回到上面的问题了,怎么通过指针的形式来修改值,不需要通过返回值的情况?只需要做如下修改即可:

void change(int *a){
a = 4;

}
int main(void){
int a = 2;
change(&a);
printf(“%d”,a);
return 0;
}
那么为什么通过指针就可以修改值了,函数不也是传递它的一个复制吗?

还是这张图,你可以把上面的地址类比于酒店的房间门牌号,修改值就是对房间内部做一定的动作(比如清扫等等,随你怎么想)。有一天,酒店管理人员要求清洁大妈(指针变量*p)把001号房间打扫一下,清洁大妈收到的房间号为001(即指针变量*p内部的值001),知道要去001房间打扫,但是这个时候大妈的女儿打电话来,说今天超市大甩卖,赶紧去抢!大妈二话不说,立马跑回去。这下经理可犯难了,知道客人要来,于是临时叫前台(新的指针变量,姑且就叫他*a吧,这个*a也存储着地址001)来帮忙,前台虽然不情愿但是不敢不听经理的话,于是同样也知道了要打扫的房间号(即指针变量*a内部的值001)。最后,房间成功被打扫完毕,客人很满意。

那么通过这个故事,很好地说明了,虽然传递的是地址的复制,但是只是多一个知道这个房间号001(地址)的人而已,不管是哪个人来打扫,房间内部还是得乖乖被清扫干净(修改值)。

如果有读者学过Java,那么可以与Java中的引用类型相类比(当然这两者不完全一样)。

这里还有一点说明以下:**声明指针变量的时候,最左端的类型根据内存地址内部存储的值来表示。**那这里可能有初学者问了,那如果这个内存地址内部存储的是其它的内存地址,该怎么表示呢?这个很简单,根据不断地寻址,最终找到的有基本类型的值所对应的类型来表示。还是看上面的图,007号地址内部存储004号地址,004号地址内部存储001号地址,001号地址内部存储int类型的值10。最终都能找到对应的值,不管得通过多少个内存地址来间接寻找。

那么这里就引出了一个问题,如果要用一个指针变量来存储一个地址值,该怎么声明呢?

很简单,根据上面的图,可以做如下声明:
int **p1 = &p;
p是一个指针变量,虽然是指针变量,但是编译器肯定也会为其分配地址空间,因此&p的值为004,被存储在007号地址中,这个就叫做指向指针(p)的指针(p1)

那么同理,你也可以再声明一个变量,如:
int *** p2 = &p1;
来存储p1的地址,那这个就是指向指向指针的指针的指针。
四个星号,五个,六个…同理。不过这种并不常见。

接下来是第二部分,变量的声明

int a;
这是一个整型变量

int *a;
这是一个指向整型的指针

int a();
这是一个函数

上面三个很简单

int *a();
那这个是什么意思呢?
如果想知道它是什么意思,就要考虑其优先级。
*优先级要低于(),因此它先是被解释为一个函数,然后被解释为一个指针,因此结合起来就是a函数,然后它的返回值是一个指向int类型的指针。
注意,这里不能说成是一个int类型的指针,因为指针固定只有指针类型,所以这个说法错误,没有int类型的指针(指针本身的类型除了指针类型不存在其它类型,int类型是它指向的值的类型)!

int (*a)();
那这个是什么意思呢?
加了括号,并且括号结合性从左到右,所以左边的(*a)部分先被解释。这是一个指针,然后才被解释成函数,因此结合起来就是,这是一个函数指针,它指向的函数的返回值为int类型。因为函数也被存于内存中的某个位置,所以声明一个函数指针(存储函数的地址)完全是可行的。

int *(*a)();
这又是什么意思?
同理,()优先级最高,所以先解释两个括号部分,这两个部分和上面的同理,是一个函数指针,不过它指向的函数的返回值是一个指向int类型的指针。

int a[];
这是一个数组,没什么好说的

int *a[];
这是什么意思?
同样,运算符不同,先考虑优先级再考虑结合性。间接运算符(*)优先级要低于[],因此它先被解释为一个数组,然后被解释为指针,因此,这个表达式的意思是:这是一个数组,数组里的每个元素是指向int类型的指针。

int a()[];
这是什么意思?
你可能认为,优先级相同,结合性从左到右,应该是先被解释成函数,然后再被解释成数组,所以a应该是一个函数,它的返回值是int类型的数组。
但是,但是!注意,不同于Java的方法,c语言函数的返回值不能是数组!(其实可以通过声明全局的数组来直接操作数组的方式营造出类似于返回值的情形)

int a[]();
这是什么意思?
你也许和上面一样,以为它先被解释为数组,再被解释为返回值为int类型的函数,然后其元素类型为返回值为int的函数。但这里就和Java一样了,不能在数组里存函数!!!

int (a[])();
如果你觉得上面的都没有难度不妨试试这个,首先先解释第一个括号里的,第一个括号里被解释成一个数组,数组元素为指向某种类型的指针。然后再考虑外边,外边是一个函数,它的返回值为int类型。因此
*,a是一个数组,数组元素是指向返回值为int类型的函数的指针。**

int *(*a[])();
这个表达式仅仅多了一个间接访问操作符,因此,a是一个指针数组,数组元素是指向返回值为int类型的函数的指针。

其实指针还有其它的作用,个人觉得最好用的地方就是某些情况下可以省略函数的返回值,同时可以充当全局变量来使用。碍于篇幅原因,就没有一一查询作用,不过读完这篇文章,相信读者对于指针的原理有了最基本的了解之后,再去看书探索应该没什么问题了。

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1038287.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号