C语言指针

第四章 指针

4.1 地址、指针和变量

地址:内存单元的编号也叫做地址。
指针:内存单元地址称为指针,即指针就是地址
指针变量:存放指针的变量称为指针变量。
各种类型变量都有地址,通过地址(指针)或变量名访问变量。

1)地址和指针的基本概念
变量:命名的内存空间,用于存放各种类型的数据。
变量名:变量名是给内存空间取的一个容易记忆的名字。
变量的地址:变量所使用的内存空间的地址。
变量值:在变量单元中存放的数值。即变量的内容。
“&”符号为取地址符号,可以取得变量的地址。
直接访问:在程序中使用变量名(变量地址)存取变量值的方式,称为”直接访问”方
式。以前的程序中都是采用这种方式。
间接访问:定义一个指针变量来存放了一个变量的地址,通过指针变量存取变量的值,
称为”间接访问”方式。
指针:就是内存单元的地址。指针指向一个内存单元。
指针变量:就是存放地址的变量。但有时为了阐述方便所说指针都是指针变量。

2) 指针变量类型的定义
定义的一般格式为:
<数据类型> <*指针变量名>[=初始地址值];
其中:数据类型是指针变量所指向变量数据类型。可以是 int、char、float 等基本类型,
也可是数组等构造类型。
例如: int p1,p2;
说明:指针变量是用来存放变量的地址的。
指针变量前面的
表示其后为指针变量。指针变量的名字是 p1、p2。而不是
p1、*p2。
一个指针变量只能指向同一个类型的变量。指针变量存放地址值,在内存中占用的空间是 2
个字节。

3) 指针变量的赋值
指针变量一定要周赋值后才可以使用。禁止使用未初始化或未赋值的指针(否则可能导致
系统的崩溃)。
指针运算符:
&: 取地址运算符。功能:取变量的地址。单目运算符,结合性为自右向左。例如:&a
为取变量 a 的地址。
*: 取内容运算符。它的作用:与&相反,表示指针所指向的变量(取变量内容)。*是单
目运算符,其结合性为自右向左。
指针变量的赋值方法:

1).将地址直接赋值给指针变量
例如:float *f=(float *)malloc(4);
注: malloc()函数申请动态分配内存空间,返回空间首地址;free()函数释放不用内存空
间。使用这 2 个函数时需要声明头文件<stdlib.h>或<alloc.h>。

2).将变量的地址赋值给指针变量
例如:int i,*p; p=&i;

4.2 指针变量的运算

1)赋值运算
有以下几种形式:
(1) 指针变量初始化赋值
例如:float *f=(float *)malloc(4);
int i,*p=&i;
(2) 变量的地址赋予指针变量
例如:int a,*p;p=&a; /把整型变量 a 的地址赋予整型指针变量 p/
(3) 指针变量的值赋予另一个指针变量(类型相同)
例如:int a,*pa=&a,*pb;pb=pa; /把 a 的地址赋予指针变量 pb/
(4) 数组的首地址赋予指针变量。
例如:int a[5],*p;p=a;或写为:p=&a[0]; 或:int a[5],p=a;
2)加减运算
(1)指针加减任意整数运算
运算条件:必须是指向数组的指针变量,才能加可以加上或减去一个整数 n。对指向
它类型变量的指针变量作加减运算是毫无意义的。
运算形式:设有数组指针变量 p,则有:p+n,p-n,p++,++p,p–,–p 运算都是合法的。
运算作用:指针变量±n 就是把指针指向的当前位置向前(减)或向后(加)移动 n 个元素
位置。
在这里插入图片描述
例如:int a[5],p;
p=a; /p 指向数组 a,也是指向 a[0]/
p=p+2; /p 指向 a[2],即 p 的值为&p[2]/ p++的操作等价于(p++),其作用是,先进行
p 的操作,然后进行 p+1 操作。
++p 的操作等价于(++p),其作用是,先将指针 p 加一,然后进行
p 操作。
(2)两指针变量相减
运算条件:必须是指向同一数组的 2 个指针变量才能进行两指针相减运算。
运算形式:设有数组指针变量 p、q,则有:p-q
运算作用:相减所得之差是两个指针所指数组元素之间相差的元素个数。
例如: 设 pf1=2010H,pf2=2000H 且指向同一实型数组 a 的两个指针变量,
则得: pf1-pf2=(2000H-2010H)/4=4
在这里插入图片描述
(3)指针的关系运算
运算条件:必须是指向同一数组的 2 个指针变量才能进行关系运算。
运算作用:两个指针变量通过关系运算进行比较,主要用于判断他们的前后位置关系。
例如:设如果 pa 和 qa 都指向同一个数组 a,
则有:pa>qa、pa<qa、 pa>=qa、 pa<=qa、 paqa、 pa!=qa
说明:当 pa 所指元素在 qa 之前,则表达式”pa<qa”为真(值为 1);
当 pa 和 qa 都指向同一个元素时,表达式”pa
qa” 为真(值为 1)。

