C语言指针与变量的关系
最近在学C语言,发现书本中有关指针操作的解释说明有些绕,所以就打算将自己的理解写下来,方便日后回顾。
总结
以下所有的地址,指针,空间,所指的多是内存中的空间,下面用了多个名词来表示,主要是为了表述清晰。
同时,阅读本文最需要明确的就是操作地址的概念,还有地址和地址值的区别。
操作一个地址,无非就是取值或赋值
使用一个变量名,就是直接操作该变量名的地址。
地址值,就是空间首地址在内存中的具体编号数值,以16进制展示
*(一个地址值),就相当于是直接操作该地址值对应的地址。
而操作该地址,又回到了取值 | 赋值的判断
变量的定义与赋值
讲到指针之前,需要先说明一下C语言中,定义一个变量都发生了什么:
执行一下代码时:
int a;//在这里,定义了一个int类型的变量,名为a
a = 1;//对a进行赋值操作
int b=a;
b = 2;
- 1
- 2
- 3
- 4
定义
在C语言中,定义一个变量时,会根据变量类型在内存中应占据的字节长度,为该变量开辟一个连续的空间。
代码中a的类型为int,而int类型的数据,在不同的编译器下,应分配的空间会有区别,这里默认int占据4个字节。
可以这么理解 int a;
在内存中开辟一块空间,空间的内容限定为int,并为空间的首地址起个别名为a
赋值
后续的a=1;
实际上就是操作首地址为2000空间的内容。
也就是将1放入对应空间2000中
int b=a;
- 1
当执行到此语句时,C会再为b开辟一个连续空间,并将空间首地址对应给b,并取得a空间中的值,放入给b的空间。
可以理解为 将a对应空间的内容 放进 b对应空间中。
所以此时b == a;只是a空间内容 == b空间内容,并非a空间==b空间。
此时再执行:
b = 2;
- 1
实际上就是操作b 对应的空间 2004;将其中的内容由 1 替换为 2;
而上文所提到的所谓的空间,在C语言中也称为指针(地址);
注意,当a的空间内容未初始化时,是无法将其内容传递给b空间的
指针变量的定义与赋值
int *c,*d;
c = &a;
d = c;
*c = b;
- 1
- 2
- 3
- 4
符号的说明
指针类型符号 :*
int * c;
//可以写成: int * c;
//或 int *c;
//由于C中可以连续定义变量,如 int x,y;
//若此时想要将x定义为指针类型,则有int *x,y; *紧贴x,避免歧义;
//若写成int* x,y;容易让人误会为 x和y的类型都是int *;
- 1
- 2
- 3
- 4
- 5
- 6
可以这么理解:
* 可以理解为类型,但是较于int,char等类型,又弱一级 , * 就是指针类型,但只能限制紧跟其后的变量名为指针类型,同时还要有如int等类型来限制之,如果要连续定义两个指针类型:
int *x,*y;//不能写成int* x,y;
- 1
地址操作符号:&
在变量的定义中,已经说明了,直接使用 变量名时 ,实际上就是直接在使用其对应地址空间的内容,所以出现以下情况时,是不可取的:
int *x = a;
- 1
现在限制了x的空间为 int指针,而直接使用a时,用的是它空间中的内容:1;
显然是错误的。
C中提供了 & 符号,就是为了区分空间内容和空间,直接使用 a ,用的是空间内容,而 &a,则是用的空间值,也就是 a所对应的 2000。
int x = &a;// x=(a=>2000)=2000
- 1
指针操作符号:*
指针操作符号和指针定义符号都是 * ,可以根据 *前有无实质类型来判断该符号的作用。
已知直接使用变量名,操作的就是变量地址,此时如果执行:
c=&a;
- 1
实际上操作的就是c对应的空间2008中的内容,使空间 2008 中的内容变更为 2000
执行:
int *d = c;
- 1
时,实际上也是直接操作c对应空间 2008 中的内容 2000,将2000 取出并赋值给d的空间内容。
所以当
那如何操作指针变量 c对应的2008 空间中的内容 2000 中的内容呢?
当 * 为指针操作符号时(非定义符号),就是将变量对应空间中的值取出来。
此时的 *c ,就是2000这块空间,*c等价于a;
直接操作a,就是直接操作a对应的 2000 空间,此时用 *c取出2000,再直接操作,实质上也就和直接操作a的作用是一样的。
小结
*和&在操作变量时,可以互相抵消,&一个变量,取的是变量地址,*一个变量,取的是变量对应空间的内容
如:
*&a = 5;
- 1
则可以理解为:
&a:把a的地址作为值提取,*(&a)等于*2000:操作a地址的内容
而*c ,*一个指针变量的流程则是:
直接使用c时,得到的就是c的空间内容2000,就像直接用a,用的是a里的内容2;
所以直接*c,就等于* (c的内容2000),*c = *2000
所以可以理解 * ,是用于操作地址值的,*一个地址,就是操作改地址的内容。
代码解释:
指针的定义
int* c 过程为:
在内存中开辟一块空间,此空间的内容只能为指针,且该指针对应的空间的内容为int,并为此时所开辟的空间的首地址起个别名 c
如:a所对应的空间是2000,这个2000就是指针,同时2000中的内容被限制为了int,所以此时可以将2000赋值给 c:
指针的赋值
c = &a;
- 1
c现在被定义成了 指针类型(*),只能存放指针(地址),并且该地址只能存放int值。
而&a,则是直接使用a的地址值,而非空间内容,所以现在c对应的空间2008 中的内容,就成了a的地址值2000;
d=c;
- 1
此语句为普通变量赋值语句,操作的都是 d 和 c 的空间内容。
c的空间内容已赋值为了2000,取出 c 的内容,并赋值给 d 的内容
指针变量的操作
*c = b;
- 1
直接引用 c ,会得到c对应空间的内容:2000,也就是一个地址
再对(*该地址)进行操作,操作的就是该地址的空间内容。
所以此语句最终会执行为:
操作2000空间中的内容。
而此时,2000的空间已被a对应,且d的空间内容也是2000。
此时直接操作2000空间的内容,就有如下结果:
*c == a == *d == b;
- 1
*c、*d、a都是对应的2000的空间,而2000空间内容已被赋值为b。
虽然b和a的空间地址不同,但直接操作空间时,操作的是空间内容,所以以上逻辑为true;
阶段小结
变量的定义与取用
定义一个变量,就是为该变量名开辟相应空间,并把空间首地址与变量名关联。
直接使用一个变量名,操作的就是该变量名对应空间。
int x=y;
- 1
代码中直接使用了变量名x,y,操作的就是它们对应空间。
y在 = 号右边,就是取用其空间内容。
x在 = 号左边,就是将其空间内容改变成 = 号右边的值。
符号
定义类型 * ,表示限制其后的第一个变量的类型为指针类型
地址取用符号 &,表示直接取用变量对应的地址。
内容操作符号 *,仅可跟在一个地址值之前,表示操作该地址值的内容。
int a = 2;
*&a = 3;
- 1
- 2
&a表示取用a对应的地址,*(a对应的地址)表示操作该地址的内容,所以此时a对应的地址的内容被改变成了3;*&a 等价于a;
但不能有
&a = 1;
- 1
因为&a,已经得到了一个地址值,注意,是地址值,并不是真正意义地址,相当于
&a为2000,2000=1;是非法的
多重指针
定义与赋值;
定义
int **e;//等价于int* *e;
- 1
以上代码可以从左至右理解为:
为e开辟一块对应空间;
左起首位 * 表示e对应的空间仅可存放指针(地址);
第二个 * 表示e内存放的指针(地址)所对应的空间内容,也只能是个指针(地址);
int限制着最底层的地址内,只能存int类型的数据。
赋值
e=&c;
- 1
c为指针变量,就会有:
&c表示取c的空间地址,取得2008;而在2008内,存放的是a的地址2000,2000内,只能存放int数据
所以&c可以得到一个指针,指针内存放的还是指针。
e对应2016空间,2016内只能存一个指针,此时则将c对应的2008放入了2016中。
取用
*e=&b;
**e=3;
- 1
- 2
&b,得到的就是b所对应的空间值:2004;
上文已知,直接使用变量名,表示操作该变量名对应空间的内容
直接使用 e,得到的就是e对应空间2016中,所存放的地址值:2008;
,而*一个地址值,表示操作地址中的内容
现在可以将*e理解为*2008,也就是操作2008空间的内容。
所以此时的赋值操作,就是将2008中,原本存放的 a对应的空间 2000,换成b对应的空间 2004;
所以用此时,e的空间2016中,存放的还是指针变量c 的地址2008,但是c对应的2008空间中,原本的2000,变成了2004;
因为直接操作一个地址(非地址值),无非就是取得地址中的值, 或者是重写地址中的值
直接操作 e 时相当于直接操作e对应的地址,不是赋值操作,便是取值操作;
这里的 e 显然是取值操作,那么就会取得2016中的2008;
所以**e 等价于**2008;
然后**2008,已知* 是操作该地址,此时操作2008,无非是取值或赋值;
这里显然也是取值,便得到了2008中的2004;
所以**2008 等价于 *2004;
* 是操作该地址,此时的*2004,显然就是操作地址2004,取值 | 赋值。
很显然,现在已经到了赋值的步骤,
所以就会直接改变2004空间的内容
而b又对应2004,所以该代码间接的改变了b的值。
*2008,又等价于2004;