高级程序设计语言吴 凡
TEL,83202682
E-mail,cdwf@tom.com
电子科技大学通信与信息工程学院第十章 指针电子科技大学通信与信息工程学院
6028
引例
甲到旅馆投宿,住在 6028号 房间,在 服务处 登记了房间号 。乙、丙两人来访问甲。假设甲事先通知了乙自己的房间号,但没有通知丙。
问题:乙、丙如何找到甲
乙知道甲的房间号,可以 直接 到 6028号房间 访问甲 —— 直接访问
丙先在 服务处 查询到甲的房间号为 6028,再到 6028号房间 访问甲 —— 间接访问电子科技大学通信与信息工程学院
内存(即内部存储器),由存储单元(单位字节,Byte)组成。特点是:存储单元是 线性 并 连续 的。
如何访问存储单元?
系统给内存单元编号 —— 内存地址,通过地址可以访问对应的存储单元内存空间与内存地址
A...,..
100 101 102 103 104 105 106 107 108 109 110 110地址
char c 变量内容变量地址电子科技大学通信与信息工程学院
变量的地址:系统为变量分配的内存单元的地址(首地址)
变量地址是一个无符号的整型数变量与变量地址地址 内存 变量变量首址
char c;
int i;
float f;
c = 'a';
i = 10;
f = 2.01;
80A5 char c&c =
80A6
int i
&i =
80A8
float f
&f =
a
10
2.01
电子科技大学通信与信息工程学院变量特性
存储空间的位置:内存首地址 —— 变量名
存储空间的大小:数据类型 —— 变量类型
存储的内容:数据值 —— 变量值
生存周期:存储类别电子科技大学通信与信息工程学院变量的访问方式
直接访问,直接通过变量的地址(变量名)
来操作对应的内存单元
变量名 在编译时会被绑定到相应的 地址
间接访问,将变量 n的 地址 存放在另一个 变量 p中,要访问变量 n,可以通过首先访问变量 p,从中获得变量 n的地址,再根据地址访问变量 n
2.12 z 803A...
803A
变量 n 变量 p
803E
变量 c
电子科技大学通信与信息工程学院间接访问
关键问题:
如何定义 P?
如何获得变量 n的地址?
如何通过 p访问 n?
变量地址 又被称为变量的,指针,
存放变量地址的变量 —— 指针变量
例:指针变量
2.12 z 803A...
803A
变量 n 变量 p
803E
变量 c
指针变量 P中存放着变量 n的 首地址
( 803A) ——指针变量 p指向 变量 n
电子科技大学通信与信息工程学院指针变量的定义和引用
指针变量定义格式:
类型说明 *指针变量名;
*:指针说明符,说明定义的变量是 一个指向某种类型变量的指针
类型说明:又称为指针变量的 基类型,表明指针 指向 的变量的 数据类型 (指针变量所内存放的是哪种类型变量的地址)
例:
int *p; /*变量 p是一个指向整型变量的指针变量 */
char *ps; /*变量 ps是一个指针变量,它存放的是字符型变量的地址 */
float *pf; /*变量 pf是一个指向浮点型变量的指针变量 */
电子科技大学通信与信息工程学院指针变量的定义和引用
指针变量赋值
指针变量的值是一个无符号整型值,但 不能 将整型常量 直接赋值 给指针变量
赋值方法 1:用 变量的地址 给指针变量 赋值
变量的地址如何获得? —— 取值符号 &
变量的类型 要与 指针变量的类型 一致
赋值方法 2,相同类型 的指针变量间可以赋值
例,int a,b;
int *p,*q;
p = &a; /*方法 1*/
q = p; /*方法 2*/
电子科技大学通信与信息工程学院指针变量赋值
注意:
在使用指针变量之前,必须要给指针变量赋值。
若不赋值,则指针变量的值是一个 随机值 。这种情况必须要避免!
可以使用 空值 NULL初始化 指针变量
例,int a=10,b=2;
int *p;
p = NULL;
p = &a;
803A
变量 a 变量 p
803C
变量 b
10 2 2000NULL803A
电子科技大学通信与信息工程学院指针变量的引用
两个与指针相关的运算符,*(指针运算符,间接访问运算符 ),&(取址运算符)
格式:
&任意变量 ; &a表示变量 a所占据的内存空间的 首地址
*指针变量 ; *p表示指针变量 p所指向的内存空间的 数据
例:
int a = 2,*p;
p = &a; /*&a表示整型变量 a在内存中的地址 */
b = *p + 2; /*表达式中,*p表示指针变量 p所指向内存单元中的数据 (*p=2)*/
电子科技大学通信与信息工程学院指针变量的引用
指针变量的常见操作:
将指针变量 指向 被访问的变量
例,int n1=12,n2,*p;
p = &n1;
读 指针指向的变量
例,n2 = *p;
printf("%d",*p);
写 指针指向的变量
例,*p = 100; /*n1 = 100*/
电子科技大学通信与信息工程学院指针变量的引用
例:读程序
注意:
*p若出现在“=” 右边,则为 读 p所指向变量的内容
*p若出现在“=” 左边,则为 写 p所指向变量的内容
#include "stdio.h"
main()
{
int a=5,b=3,*p = NULL;
p = &a;
b = *p + 5;
printf("b=%d\n",b);
*p = 4;
printf("a=%d\n",a);
}
变量 a
5
变量 b
3
变量 p
NULL地址 902A
地址 902C
902A
*p
10
4
电子科技大学通信与信息工程学院指针变量的引用
注意:
*出现在不同的位置,有不同的含义:
出现在指针变量 定义处,*表示定义的变量是一个指针;
出现在其它语句中,*表示 间接访问运算 (即引用指针变量所指向的内存单元)
电子科技大学通信与信息工程学院指针变量的引用
*(指针运算符),&(取址运算符) 优先级相同,结合方向从右向左
是单目运算符,与 ++,--、!优先级相同,结合方向从右向左
优先级高(高于算术运算符)
例,int a=3,*p;
p = &a;
&*p? 变量 a的地址( &*p <->&a <->p)
*&p? 变量 a( *&p<->a)
电子科技大学通信与信息工程学院指针变量的引用
例,int a=2,b=3,*p;
p = &a;
(*p)++:表示指针 p所指向的变量(整型变量 a)
自增
*p++:表示改变指针 p
所指向的变量,将其指向整型变量 a后的下一个整型变量(整型变量
b)
变量 a2
3
803A
803C
地址变量 b
变量 p803A
int a=2,b=3,*p;
p = &a;
(*p)++;
*p++;
3
803C
电子科技大学通信与信息工程学院指针变量的引用
读程序:
#include <stdio.h>
main()
{
int a,b,c;
int *pa,*pb,*pc;
pa = &a; pb = &b; pc = &c;
scanf("%d%d",pa,pb);
printf("a=%d,b=%d\n",*pa,*pb);
c = a + b;
printf("c=%d\n",*pc);
*pc = a + *pb;
printf("c=%d\n",*pc);
c = *pa**pb;
printf("c=%d\n",c);
c = ++*pa + (*pb)++;
printf("c=%d\n",c);
}
电子科技大学通信与信息工程学院指针变量的引用
例:输入 a和 b两个整数,按先大后小的顺序输出 a和 b。
#include "stdio.h"
main()
{ int *p1,*p2,*p,a,b;
scanf("% d% d",&a,&b);
p1 = &a;
p2 = &b;
if (a< b){p=p1; pl=p2; p2=p;}
printf ( "\na=% d,b=% d\n”,a,b);
printf( "max=% d,min=% d\n",*pl,*p2);
}
变量 a
5
变量 b
10
变量 p1
变量 p2
变量 p
电子科技大学通信与信息工程学院指针变量作为函数参数
引例 1:编写一个函数 swap用于交换两个整数
#include "stdio.h"
void swap(int x,int y)
{
int tmp;
tmp = x;
x = y;
y = tmp;
}
main()
{
int a = 2,b = 3;
swap(a,b);
printf("%d,%d\n",a,b);
}
2
3
80A1 a
80A9 b
2
3
9102 x
910A y
main
swap
电子科技大学通信与信息工程学院指针变量作为函数参数
引例 2:编写一个函数 swap用于交换两个整数
#include "stdio.h"
void swap(int *x,int *y)
{
int *tmp;
tmp = x;
x = y;
y = tmp;
}
main()
{
int a = 2,b = 3;
swap(&a,&b);
printf("%d,%d\n",a,b);
}
2
3
80A1 a
80A9 b
80A1
80A9
9102 x
910A y
main
swap
电子科技大学通信与信息工程学院指针变量作为函数参数
引例 3:编写一个函数 swap用于交换两个整数
#include "stdio.h"
void swap(int *x,int *y)
{
int tmp;
tmp = *x;
*x = *y;
*y = tmp;
}
main()
{
int a = 2,b = 3;
swap(&a,&b);
printf("%d,%d\n",a,b);
}
2
3
80A1 a
80A9 b
80A1
80A9
9102 x
910A y
main
swap
*x
*y
电子科技大学通信与信息工程学院指针变量作为函数参数
注意:指针变量必须初始化
#include "stdio.h"
void swap(int *x,int *y)
{
int *tmp;
*tmp = *x;
*x = *y;
*y = *tmp;
}
main()
{
int a = 2,b = 3;
swap(&a,&b);
printf("%d,%d\n",a,b);
}
指针变量 tmp没有初始化,此时 tmp
内存放的地址是一个随机值。在系统中,程序对内存单元任意访问是 极危险的电子科技大学通信与信息工程学院指针和数组
数组:
连续 存放的若干 相同类型 变量 的集合
每一个 数组元素 都是一个 变量
其引用方式是:数组名 [下标 ]
该元素的地址,&数组名 [下标 ]
数组名 就是数组的 首地址,即该数组第一个元素地址(指针)
例,int a[10]?
数组名 a=数组的首地址= &a[0]
32
90
80A1 a[0]
80A3 a[1]
12
2341
80A5 a[2]
80A7 a[3]
26780A9 a[4]
int a[5]
a =
电子科技大学通信与信息工程学院指向数组元素的指针变量
例:
说明:
p = a 等价于 p = &a[0];
32
90
80A1 a[0]
80A3 a[1]
12
2341
80A5 a[2]
80A7 a[3]
26780A9 a[4]
int a[5]
P
NULL
a =
80A1
int a[5];
int *p = NULL;
p = a;
p = &a[2];
80A5
电子科技大学通信与信息工程学院指向数组元素的指针变量
例:
p当前指向数组的首地址,也就是数组第一元素 a[0]的地址,所以:
*p = 12; 等价于 a[0] = 12;
那么,p+1,p+2...又表示什么呢?
C语言中,如果指针变量 p指向数组的首址 a( &a[0]),则 p+1
指向该数组的下一个元素(即
a[1]),p+ i指向数组元素 a[i]
32
90
a[0]
a[1]
12
2341
a[2]
a[3]
267 a[4]
int a[5]
P
int a[5];
int *p = NULL;
p = a;
P+1
P+4
80A1
80A3
80A9
电子科技大学通信与信息工程学院指向数组元素的指针变量
数组元素引用的内部实现
原理,首地址 + 相对位移
例,int a[10];
a[1]的地址,a+1 ; 变量 a[1] 等价与 *(a+1)
a[2]的地址,a+2 ; 变量 a[2] 等价与 *(a+2)
a[ i]的地址,a+ i ; 变量 a[ i ] 等价与 *(a+ i)
如果用数组指针变量 p( p = a;)
a[1]的地址,p+1 ; 变量 a[1] 等价与 *(p+1)
a[2]的地址,p+2 ; 变量 a[2] 等价与 *(p+2)
a[ i]的地址,p+ i ; 变量 a[ i ] 等价与 *(p+ i)
注意,相对位移量 由数组的类型( 指针变量的基类型 )决定
例,int *p = a;则位移量为 2( int占 2字节内存单元)
如果 a的地址值,8024,
则 p+2表示的地址值,8024+ 2× 2= 8028
电子科技大学通信与信息工程学院数组元素引用
数组元素引用,int a[10],*p = a;
数组名 [下标 ];
元素地址,&a[0],&a[4],..
元素变量,a[0],a[4],
指针形式:
元素地址,a,a+1,p+4,p+i
元素变量,*(a+2),*(p+4)...
注意:
指向数组的指针变量也可以带下标
例,p[2] <->*(p+2) <->a[2]
电子科技大学通信与信息工程学院数组指针、指针变量与数组元素
设,int a[10]; *p = a;
地址关系
内容关系指针变量 p 数组指针 a 数组元素
p a &a[0]
p+1 a+1 &a[1]
p+i a+i &a[i]
指针变量 p 数组指针 a 数组元素
*p *a a[0]
*(p+1) *(a+1) a[1]
*(p+i) *(a+i) a[i]
电子科技大学通信与信息工程学院数组指针与指针变量的区别
注意:区别数组名 a(数组指针)和指针变量
p是不同的
数组名 a:就是数组的 首地址,是一个 常量,不能改变,不能赋值(例,a++X,a = &a[2] X )
指针变量 p:是一个 变量,可以改变,可以赋值
(例,p++; p = a+1;)
对于外部变量,定义与声明必须一致,
外部变量 定义为数组,则外部变量 声明 时也应该声明为数组,而 不能是指针
反之,亦然电子科技大学通信与信息工程学院数组指针与指针变量的区别
注意:区别数组名 a(数组的指针)和指针变量 p是不同的
..,1 2 3 4 5
803A
p=803A p+3
8040
..,1 2 3 4 5
803A
p p+3
8040
803A
8020
1,取下标值 i,将 i与数组首址
803A(数组名 p)相加,得到地址 8040
2,取地址 8040单元中的内容
1,根据地址 8020,获得指针变量 p中的内容:
803A(数组的首地址 )
2,取下标值 i,将 i与指针变量 p相加,得到地址
8040
3,取地址 8040单元中的内容
int p[5]={1,2,3,4,5};
p[3] = 7;
int a[5]={1,2,3,4,5},*p;
p = a;
p[3] = 7;
电子科技大学通信与信息工程学院数组指针与指针变量的区别
注意:区别数组名 a(数组的指针)和指针变量 p是不同的
对于外部变量,定义与声明必须一致,
外部变量 定义为数组,则外部变量 声明 时也应该声明为数组,而 不能是指针
反之,亦然
file1
int p[5]={1,2,3,4,5};
file2
extern int p[];
main()
{,..
p[3] = 16;
...
}
file1
int p[5]={1,2,3,4,5};
file2
extern int *p;
main()
{,..
p[3] = 16;
...
}
X
电子科技大学通信与信息工程学院指向数组元素的指针变量
例:读程序
#include "stdio.h"
main()
{
int a[10],*p,i;
p = a;
for(i=0; p<a+10; i++){
*p = i;
p++;
}
for(i=0; i<10; i++)
printf("%d,",a[i]);
}
#include "stdio.h"
main()
{
int a[10],*p,i;
for(i=0; i<10; i++)
*(a+i) = i;
for(i=0; i<10; i++)
printf("%d,",p[i]);
}
电子科技大学通信与信息工程学院数组指针的运算
设 int a[5],*p,*p1,*p2;
赋值运算,p = &a; p = NULL; p1 = p2;
注意,数组类型 与 指针类型 匹配
指针 + 整数;指针 - 整数
例,a+i,p+i,p-i
注意:
1,数组名 不能作减法
2,指针的加、减运算只能运用于数组元素的引用
3,下标的取值范围
1 2 3 4 5
803A
int a[5]
电子科技大学通信与信息工程学院数组指针的运算
指针间的减法:求两地址间的距离 (距离:元素个数 )
格式:指针 - 指针
例,&a[4]- &a[1]= 3( a[1]与 a[4]间有 a[2],a[3],a[4]3个元素)
注意:两个指针类型相同,并指向同一个连续存储区域
移动指针( ++,--)
只能针对指针变量,数组名(数组指针) 不行 。
*p++ 等价于 *( p++); *p-- 等价于 *( p--)
*(++p),p先自增 1,再得到 *p的值; (*p)++,p指向的数组元素加 1
1 2 3 4 5
803A
int a[5]
电子科技大学通信与信息工程学院数组指针的运算
指针间的比较运算
依据:指针(地址)的大小
例,&a[4] > &a[2]
注意:
以上运算,一般都是针对数组指针(即针对连续存储空间)
1 2 3 4 5
803A
int a[5]
电子科技大学通信与信息工程学院指向数组元素的指针变量
例:把数组 a的数据复制到数组 b,并输出数组 b
下标法 指针法
#include "stdio.h"
main()
{ int a[5]={1,2,3,4,5};
int b[5]={0},i;
for(i=0; i<5; i++)
b[i] = a[i];
for(i=0; i<5; i++)
printf("b[%d]=%d ",i,b[i]);
printf("\n");
}
#include "stdio.h"
main()
{ int a[5]={1,2,3,4,5};
int b[5]={0},i;
int *p = a,*q = b;
for(i=0; i<5; i++){
*q = *p;
p++;
q++;
}
for(i=0; i<5; i++)
printf("b[%d]=%d ",i,b[i]);
printf("\n");
}
*q++ = *p++;
电子科技大学通信与信息工程学院数组的指针与函数参数
数组作为函数参数有以下 4种情况:
int fun(int x[],int n)
{...}
main()
{
int a[10];
fun(a,10);
..,
}
int fun(int *x,int n)
{,.,}
main()
{
int a[10];
fun(a,10);
..,
}
int fun(int *x,int n)
{...}
main()
{
int a[10],*p = a;
fun(p,10);
..,
}
int fun(int x[],int n)
{...}
main()
{
int a[10],*p = a;
fun(p,10);
..,
}
电子科技大学通信与信息工程学院数组的指针与函数参数
作为函数定义的 形式参数,数组和指针变量可以互换
C语言内部,总是将 数组形参 作为 指针变量 处理的,即在调用函数时,函数实参是一个数组指针,系统将该 指针 拷贝给被调函数的 形参变量,而不是将整个数组拷贝给形参
所以,以上 4种情况可以归纳为 1种
int fun(int *x,int n)
{...}
main()
{
int a[10],*p = a;
fun(p,10)或者 fun(a,10)
..,
}
电子科技大学通信与信息工程学院数组的指针与函数参数
例:编写一个函数求一维整型数组的最大元素及其对应下标
函数分析
已知:数组首地址 p,元素个数 n
返回:下标 k
#include "stdio.h"
int max_array(int *p,int n)
{ int m,i,k=0;
m = *p;
for(i=0; i<n; i++)
if (*(p+i)>m){
m=*(p+i);
k = i;
}
return k;
}
main()
{ int a[5]={1,4,2,9,6};
int ind,*q,i;
for(i=0; i<5; i++)
printf("a[%d]=%d ",i,*(q+i));
ind = max_array(a,5);
printf("max:a[%d]=%d\n",ind,a[ind]);
}
if (p[i]>m){
m = p[i];
k = i;
}
for(i=0; i<n,p++,i++)
if (*p>m){
m = *p;
k = i;
}
电子科技大学通信与信息工程学院
5 4 3 2 1
数组的指针与函数参数
例 (P217例 10.7)将数组 a中 n个整型数反序存放
inverse()函数分析
已知:一维数组,数组元素个数
结果:倒置后的数组
算法:
1,令 p指向数组的开始,q指向数组的结束;
2,交换两个单元的内容
3,p,q两指针向中间靠拢
4,重复 2,3直到 q>=p
1 2 4 5
p qp q
q
p
电子科技大学通信与信息工程学院数组的指针与函数参数
例 (P217例 10.7)
将数组 a中 n个整型数反序存放
#include "stdio.h"
void inverse(int *p,int n)
{ int *q,tmp;
q = p + n - 1;
while (p < q){
tmp = *p;
*p = *q;
*q = tmp;
p++;
q--;
}
}
main()
{ int a[5]={1,2,3,4,5};
int i;
for(i=0; i<5; i++)
printf("a[%d]=%d ",i,a[i]);
inverse(a,5);
printf("\ninverse\n");
for(i=0,i<5; i++)
printf("a[%d]=%d ",i,a[i]);
}
电子科技大学通信与信息工程学院指针与字符串
字符串指针
C语言中,使用字符数组来存放字符串,所以 字符串指针 就是 字符数组的首地址
字符串的特点:以 \0为结束标志
例,char a[]="china";
字符串指针变量
定义,char *指针变量名 ;
例,char str[]="hello",*p1 = str,*p2;
p2 = "It's a test.";
注意:字符串 常量 是存放在 字符数组 中,字符串指针变量指向该数组的首地址区别:
int *p,*q;
p = 123; X
q = &321 X
电子科技大学通信与信息工程学院指针与字符串
例:逆序打印字符串
分析:
指针 q指向字符串首
指针 p指向字符串尾
打印 *p,将 p迁移,直到串首( p == q)
#include "stdio.h"
main()
{
char *q="This is a test";
char *p;
puts(q);
for(p=q; *p!='\0'; p++);
for(p=p-1; q<=p; p--)
putchar(*p);
printf("\n");
}
电子科技大学通信与信息工程学院指针与字符串
例:判断字符串是否是回文(即该字符串顺序与逆序相同,例如,abba”)
分析:
设置标志 flag = 0,
指针 q指向字符串首
指针 p指向字符串尾
判断 *p==*q,q,p向中间靠拢,否则返回 1
#include "stdio.h"
int palindrome(char *q,int n)
{ char *p;
p = q + n – 1;
while (q <= p){
if (*q != *p) return 1;
q++;
p--;
}
return 0;
}
main()
{ char *q="This is a test";
int flag,len;
len = strlen(q);
flag = palindrome(q,len);
if (flag == 0)
printf("%s is palindrome\n",q);
else
printf("%s is not\n",q);
}
电子科技大学通信与信息工程学院字符数组与字符指针变量的比较
例,char str[]="It's a test",*p=str;
存储内容不同
字符数组存放字符串
字符指针变量存的是字符串的首地址
赋值方式不同
字符数组( 定义时除外 )只能对各个元素赋值
字符指针变量赋值为字符串的首地址
例,char str[20],*p;
str = "I love China"; X
p = " I love China ";
电子科技大学通信与信息工程学院字符数组与字符指针变量的比较
例,char str[]="It's a test",*p=str;
存储内容不同
字符数组存放字符串
字符指针变量存的是字符串的首地址
赋值方式不同
字符数组( 定义时除外 )只能对各个元素赋值
字符指针变量赋值为字符串的首地址
例,char str[20],*p;
str = "I love China"; X
p = " I love China ";
It's a test\0
803A
803A
数组 str[]
指针 p
电子科技大学通信与信息工程学院字符数组与字符指针变量的比较
字符数组名不是变量,不能修改;字符指针变量可以修改值
例,char str[] = "test",*p;
str++; X
p++;
字符指针变量也可以用下标形式对字符数组的元素进行引用
例,char *a;
a = "Hello";
printf("%c",a[2]);
电子科技大学通信与信息工程学院