4.3 指针与数组

(1) 数组指针
1)指向一维数组的指针
数组名代表数组的首地址,是一个常量。数组指针:就是指向数组元素地址的指针变量。
引用数组元素除了用下标法外也可以用指针法来引用数组元素。
例如:int a[8]={2,4,6,8,10,12,14,16};
int *p;
p=&a[0];
下面的语句是等价的:p=&a[0]; p=a;
在这里插入图片描述
例如:
int a[8]={2,4,6,8,10,12,14,16};
int *p=&a[0];
它相当于:
int *p; p=&a[0];
有以下四种方法引用数组元素 a[k]: (p+k)、(a+k)、p[k]、a[k]
前两种称为指针法,后两种称为下标法。
在这里插入图片描述
注意指针变量的运算。
设指针 p=a(a 为数组名)
① 执行 p++ ,p 指向下一个元素,即 a[1]。*p 为 a[1]的值。
② *p++,相当于 *(p++)。
③ *(p++)与 *(++p)作用不同。
④ (*p)++,表示将 p 所指向的元素值加 1. ⑤ 如果 p 当前指向 a 数组中的第 i 个元素,则:
(p–)相当于 a[i–],先取 p 值作””运算,再使 p 自减。
(++p)相当于 a[++i],先使 p 自加,再作运算。
(–p)相当于 a[–i], 先使 p 自减,再作运算。
2)指向二维数组的指针

