【C语言】11-指针
2014-03-03 16:46
645 查看
本文目录
直接引用
一、什么是指针?
二、指针的定义
三、指针的初始化
四、指针运算符
五、指针的用途举例
六、关于指针的疑问
说明:这个C语言专题,是学习iOS开发的前奏。也为了让有面向对象语言开发经验的程序员,能够快速上手C语言。如果你还没有编程经验,或者对C语言、iOS开发不感兴趣,请忽略
指针是C语言中非常重要的数据类型,如果你说C语言中除了指针,其他你都学得很好,那你干脆说没学过C语言。究竟什么是指针呢?我们先来看一个概念。
回到顶部
1. 回想一下,之前我们是如何更改某个变量的值?
我们之前是通过变量名来直接引用变量,然后进行赋值:
2. 看上去是很简单,其实程序内部是怎么操作的呢?
其实,程序对变量的读写操作,实际上是对变量所在的存储空间进行写入或取出数据。就上面的代码而言,系统会自动将变量名a转换为变量的存储地址,根据地址找到变量a的存储空间,然后再将数据10以2进制的形式放入变量a的存储空间中。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/5ad6ac2e442c5db3b672d12d1d9654ac.png)
3. 通过变量名引用变量,由系统自动完成变量名和其存储地址之间的转换,称为变量的"直接引用"方式
回到顶部
1.我们已经知道,"直接引用"是直接通过变量名来读写变量
2.C语言中还有一种"间接引用"的方式(以变量a为例):首先将变量a的地址存放在另一个变量中,比如存放在变量b中,然后通过变量b来间接引用变量a,间接读写变量a的值。这就是"间接引用"。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/6f10740135e2331a31b724444149d33b.png)
如果程序通过"间接引用"的方式来修改a的值,可以这样做:先根据 变量名b 获取 变量b 的地址ffc2,取出变量b中存储的内容ffc1,也就是变量a的地址,再根据变量a的地址ffc1找到a的存储空间,然后修改里面的数据。
3.总结一句:用来存放变量地址的变量,就称为"指针变量"。在上面的情况下,变量b就是个"指针变量",我们可以说指针变量b指向变量a。
回到顶部
一般形式:类名标识符 *指针变量名;
"*"是一个说明符,用来说明这个变量是个指针变量,是不能省略的,但它不属于变量名的一部分
前面的类型标识符表示指针变量所指向的变量的类型,而且只能指向这种类型的变量
回到顶部
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
注意第8行,赋值给p的是变量a的地址&a
指针变量是用来存放变量地址的,不要给它随意赋值一个常数。下面的写法是错误的
回到顶部
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
当程序刚执行完第5行代码时,内存中大概的分布情况是这样的
![](https://oscdn.geek-share.com/Uploads/Images/Content/202003/24/43e0eefd9ae780888b05d9e9d66cee8a.png)
,a值是10,p值就是变量a的地址ffc3。
注意下第5、第8行,都有个"*",它们的含义是不一样的:
(1) 第5行的"*"只是用来说明p是个指针变量
(2) 第8行的"*"是一个指针运算符,这里的*p代表根据p值ffc3这个地址访问对应的存储空间,也就是变量a的存储空间,然后将右边的数值9写入到这个存储空间,相当于 a = 9;,于是内存中就变成这样了
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/102eae18ef16096c29939ef3922c357e.png)
输出结果为:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/de9882161ba47120a63a902f8183b5d8.png)
,可以发现,我们通过变量p间接修改了变量a的值。
指针运算符除了可以赋值之外,还可以用于取值
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
输出结果:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/1d1840141addf5690da1d3f5191355fa.png)
,第6行中的*p的意思是:根据p值(即变量a的地址)访问对应的存储空间,并取出存储的内容(即取出变量a的值),赋值给value
在指针变量没有指向确定地址之前,不要对它所指的内容赋值。下面的写法是错误的
应该在指针变量指向一个确定的变量后再进行赋值。下面的写法才是正确的
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
回到顶部
前面我们通过指针变量p间接访问了变量a,在有些人看来,觉得指针变量好傻B,直接用变量名a访问变量a不就好了么,干嘛搞这么麻烦。别着急,接下来举个例子,让大家看看指针还能做什么事情。
现在有个要求:写一个函数swap,接收2个整型参数,功能是互换两个实参的值。
1> 如果没学过指针,你可能会这样写
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
输出结果:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/73aaa214b700a12592ef5cafc11b0fee.png)
,虽然v1和v2的值被交换了,但是变量a和b的值根本就没有换过来。因为基本数据类型作为函数实参时,只是纯粹地将值传递给形参,形参的改变并不影响实参。
我们可以简要分析一下这个过程:
* 在第20行中,将变量a、b的值分别传递给了swap函数的两个形参v1、v2
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/2f1f96e3fc1d111680cb29d99ca739f0.png)
* 在第8行中,将v1的值赋值给了temp
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/5f1a70d6e45173f49eafe45d2b6f96b3.png)
* 在第9行中,将v2的值赋值给了v1
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/86f5f2aa67269c63f82186ecb55e654a.png)
* 在第10行中,将temp的值赋值给了v2
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/10dccd29ef0c648c2dbe556659e2736f.png)
就这样,v1和v2的值被交换了,但是a和b的值一直都没有改变
2> 如果学了指针,就应该这样写
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
先看看输出结果:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/c5266df9efd14345b7426a92d1176bf2.png)
,变量a和b的值终于换过来了。
解释一下:
(在16位编译器环境下,一个指针变量占用2个字节)
* 先注意第20行,传递是变量的地址。因此swap函数的形参v1指向了变量a,v2指向了变量b
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/8036c3f14f3616bc41a0d7f825892275.png)
* 第6行代码是取出v1指向的变量的值,也就是变量a的值:10,然后赋值给变量temp
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/4ee2241455f126e67ea2ead8a6d814b4.png)
* 第9行代码是取出v2指向的变量(变量b)的值,然后赋值给v1指向的变量(变量a)
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/35d5826ada22500242d9519d64c87c0c.png)
* 第12行代码是将temp变量的值赋值给v2指向的变量(变量b)
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/b69dc454a296ad96dc3abacc1594092a.png)
相信你已经感受到指针的强大了,如果没有指针,在一个函数的内部根本改变不了外部的实参。
接下来再举一个指针的实用例子。默认情况下,一个函数只能有一个返回值,有了指针,我们可以实现函数有"多返回值"。
现在有个要求:写一个函数sumAndMinus,可以同时计算2个整型的和与差,函数执行完毕后,返回和与差(注意了,这里要返回2个值)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
输出结果:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/0dd59d30f39c8e34d23f975864b2efc8.png)
,和与差都由同一个函数计算并返回出来。和是函数的直接返回值,差是通过函数的第3个指针参数间接返回。
因此有了指针,我们可以让函数有"无限个"返回值。
回到顶部
刚学完指针,都可能有一大堆的疑惑,这里我列出几个常见的疑惑吧。
在同一种编译器环境下,一个指针变量所占用的内存空间是固定的。比如,在16位编译器环境下,任何一个指针变量都只占用2个字节,并不会随所指向变量的类型而改变。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202003/24/4edd680e68ee36f0094a596277de7d54.png)
其实,我觉得这个问题跟"数组为什么要分类型"是一样的。
* 看下面的代码,利用指针p读取变量c的值
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
这个输出结果应该难不倒大家:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/f1cb291be682ffa0aa16bb2526bbb880.png)
,是可以成功读取的。
* 如果我改一下第5行的代码,用一个本应该指向int类型变量的指针p,指向char类型的变量c
我们再来看一下输出:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/73838583960b95ff654b18a71e466aac.png)
,c的原值是1,现在取出来却是513,怎么回事呢?这个要根据内存来分析
根据变量的定义顺序,这些变量在内存中大致如下图排布:
其中,指针变量p和int类型变量i各占2个字节,char类型的c占一个字节,p指向c,因此p值就是c的地址
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/e3915bf0609bc41e94fbcfa6fd683054.png)
1> 最初的时候,我们用char *p指向变量c。当利用*p来获取变量c的值时,由于指针p知道变量c是char类型的,所以会从ffc3这个地址开始读取1个字节的数据:0000 0001,转为10进制就是1
2> 后来,我们用int *p指向变量c。当利用*p获取变量c的值时,由于指针p认为变量c是int类型的,所以会从ffc3这个地址开始读取2个字节的数据:0000 0010 0000 0001,转为10进制就是513
可见,给指针分类是多么重要的一件事,而且一种指针最好只指向一种类型的变量,那是最安全的。
直接引用
一、什么是指针?
二、指针的定义
三、指针的初始化
四、指针运算符
五、指针的用途举例
六、关于指针的疑问
说明:这个C语言专题,是学习iOS开发的前奏。也为了让有面向对象语言开发经验的程序员,能够快速上手C语言。如果你还没有编程经验,或者对C语言、iOS开发不感兴趣,请忽略
指针是C语言中非常重要的数据类型,如果你说C语言中除了指针,其他你都学得很好,那你干脆说没学过C语言。究竟什么是指针呢?我们先来看一个概念。
回到顶部
直接引用
1. 回想一下,之前我们是如何更改某个变量的值?我们之前是通过变量名来直接引用变量,然后进行赋值:
char a; a = 10;
2. 看上去是很简单,其实程序内部是怎么操作的呢?
其实,程序对变量的读写操作,实际上是对变量所在的存储空间进行写入或取出数据。就上面的代码而言,系统会自动将变量名a转换为变量的存储地址,根据地址找到变量a的存储空间,然后再将数据10以2进制的形式放入变量a的存储空间中。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/5ad6ac2e442c5db3b672d12d1d9654ac.png)
3. 通过变量名引用变量,由系统自动完成变量名和其存储地址之间的转换,称为变量的"直接引用"方式
回到顶部
一、什么是指针?
1.我们已经知道,"直接引用"是直接通过变量名来读写变量2.C语言中还有一种"间接引用"的方式(以变量a为例):首先将变量a的地址存放在另一个变量中,比如存放在变量b中,然后通过变量b来间接引用变量a,间接读写变量a的值。这就是"间接引用"。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/6f10740135e2331a31b724444149d33b.png)
如果程序通过"间接引用"的方式来修改a的值,可以这样做:先根据 变量名b 获取 变量b 的地址ffc2,取出变量b中存储的内容ffc1,也就是变量a的地址,再根据变量a的地址ffc1找到a的存储空间,然后修改里面的数据。
3.总结一句:用来存放变量地址的变量,就称为"指针变量"。在上面的情况下,变量b就是个"指针变量",我们可以说指针变量b指向变量a。
回到顶部
二、指针的定义
一般形式:类名标识符 *指针变量名;int *p; float *q;
"*"是一个说明符,用来说明这个变量是个指针变量,是不能省略的,但它不属于变量名的一部分
前面的类型标识符表示指针变量所指向的变量的类型,而且只能指向这种类型的变量
回到顶部
三、指针的初始化
1.先定义后初始化
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
1 // 定义int类型的变量a 2 int a = 10; 3 4 // 定义一个指针变量p 5 int *p; 6 7 // 将变量a的地址赋值给指针变量p,所以指针变量p指向变量a 8 p = &a;
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
注意第8行,赋值给p的是变量a的地址&a
2.在定义的同时初始化
// 定义int类型的变量a int a = 10; // 定义一个指针变量p // 并将变量a的地址赋值给指针变量p,所以指针变量p指向变量a int *p = &a;
3.初始化的注意
指针变量是用来存放变量地址的,不要给它随意赋值一个常数。下面的写法是错误的int *p; p = 200; // 这是错误的
回到顶部
四、指针运算符
1.给指针指向的变量赋值
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
1 char a = 10; 2 printf("修改前,a的值:%d\n", a); 3 4 // 指针变量p指向变量a 5 char *p = &a; 6 7 // 通过指针变量p间接修改变量a的值 8 *p = 9; 9 10 printf("修改后,a的值:%d", a);
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
当程序刚执行完第5行代码时,内存中大概的分布情况是这样的
![](https://oscdn.geek-share.com/Uploads/Images/Content/202003/24/43e0eefd9ae780888b05d9e9d66cee8a.png)
,a值是10,p值就是变量a的地址ffc3。
注意下第5、第8行,都有个"*",它们的含义是不一样的:
(1) 第5行的"*"只是用来说明p是个指针变量
(2) 第8行的"*"是一个指针运算符,这里的*p代表根据p值ffc3这个地址访问对应的存储空间,也就是变量a的存储空间,然后将右边的数值9写入到这个存储空间,相当于 a = 9;,于是内存中就变成这样了
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/102eae18ef16096c29939ef3922c357e.png)
输出结果为:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/de9882161ba47120a63a902f8183b5d8.png)
,可以发现,我们通过变量p间接修改了变量a的值。
2.取出指针所指向变量的值
指针运算符除了可以赋值之外,还可以用于取值![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
1 char a = 10; 2 3 char *p; 4 p = &a; 5 6 char value = *p; 7 printf("取出a的值:%d", value);
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
输出结果:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/1d1840141addf5690da1d3f5191355fa.png)
,第6行中的*p的意思是:根据p值(即变量a的地址)访问对应的存储空间,并取出存储的内容(即取出变量a的值),赋值给value
3.使用注意
在指针变量没有指向确定地址之前,不要对它所指的内容赋值。下面的写法是错误的int *p; *p = 10; //这是错误的
应该在指针变量指向一个确定的变量后再进行赋值。下面的写法才是正确的
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
// 定义2个int型变量 int a = 6, b; // 定义一个指向变量b的指针变量p int *p; p = &b; // 将a的值赋值给变量b *p = a;
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
回到顶部
五、指针的用途举例
1.例子1
前面我们通过指针变量p间接访问了变量a,在有些人看来,觉得指针变量好傻B,直接用变量名a访问变量a不就好了么,干嘛搞这么麻烦。别着急,接下来举个例子,让大家看看指针还能做什么事情。现在有个要求:写一个函数swap,接收2个整型参数,功能是互换两个实参的值。
1> 如果没学过指针,你可能会这样写
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
1 void swap(char v1, char v2) { 2 printf("更换前:v1=%d, v2=%d\n", v1, v2); 3 4 // 定义一个中间变量 5 char temp; 6 7 // 交换v1和v2的值 8 temp = v1; 9 v1 = v2; 10 v2 = temp; 11 12 printf("更换后:v1=%d, v2=%d\n", v1, v2); 13 } 14 15 int main() 16 { 17 char a = 10, b = 9; 18 printf("更换前:a=%d, b=%d\n", a, b); 19 20 swap(a, b); 21 22 printf("更换后:a=%d, b=%d", a, b); 23 return 0; 24 }
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
输出结果:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/73aaa214b700a12592ef5cafc11b0fee.png)
,虽然v1和v2的值被交换了,但是变量a和b的值根本就没有换过来。因为基本数据类型作为函数实参时,只是纯粹地将值传递给形参,形参的改变并不影响实参。
我们可以简要分析一下这个过程:
* 在第20行中,将变量a、b的值分别传递给了swap函数的两个形参v1、v2
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/2f1f96e3fc1d111680cb29d99ca739f0.png)
* 在第8行中,将v1的值赋值给了temp
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/5f1a70d6e45173f49eafe45d2b6f96b3.png)
* 在第9行中,将v2的值赋值给了v1
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/86f5f2aa67269c63f82186ecb55e654a.png)
* 在第10行中,将temp的值赋值给了v2
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/10dccd29ef0c648c2dbe556659e2736f.png)
就这样,v1和v2的值被交换了,但是a和b的值一直都没有改变
2> 如果学了指针,就应该这样写
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
1 void swap(char *v1, char *v2) { 2 // 中间变量 3 char temp; 4 5 // 取出v1指向的变量的值 6 temp = *v1; 7 8 // 取出v2指向的变量的值,然后赋值给v1指向的变量 9 *v1 = *v2; 10 11 // 赋值给v2指向的变量 12 *v2 = temp; 13 } 14 15 int main() 16 { 17 char a = 10, b = 9; 18 printf("更换前:a=%d, b=%d\n", a, b); 19 20 swap(&a, &b); 21 22 printf("更换后:a=%d, b=%d", a, b); 23 return 0; 24 }
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
先看看输出结果:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/c5266df9efd14345b7426a92d1176bf2.png)
,变量a和b的值终于换过来了。
解释一下:
(在16位编译器环境下,一个指针变量占用2个字节)
* 先注意第20行,传递是变量的地址。因此swap函数的形参v1指向了变量a,v2指向了变量b
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/8036c3f14f3616bc41a0d7f825892275.png)
* 第6行代码是取出v1指向的变量的值,也就是变量a的值:10,然后赋值给变量temp
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/4ee2241455f126e67ea2ead8a6d814b4.png)
* 第9行代码是取出v2指向的变量(变量b)的值,然后赋值给v1指向的变量(变量a)
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/35d5826ada22500242d9519d64c87c0c.png)
* 第12行代码是将temp变量的值赋值给v2指向的变量(变量b)
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/b69dc454a296ad96dc3abacc1594092a.png)
相信你已经感受到指针的强大了,如果没有指针,在一个函数的内部根本改变不了外部的实参。
2.例子2
接下来再举一个指针的实用例子。默认情况下,一个函数只能有一个返回值,有了指针,我们可以实现函数有"多返回值"。现在有个要求:写一个函数sumAndMinus,可以同时计算2个整型的和与差,函数执行完毕后,返回和与差(注意了,这里要返回2个值)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
// 计算2个整型的和与差 int sumAndMinus(int v1, int v2, int *minus) { // 计算差,并赋值给指针指向的变量 *minus = v1 - v2; // 计算和,并返回和 return v1 + v2; } int main() { // 定义2个int型变量 int a = 6, b = 2; // 定义2个变量来分别接收和与差 int sum, minus; // 调用函数 sum = sumAndMinus(a, b, &minus); // 打印和 printf("%d+%d=%d\n", a, b, sum); // 打印差 printf("%d-%d=%d\n", a, b, minus); return 0; }
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
输出结果:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/0dd59d30f39c8e34d23f975864b2efc8.png)
,和与差都由同一个函数计算并返回出来。和是函数的直接返回值,差是通过函数的第3个指针参数间接返回。
因此有了指针,我们可以让函数有"无限个"返回值。
回到顶部
六、关于指针的疑问
刚学完指针,都可能有一大堆的疑惑,这里我列出几个常见的疑惑吧。
1.一个指针变量占用多少个字节的内存空间?占用的空间是否会跟随所指向变量的类型而改变?
在同一种编译器环境下,一个指针变量所占用的内存空间是固定的。比如,在16位编译器环境下,任何一个指针变量都只占用2个字节,并不会随所指向变量的类型而改变。![](https://oscdn.geek-share.com/Uploads/Images/Content/202003/24/4edd680e68ee36f0094a596277de7d54.png)
2.既然每个指针变量所占用的内存空间是一样的,而且存储的都是地址,为何指针变量还要分类型?而且只能指向一种类型的变量?比如指向int类型的指针、指向char类型的指针。
其实,我觉得这个问题跟"数组为什么要分类型"是一样的。* 看下面的代码,利用指针p读取变量c的值
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
1 int i = 2; 2 char c = 1; 3 4 // 定义一个指向char类型的指针 5 char *p = &c; 6 7 // 取出 8 printf("%d", *p);
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/69c5a8ac3fa60e0848d784a6dd461da6.gif)
这个输出结果应该难不倒大家:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/f1cb291be682ffa0aa16bb2526bbb880.png)
,是可以成功读取的。
* 如果我改一下第5行的代码,用一个本应该指向int类型变量的指针p,指向char类型的变量c
int *p = &c;
我们再来看一下输出:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/73838583960b95ff654b18a71e466aac.png)
,c的原值是1,现在取出来却是513,怎么回事呢?这个要根据内存来分析
根据变量的定义顺序,这些变量在内存中大致如下图排布:
其中,指针变量p和int类型变量i各占2个字节,char类型的c占一个字节,p指向c,因此p值就是c的地址
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/06/e3915bf0609bc41e94fbcfa6fd683054.png)
1> 最初的时候,我们用char *p指向变量c。当利用*p来获取变量c的值时,由于指针p知道变量c是char类型的,所以会从ffc3这个地址开始读取1个字节的数据:0000 0001,转为10进制就是1
2> 后来,我们用int *p指向变量c。当利用*p获取变量c的值时,由于指针p认为变量c是int类型的,所以会从ffc3这个地址开始读取2个字节的数据:0000 0010 0000 0001,转为10进制就是513
可见,给指针分类是多么重要的一件事,而且一种指针最好只指向一种类型的变量,那是最安全的。
相关文章推荐