目录
函数的参数
对象和地址
取地址运算符
注意
指针
注意
指针运算符
注意
在C语言中,指针是一个十分重要的概念,它的作用是“指示对象”。
例如:你要去一座公寓楼找一位朋友,公寓楼由很多楼层组成,每个楼层由若干个房间组成,而房间都有属于一个自己的编号,假如你不知对应的楼层的房间号,你需要一个一个房间去找,这是很麻烦的,而当你指定对应的房号时就能快速精准定位目标,而这里的房间号就对应为地址,也可以称为指针。
下面我们由一个例子来引出接下来我们所要学习的关于指针的基本知识:
函数的参数
#include<stdio.h>
/*将n1和n2的和、差分别保存在sum、diff中(错误的示范)*/
void sum_diff(int n1, int n2, int sum, int diff)
{
sum = n1 + n2;
diff = (n1 - n2) ? n1 - n1:n2 - n1;
}
int main()
{
int na, nb;
int wa = 0, sa = 0;
puts("请输入两个整数:");
printf("整数A:");scanf("%d", &na);
printf("整数B:");scanf("%d", &nb);
sum_diff(na, nb, wa, sa);
printf("两数之和是%d,两数之差是%d", wa, sa);
return 0;
}
main函数在调用sum_diff函数时,实参na,nb,wa,sa的值会分别传递给形参n1,n2,sum,diff。这个复制的过程是单向的,这中参数传递方式称为值传递,这样即使改变形参sum,diff的值原来的wa,sa的值并不会改变。
因此在调用sum_diff函数之后,在main函数中wa,sa的值依然是0.
在前面的学习中,我们可以得知,函数返回到调用源的返回值只能有1个,不能返回到两个以上的值,所以不能将和、差返回给函数。
为了解决这个问题,必须掌握C语言学习的难点之一——指针(pointer)
对象和地址
变量是“保存数值的盒子”,它并不是像下图中随意存放的:
而是下面这样有序的存放在内存空间里:
变量具有多个侧面或者是属性,举例来说,其中一个属性就是数据类型长度,上面中的int型和double型的数据长度就不同,这两个变量的长度可以通过sizeof()求得。
有些编译器中sizeof(int)、sizeof(double)是相等的,但是构成它们的每一位却不尽相同
数据类型决定变量可以表示数值范围,除此之外,表示变量在内存中生命期范围的存储期以及变量名也都是变量的重要属性。
在广阔的内存空间上,存在着很多对象,这就需要某种方式来表示各个对象在内存中存储的位置,这就是地址(address)。
对象(变量)的地址是指其在内存中的存储编号,其中int型变量n的地址为212,double型变量x的地址为216。
取地址运算符
每个对象都有地址,那么我们来看下它们的地址是怎样的:
#include<stdio.h>
int main()
{
int n;
double x;
int a[3];
printf("n的地址:%p\n", &n);
printf("x的地址:%p\n", &x);
printf("a[0]的地址:%p\n", &a[0]);
printf("a[1]的地址:%p\n", &a[1]);
printf("a[2]的地址:%p\n", &a[2]);
return 0;
}
/*n的地址:0061FECC
x的地址:0061FEC0
a[0]的地址:0061FEB4
a[1]的地址:0061FEB8
a[2]的地址:0061FEBC*/
对象的地址通常都是由十六进制数表示的,但是在不同的编译器下,有时结果也不相同。
我们一直使用的单目运算符&通常被称为取地址运算符(address operator),将&写在对象名之前就可以得到该对象的地址。
如果对象的长度为2,占用212号和213号的内存单元,那么该对象的地址就是它的首地址212号。
&a 取得a的地址(生成指向a的指针) |
注意
取地址运算符&的功能是取得对象的地址,表示对象地址的转换说明为%p(p为pointer的首字母)。
指针
只显示地址并无意思,让我来看看更具实际作用的功能吧:
#include<stdio.h>
int main()
{
int Lihua = 178;//李华的身高
int Wangjun = 175;//王军的身高
int Xiaoming = 179;//小明的身高
int *honghong, *huahua;//红红、花花
honghong = &Lihua;//honghong指向Lihua(喜欢李华)
huahua = &Xiaoming;//huahua指向Xiaoming
printf("红红喜欢的人身高:%d\n", *honghong);
printf("花花喜欢的人身高:%d\n", *huahua);
honghong = &Wangjun;//honghong指向Wangjun(移情别恋)
*huahua = 180;//将huahua指向的对象赋值为180(修改花花喜欢的人身高)
putchar('\n');
printf("李华的身高是:%d\n", Lihua );
printf("王军的身高是:%d\n", Wangjun );
printf("小明的身高是:%d\n", Xiaoming );
printf("红红喜欢的人的身高是:%d\n", *honghong);
printf("花花喜欢的人的身高是:%d\n", *huahua);
return 0;
}
变量honghong和huahua的声明中,变量名前带有 * ,通过该声明定义了两个“指向int型变量的指针变量”,它们指向的是int型对象
我首先明确一下“int型变量”和“指向int型变量的指针变量”有何区别:
☛int型变量:保存“整数”盒子
☛指向int型变量的指针变量:保存“存放整数对象的地址”的盒子
我们以下图为例:
int型Lihua的地址是212号,因此若执行“honghong = &Lihua”,honghong中就会被存入212号,这是honghong和Lihua的关系就是honghong指向lihua
当指针p的值为对象x的地址时,一般说“p”指向“x”。
honghong的数据类型是“指向int型变量的指针型”
honghong = &Lihua;
&Lihua的类型也是“指向int型变量的指针”,取地址运算符与其说时取得地址,不如说是生成指针。
表达式&Lihua是指向Lihua的指针,其值为Lihua的地址。
注意
将取地址运算符&写在Type型对象x前得到的&x为Type *指针,其值为x的地址。
指针运算符
在进行显示的地方,就要用到指针运算符(也称为间接访问运算符)的单目运算符 * (unary operator)。将指针运算符*写于指针之前,就可以显示该指针指向的对象内容。
*a a指向的对象 |
#include<stdio.h>
int main()
{
int a = 10;
int *pa = &a;
*pa = 20;
printf("%d", a);
return 0;
}
在这里*pa就存放变量a的地址,就相当于“pa指向的对象”,因此*pa就是变量a的别名,所以改动*pa存放地址的值,也就相当于改动了a的值。
注意
当pa指向a时,*pa就是变量a的别名。
指针在C语言中是十分重要的,但是不难理解:指针就相当于一个门牌号方便与从内存单元中查找,而形如int *a;的形式 *a指的是指针型变量,而加上int就是创造了一个保存对象地址的变量,而取得地址的指针型变量就相当于该地址所在变量的分身。
相信大家只要用心梳理几遍就可以明了了。在下面的学习中我们还将会学习关于函数、数组与指针的关系。