指针
指针,可以直接通过内存地址访问计算机内存。存储空间的每一个字节都有一个编址,就是每个内存的地址编号。表示在内存中的位置。
1.指针变量的定义
指针变量是用于保存地址值的变量。
语法:
基类型标识符 *指针变量名;
//示例1:
int *p,*q;
基类型标识符代表该指针变量可以指向的变量的类型,对应的空间中只能存这个类型的数据。
这个" * "星号,是定义指针变量的说明符。说明这个变量是一个指针变量。
示例1中说明p和q都是用于保存int类型变量的地址值的指针变量。
2.指针变量的初始化和赋值
//示例2.1
int r=504;
int *p=&r;/*初始化方法*/
//示例2.2
int r=614;
int *p;/*先定义指针变量*/
p=&r;/*然后用变量地址值给指针变量赋值*/
p作为指针变量,也是一个变量,初始化或赋值后当然可以重新赋值改变它的值。
基类型相同的指针变量之间可以相互赋值。但是基类型不同的指针变量之间,通过指针间接访问值的时候会发生错误。
3.直接访问和间接访问
//示例2.3
#include<stdio.h>
int main()
{
int r=504;
int *p=&r;/*初始化指针变量*/
printf("\t address\t\tvalue\n");
printf("r:\t%10p\t%12d\n",&r,r);
//整型变量r的地址和值,用变量名直接得到要操作的内存单元的内容,即直接访问。
printf("p:\t%10p\t%15p\n",&p,p);
printf("\n change r:");
r=1008;
printf("p=%p\t*p=%d\n",p,*p);
/*p表示指针变量,*p,表示间接访问运算符“*”通过地址间接得到该地址中存储的内容变量r的值*/
/*"&"表示取地址,直接取存储空间中的内容,“*”表示间接访问运算符。*/
return 0;
}
得运行结果
4.非法指针
定义指针变量时常将其初始化为NULL,NULL是一个标准规定的宏定义。表示指针未指向任何存储空间。
定义一个指针*p,然后它的值要么是:
double *p=NULL;
要么就是,比如定义一个float型的变量r,然后让指针p取址指向r的地址。
float r=504;
float *p=&r;
5.指针变量的运算
“*”和“&”互为逆运算。
“*”间接引用运算符,操作数是地址值。
“&”取地址运算符,得到的是变量的地址。
两个运算符可以一起使用。
*&r,就是*(&)r,先计算&r,得到r的地址,再间接访问。
&*p先计算*p得到变量的值,再对它取值。
这里要注意,不能对一般变量进行间接访问,即不可以“*r”。
算术运算
指针变量是用于存储地址值的变量,地址值相加的结果没有意义。
单个指针变量进行自加自减运算或者加减整数的运算,是表示增加指针地址值增加的基类型所占的存储空间字节数。
例如:对int类型指针+2.地址值增加了2*sizeof(int)个字节。(这里的星号是乘法运算符)。
两个同类型指针变量之间做减法得到的整数表示这两个指针变量之间相差几个存储空姐字节数。
指针变量之间的关系运算
指针变量之间的关系运算用于比较地址变量的大小。例如指针p存储的地址值<指针q存储的地址值,那么p<q的值为1。
NULL作为特殊的值表示指针没有指向,有时候需要判断指针是否有值:
int *p;
……
if(p==NULL)/*判断指针是否没有指向*/
……
指针与数组
数组是一组相同类型数据的集合,它在内存中是连续存放的。和指针的间接访问类似。
C语言规定,用数组名表示数组第一个元素的地址,数组名实质上是一个指针常量(地址常量),因此可以作为间接访问运算符“*”的运算对象。可以直接用数组名来输出该数组中的第一个值。
所以可以定义一个基类型和数组元素类型一样的指针变量来指向数组,通过移动指针来访问各个数组元素。
#include<stdio.h>
int main()
{
double r[6]={4.12,10.08,6.14,5.4,5.1,2022.49};
double *p=r;
int i;
double sum=0.0;
printf("这个数组为:");
for(i=0;i<6;i++)
{
printf("r[%d]:\t%p\t$4.2f\n",i,r[i],*(p+i));/*移动下标*/
}
for(p=r;p<r+6;p++)
{sum+=*p;}
printf("数组中所有元素的平均值为:%4.2f\n",sum/6);
return 0;
}
需要注意的是,指针变量的间接访问不能超出数组的边界。
指针变量与二维数组
我们知道,二维数组r[i][j]中,存储顺序先行后列。即r[0][0],r[0][1],r[0][2]……第一行结束后,r[1][0],r[1][1],r[1][2]……,在指针常量中,运算r+1表示从指向a[0]变成指向a[1],直接移动一行。
可以将二位数组表示行的部分堪为一维数组名,即r[i][j]中的r[i]部分,r[i]+j就表示移动j列。
二维数组中的行、列地址可以通过相应的运算进行相互转换:加“*”使行地址转为列地址,加“&”使列地址转为行地址。
类型 | 表示形式 | 含义 | 地址运算 |
---|---|---|---|
行地址 | r+j 或&r[i] | 第i行地址 | r+j+1指向下一行 |
列地址 | *(r+i)+j或r[i]+j | 第i行第j列的元素地址 | *r+i)+j+1 |
元素 | * (*(r+i)+j)或r[i][j] | 第i行第j列的元素 | N/A |
//指针访问二维数组示例
#include<stdio.h>
int main()
{
int r[3][3]={1,2,3,4,5,6,7,8,9};
int i=0;
int *p=&r[0][0];
for(i=0;i<9;i++)
{
printf("%p\t%d\n",p+i,*(p+i));
}
运行结果:
0060FEC4 1
0060FEC8 2
0060FECC 3
0060FED0 4
0060FED4 5
0060FED8 6
0060FEDC 7
0060FEE0 8
0060FEE4 9
用行指针变量访问二维数组元素
例如:int (*p)[2];指向的是长度为2的一维整型数组,如果这样的指针变量指向长度为2的二维数组,指针加1就相当于直接移动一行。
指针数组
指针数组就是数组元素为一级指针变量的数组。例如:
定义一个长度为3的指针数组p,
int *p[3];
数组元素都是基类型为int的指针变量。指针数组的元素是一级指针变量。
用指针访问二维数组:
int *p[i];
p[0]=r[y];/*把r[y]赋值给p[0] */
printf("%d\n",*p[0]+1);
printf("%d\n",p[0]+1);
p[0]指的是指针所在地址。*p[0]可以指向该地址的数据。
指针与函数
函数调用时有两种方式:传值、传地址
指针作为函数形参可以返回多个值
函数调用结束可以返回一个指针
传值与传地址
函数调用是数据从实参传给形参。
若实参给形参传递的是地址值,就是传地址。否则称为传值。
普通类型变量一般是值形参,用来传值。数组形参实质上就是指针形参变量,指针对应的实参是地址值。