设有一个二维数组 a 定义为:
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
二维数组 a,可视为三个一维数组:a[0]、a[1]、a[2];而每个一维数组又是一维数组,
分别由 4 个元素组成,即:二维数组名 一维数组名 数组元素
在这里插入图片描述
(1)用数组名表示二维数组的行地址
数组名总是代表数组的首地址。则有:a 为二维整型数组名,a=2000。
a 就是第 0 行的首地址 2000。即为 a[0]
a+1 代表第一行首地址。为 2008。即为 a[1]
a+2 代表第二行首地址。即 2016。即为 a[2]
(2)用数组名表示二维数组元素地址
二维数组中 a[0]、a[1]、a[2]都是地址,
则有:(a+0)、(a+1)和*(a+2)也是地址,它们分别是第 0 行、1 行和 2 行的第 0 列首地
址。因此,a[0]+1 等介于*(a+0)+1,即&a[0][1]。
a[1]+2 等介于*(a+1)+2,即&a[1][2]。
a[2]+3 等介于*(a+1)+3,即&a[1][3]。
a[0][1]的值 可表示为:(a[0]+1)和((a+0)+1)
a[i][j]的值 可表示应为:
(a[i]+j)和*((a+i)+j)。
在这里插入图片描述
数组元素三种形式引用:
(1) a[i][j] 下标法
(2) (a[i]+j) 用一维数组名
(3) ((a+i)+j) 用二维数组名
3)用指针变量指向二维数组及其元素
(1) 指向数组元素的指针变
二维数组的每个元素在内存中存储在地址连续的存储空间中。用指向数组元素的指针变
量来引用数组。
(2) 指向由 m 个整数组成的一维数组的指针变量
定义一个整型指针变量指向一维数组,一维数组的每个元素包含 m 个元素。
定义格式:
(标识符)[一维数组元素个数];
例如:int (q)[4];
定义一个指针变量 q,它指向包含有 4 个元素的一维数组。
注意:q 必须放在括弧内,否则就变成了定义指针数组。
由于 q 是指向有 4 个整形元素的一维数组的指针变量,因此,q+1 是将地址值加上 4
2,
即指向下一个一维数组。
设有如下定义:
int a[3][4]= {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int (q)[4];
q=a;
则:q+0 为二维数组第 0 行首地址,与 a+0 或
(a+0)相同;
q+1 为二维数组第 1 行首地址,与 a+1 或
(a+1)相同;
q+2 为二维数组第 2 行首地址,与 a+2 或
(a+2)相同;
(q+i)+j 为第 i 行第 j 列元素的地址,与(a+i)+j 相同;
((q+i)+j) 为第 i 行第 j 列元素,与
(
(a+i)+j)相同,即 a[i][j]。
如 int a[5][10] 与 int (p)[10]
二维数组名是一个指向有 10 个元素的一维数组的指针(地址)常量
p=a+i 使 p 指向二维数组的第 i 行
((p+i)+j) = a[i][j]
二维数组形参实际上是一维数组指针变量,即 int x[][10]= int (x)[10]
变量定义(不是形参)时两者不等价
系统只给 p 分配能保存一个指针值的内存区(一般 2 字节);而给 a 分配 2
5
10 字节的内
存区

4.4 指针作为函数的参数

函数的参数传递可以采用地址传递。所以指针可作为函数参数。当指针作为函数的形参
时,实参传递的是地址,在函数中通过地址访问实参,所以,在函数中通过地址对实参的修
改影响到实参的值。
【例】输入的两个整数按大小顺序输出。
#include<stdio.h>
void swap(int x,int y)
{ int temp;
temp=x;
x=y;
y=temp;}
void main()
{ int a,b;
scanf(“%d,%d”,&a,&b);
if(a<b) swap(a,b);
printf(“\n%d,%d\n”,a,b); }
运行结果:
1, 2↙
1,2
可见:此程序没能够达到题目要求。虽然 a<b,并且进入 swap()函数进行数据交换,实
现了 x 和 y 的交换,返回到主函数后,x 和 y 都被释放掉了。
【例】输入的两个整数按大小顺序输出。
swap(int *p1,int *p2)
{ int temp;
temp=*p1; *p1=*p2; *p2=temp;
}
void main()
{ int a,b;
int *p1,*p2;
scanf(“%d,%d”,&a,&b);
p1=&a;p2=&b;
if(a<b) swap(p1,p2);
printf(“\n%d,%d\n”,a,b);
}
4.5 数组名作为函数参数
【例】 在主函数中定义数组,在被调用函数中给 20 个元素依次赋值 0 到 19,在主函数
中输出这些整数。
程序如下:
#include<stdio.h>
#include “stdlib.h”
#define N 20
getin(int x[ ],int m)
{
int i;
for (i=0;i<m;i++)
x[i]=i;
}
void main()
{
int array[N];
int i;
getin(array,N);
for (i=0;i<N;i++)
{ if (i%5==0 ) printf(“\n”);
printf(“%4d”,array[i]);
}
}
数组指针作为函数参数可以分为 4 种情况:
☆ 形参、实参都是数组名;
☆ 实参是数组名,形参是指针变量;
☆ 形参、实参都是指针变量;
☆ 实参是指针变量,形参是数组名。
第一种:形参、实参都是数组名
void inv(int x[], int n)
{ int t, i, j, m=(n-1)/2;
for(i=0; i<=m; i++)
{ j= n-1-i;
t=x[i]; x[i]=x[j]; x[j]=t; }
}
main( )
{ int i, a[10]={3,7,9,11,0,6,7,5,4,2};
inv(a,10);
printf(“The reverted array:\n”);
for(i=0; i<10; i++)
printf(“%d,”, a[i]);
printf(“\n”);
}
第二种:实参是数组名,形参是指针变量
void inv(int *x, int n)
{ int t, *p, *i, *j, m=(n-1)/2;
i=x; j=x+n-1; p=x+m;
for( ; i<=p; i++, j–)
{ t=*i; *i=*j; *j=t; }
}
main( )
{ int i, a[10]={3,7,9,11,0,6,7,5,4,2};
inv(a,10);
printf(“The reverted array:\n”);
for(i=0;i<10;i++)
printf(“%d,”, a[i]);
printf(“\n”);
}
第三种:形参、实参都是指针变量
void inv(int *x, int n)
{ int t, *i, *j, *p, m=(n-1)/2;
i=x; j=x+n-1; p=x+m;
for( ; i<=p; i++, j–)
{ t=*i; *i=*j; *j=t; }
}
main( )
{ int i,a[10],*p=a;
for(i=0; i<10; i++, p++)
scanf(“%d”, p);
p=a;
inv(p,10);
printf(“The reverted array:\n”);
for(p=a; p<a+10; p++)
printf(“%d”,*p);
}
第四种:实参是指针变量,形参是数组名
void inv(int x[], int n)
{ int t, i, j, m=(n-1)/2;
for(i=0; i<=m; i++)
{ j=n-1-i;
t=x[i]; x[i]=x[j]; x[j]=t;
}
}
main( )
{ int i, a[10], *p=a;
for(i=0; i<10; i++,p++)
scanf(“%d”, p);
p=a; inv(p,10);
printf(“The reverted array:\n”);
for(p=a; p<a+10; p++)
printf(“%d “,*p);
}
4.6 指针数组
指针数组:数组的所有元素都是存放指针的数组称为指针数组。
指针数组的定义形式:
<数据类型> <指针数组名>[数组长度][={地址列表}];
int p[4];
由于[ ]比
的优先级高,先构成 p[4]数组形式,有 4 个元素:p[0],p[1],p[2],p[3]。然后再与
p 前面的”
”结合成为指针数组。
指针数组的用途:每个元素存放一个字符串首址,使得在处理多个字符串时非常方便。
void show_str(char *s[],int n)
{
int i;
for(i=0;i<n;i++)
printf(”%s \n”,s[i]);
}
main( )
{ char *name[ ]={“Follow me”,“BASIC”,“Great Wall”,“FORTRAN”,“Computer “};
int n=5;
show_str(name,n);
}
4.7 指向指针的指针
指针变量:专门用来存放另一个变量的地址的变量就称为指针变量;
指向指针的指针:用来存放指针变量的地址的变量就称为指向指针变量的指针变量。有的
教材又称为二级指针变量。
定义格式:
<类型标识符> **变量名;
例如:int **p;
void main()
{
int a=12;
int *p1,**p2;
p1=&a;
p2=&p1;
printf(”%d,%d\n”,*p1,**p2);
}
运行结果:
12,12
#define NULL 0
void main( )
{
char **p;
char *name[]={“hello”,“good”,“world”,“bye”,“”};
p=name+1;
printf(“%o : %s “, *p,*p);
p+=2;
while(**p!=NULL)
printf(”%s\n”,*p++);
}
int **p 与 int *q[10]的区别和联系:
指针数组名是二级指针常量,若 p=q; p+i 是 q[i]的地址;指针数组作形参时,int *q[ ]
与 int **p 完全等价;但作为变量定义两者不同:系统只给 p 分配能保存一个指针值的内存区;
而给 q 分配 10 块内存区,每块可保存一个指针值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值