第九章 指针 C程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值
9.1 指针的概念 变量与地址 i k 程序中: int i; float k; 变量是对程序中数据 存储空间的抽象 9.1 指针的概念 变量与地址 内存中每个字节有一个编号-----地址 …... 2000 2001 2002 2005 内存 1 2003 程序中: int i; float k; i 编译或函数调用时为其分配内存单元 k 变量是对程序中数据 存储空间的抽象
指针:可根据变量地址找到变量的存储单元,地址起到指示性作用,一个变量的地址称为该变量的指针 地址:内存中每一个字节有一个编号 指针:可根据变量地址找到变量的存储单元,地址起到指示性作用,一个变量的地址称为该变量的指针 指针变量:专门存放变量地址的变量叫~ 指针 …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 变量的内容 变量的地址 2000 指针变量 指针变量 变量 变量地址(指针) 变量值 指向 地址存入
i_pointer &i &(*i_pointer) i *i_pointer *(&i) &与*运算符 含义 含义: 取变量的地址 单目运算符 优先级: 2 结合性:自右向左 含义: 取指针所指向变量的内容 单目运算符 优先级: 2 结合性:自右向左 两者关系:互为逆运算 理解 2000 10 i_pointer *i_pointer &i_pointer i …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 指针变量 i_pointer &i &(*i_pointer) i *i_pointer *(&i) i_pointer = &i = &(*i_pointer) i = *i_pointer = *(&i) i_pointer-----指针变量,它的内容是地址量 *i_pointer----指针的目标变量,它的内容是数据 &i_pointer---指针变量占用内存的地址
指针变量与其所指向的变量之间的关系 在C程序中用*表示"指向" i *i_pointer 2000 3 &i i_pointer 则*i_pointer是i_pointer所指向的变量 i *i_pointer 2000 i_pointer *i_pointer 3 i 2000 &i i_pointer i=3; *i_pointer=3;
直接访问与间接访问 直接访问:按变量地址存取变量值 间接访问:通过存放变量地址的变量去访问变量 <例>i=3; ----直接访问 指针变量 …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 <例>i=3; ----直接访问 20 3 <例>*i_pointer=20; -----间接访问
10 <例> k=i; k=*i_pointer; <例> k=i; --直接访问 指针变量 …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 整型变量k 10
9.2 变量与指针 指针变量的定义 指针变量与其所指向的变量之间的关系 一般形式: [存储类型] 数据类型 *指针名; 3 变量i 2000 i_pointer *i_pointer i &i i=3; *i_pointer=3 3 变量i 2000 i_pointer *i_pointer i &i i=3; *i_pointer=3 指针变量的定义 一般形式: [存储类型] 数据类型 *指针名; <例> int *p1,*p2; float *q ; static char *name; 表示定义指针变量 不是‘*’运算符 合法标识符 指针变量本身的存储类型 指针的目标变量的数据类型 注意: 1、int *p1, *p2; 与 int *p1, p2; 2、指针变量名是p1,p2 ,不是*p1,*p2 3、指针变量只能指向定义时所规定类型的变量 4、指针变量定义后,变量值不确定,应用前必须先赋值
指针变量的初始化 一般形式:[存储类型] 数据类型 *指针名=初始地址值; <例> int i; int *p=&i; 一般形式:[存储类型] 数据类型 *指针名=初始地址值; <例> int i; int *p=&i; 赋给指针变量, 不是赋给目标变量 变量必须与已说明过的 类型应一致 <例> int i; int *p=&i; int *q=p; <例> int *p=&i; int i; 用已初始化指针变量作初值 <例> main( ) { int i; static int *p=&i; .............. } () 不能用auto变量的地址 去初始化static型指针
指针变量必须先赋值,再使用 <例>main( ) { int i=10; int *p; *p=i; printf(“%d”,*p); } …... 2000 2004 2006 2005 整型变量i 10 指针变量p 2001 2002 2003 随机 危险! <例>main( ) { int i=10,k; int *p; p=&k; *p=i; printf(“%d”,*p); }
零指针与空类型指针 p指向地址为0的单元,这意味着,指针并不指向任何地址。 零指针:(空指针) #define NULL 0 定义:指针变量值为零 表示: int * p=0; p指向地址为0的单元,这意味着,指针并不指向任何地址。 #define NULL 0 int *p=NULL: <例> int *p; ...... while(p!=NULL) { ...… } <例>char *p1; void *p2; p1=(char *)p2; p2=(void *)p1; p=NULL与未对p赋值不同 用途: 避免指针变量的非法引用 在程序中常作为状态比较 表示不指定p是指向哪一种 类型数据的指针变量 void *类型指针 表示: void *p; 使用时要进行强制类型转换
<例> 指针的概念 main() { int a; int *pa=&a; 10 a=10; …... f86 f8a f8c f8b 整型变量a 10 指针变量pa f87 f88 f89 运行结果: a:10 *pa:10 &a:f86(hex) pa:f86(hex) &pa:f88(hex) main() { int a; int *pa=&a; a=10; printf("a:%d\n",a); printf("*pa:%d\n",*pa); printf("&a:%x(hex)\n",&a); printf("pa:%x(hex)\n",pa); printf("&pa:%x(hex)\n",&pa); }
<例> 输入两个数,并使其从大到小输出 <例> 输入两个数,并使其从大到小输出 main() { int *p1,*p2,*p,a,b; scanf("%d,%d",&a,&b); p1=&a; p2=&b; if(a<b) { p=p1; p1=p2; p2=p;} printf("a=%d,b=%d\n",a,b); printf("max=%d,min=%d\n",*p1,*p2); } …... 指针变量p1 指针变量p 2000 2008 2002 2004 2006 指针变量p2 整型变量b 整型变量a 2006 2008 2006 2008 2006 5 9 运行结果:a=5,b=9 max=9,min=5
指针变量作为函数参数——地址传递 特点:共享内存,“双向”传递 <例> 将数从大到小输出 swap(int x,int y) 5 …... 2000 2008 200A 2002 2004 2006 变量a 变量b (main) swap(int x,int y) { int temp; temp=x; x=y; y=temp; } main() { int a,b; scanf("%d,%d",&a,&b); if(a<b) swap(a,b); printf("\n%d,%d\n",a,b); 5 5 9 9 变量temp 变量y 变量x (swap) COPY 9 5 5
指针变量作为函数参数——地址传递 特点:共享内存,“双向”传递 <例> 将数从大到小输出 swap(int x,int y) …... 2000 2008 200A 2002 2004 2006 变量a 变量b (main) swap(int x,int y) { int temp; temp=x; x=y; y=temp; } main() { int a,b; scanf("%d,%d",&a,&b); if(a<b) swap(a,b); printf("\n%d,%d\n",a,b); 5 9 值传递 运行结果:5, 9
也可以这样描述: <例> 交互两数。 swap(int x,int y) { int temp; temp=x; x=y; y=temp; } main() { int a,b; a=5;b=9; if(a<b) swap(a,b); printf("\n%d,%d\n",a,b); a 5 5 b 9 9 x 9 y 5 temp 形参、实参是简单变量时,单向值传递
形参、实参是指针变量时,实现“双向”传递 实参本身没有改变,改变的是实参所指向的对象 <例>交换两数。 地址传递 swap(int *q1, int *q2) { int q; q=*q1; *q1=*q2; *q2=q; } main() { int a,b; int *p1,*p2; a=5; b=9; p1=&a; p2=&b; if(a<b)swap(p1,p2); printf("\n%d,%d\n",a,b); p1 a p2 b 5 &a 9 &b 9 5 q2 q1 &a &b 形参、实参是指针变量时,实现“双向”传递 q 5 实参本身没有改变,改变的是实参所指向的对象 运行结果:9,5
改变的是形参本身,并不能影响实参,当然也不能影响实参所指向的对象 <例> 交换两数。 swap(int *q1, int *q2) { int *q; q=q1; q1=q2; q2=q; } main() { int a,b; int *p1,*2; a=5; b=9; p1=&a; p2=&b; if(a<b)swap(p1,p2); printf("\n%d,%d\n",a,b); p2 p1 a b 5 &a &b 9 q2 q1 &b &b &a &a q &a 改变的是形参本身,并不能影响实参,当然也不能影响实参所指向的对象 运行结果:5,9
但是,指针q未赋值,就取*运算,*q=….. <例> 交换两数。 swap(int *q1, int *q2) { int *q; *q=*q1; *q1=*q2; *q2=*q; } main() { int a,b; int *p1,*2; a=5; b=9; p1=&a; p2=&b; if(a<b)swap(p1,p2); printf("\n%d,%d\n",a,b); a p2 p1 b 5 &a 9 &b 9 5 q2 q1 q 但是,指针q未赋值,就取*运算,*q=….. 危险 运行结果:9,5
(1) swap(int *q1, int *q2) { int *q; q=q1; q1=q2; q2=q; } (2) swap(int *q1, int *q2) { int *q; *q=*q1; *q1=*q2; *q2=*q; } 或者更改:int k, *q=&k; main() { int a,b; int *p1,*p2; a=5; b=9; p1=&a; p2=&b; if(a<b)swap(p1,p2); printf("\n%d,%d\n",a,b); } (3) swap(int *q1, int *q2) { int q; q=*q1; *q1=*q2; *q2=q; }
9.3 一维数组与指针 指向数组元素的指针变量 例 int array[10]; int *p; p=&array[0]; // p=array; 或 int *p=&array[0]; 或 int *p=array; array[0] array[1] array[2] array[3] array[9] ... 整型指针p &array[0] p 数组名是表示数组首地址的地址常量
指针的运算 如 int i, *p; p=1000; () i=p; () 指针变量的赋值运算 p=&a; (将变量a地址p) p=array; (将数组array首地址p) p=&array[i]; (将数组元素地址p) p1=p2; (指针变量p2值p1) 不能把一个整数p,也不能把p的值整型变量 指针变量与其指向的变量具有相同数据类型
指针的运算 指针变量的赋值运算 指针的算术运算: 1 <例>int a[10]; int *p1=&a[2]; pi p id (i为整型数,d为p指向的变量所占字节数) p++, p--, p+i, p-i, p+=i, p-=i等 若p1与p2指向同一数组,p1-p2=两指针间元素个数(p1-p2)/d p1+p2 无意义 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] a数组 p p+1,a+1 p+i,a+i p+9,a+9 <例> p指向float数,则 p+1 p+1 4 <例> p指向int型数组,且p=&a[0]; 则p+1 指向a[1] 1 <例> int a[10]; int *p=&a[2]; p++; *p=1; <例>int a[10]; int *p1=&a[2]; int *p2=&a[5]; 则:p2-p1=? 3
指针的运算 指针变量的赋值运算 指针的算术运算 指针变量的关系运算 若p1和p2指向同一数组,则 p1<p2 表示p1指的元素在前 p==NULL或p!=NULL
a[i] p[i] *(p+i) *(a+i) 数组元素表示方法 [] 变址运算符 a[i] *(a+i) a[0] a[1] a[2] a[3] a[9] ... a a+9 a+1 a+2 地址 元素 下标法 a[0] a[1] a[2] a[3] a[9] ... p p+9 p+1 p+2 地址 元素 指针法 *p *(p+1) *(p+2) *(p+9) *a *(a+1) *(a+2) *(a+9) p[0] p[1] p[2] p[9] a[i] p[i] *(p+i) *(a+i)
<例>数组元素的引用方法 main() { int a[5],*pa,i; for(i=0;i<5;i++) a[i]=i+1; pa=a; printf("*(pa+%d):%d\n",i,*(pa+i)); printf("*(a+%d):%d\n",i,*(a+i)); printf("pa[%d]:%d\n",i,pa[i]); printf("a[%d]:%d\n",i,a[i]); } a[0] a[1] a[2] a[3] a[4] pa 1 2 3 4 5
<例>int a[]={1,2,3,4,5,6,7,8,9,10},*p=a,i; 数组元素地址的正确表示: (A)&(a+1) (B)a++ (C)&p (D)&p[i] 数组名是地址常量 p++,p-- () a++,a-- () a+1, *(a+2) ()
<例>注意指针变量的运算 例 void main() { int a []={5,8,7,6,2,7,3}; int y,*p=&a[1]; y=(*--p)++; printf(“%d ”,y); printf(“%d”,a[0]); } 5 8 7 6 2 3 1 4 a p 6 p 输出:5 6
for(i=0;i<7;i++,p++) printf("%d",*p); } <例> 注意指针的当前值 main() { int i,*p,a[7]; p=a; for(i=0;i<7;i++) scanf("%d",p++); printf("\n"); for(i=0;i<7;i++,p++) printf("%d",*p); } 5 8 7 6 2 3 1 4 a p p p p p p=a; p p p 指针变量可以指到数组后的内存单元
数组名作函数参数 数组名作函数参数,是地址传递 数组名作函数参数,实参与形参的对应关系 实参 形参 数组名 指针变量
<例>将数组a中的n个整数按相反顺序存放 void convert(int x[], int n) { int t,i=0,j=n-1; for(i=0;i<j; i++,j--) { 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}; convert(a,10); printf("reverted:\n"); for(i=0;i<10;i++) printf("%d,",a[i]); printf("\n"); i j i j i j i j j i x 3 7 9 11 0 6 7 5 4 2 0 1 2 3 4 5 6 7 8 9 a 2 3 4 7 5 9 11 7 6 实参与形参均用数组
<例>将数组a中的n个整数按相反顺序存放 void convert(int *p, int n) { int t,*q; for(q=p+n-1; p<q; p++, q--) { t=*p; *p=*q; *q=t; } main() { int i,a[10]={3,7,9,11,0,6,7,5,4,2}; convert(a,10); printf("reverted:\n"); for(i=0;i<10;i++) printf("%d,",a[i]); printf("\n"); 3 7 9 11 6 5 4 2 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] p a数组 a q a q 2 3 p q p<q; 4 7 p q 5 9 p q 7 11 p 6 q 实参用数组,形参用指针变量
<例>将数组a中的n个整数按相反顺序存放 void convert(int *p, int n) { int t,*q; for(q=p+n-1; p<q; p++, q--) { t=*p; *p=*q; *q=t; } main() { int i,a[10]={3,7,9,11,0,6,7,5,4,2},*pa=a; convert(pa,10); printf("reverted:\n"); for(i=0;i<10;i++) printf("%d,", *(pa+i)); printf("\n"); } 实参与形参均用指针变量 for( ; pa<a+10; pa++) printf(“%d”, *pa); for(i=0; i<10; i++,pa++) printf(“%d”, *pa); for(i=0; i<10; i++) printf(“%d”, *pa++);
一级指针变量与一维数组的关系 int *p 与 int q[10] 数组名是指针(地址)常量 p=q; p+i 是q[i]的地址 数组元素的表示方法:下标法和指针法, 即若p=q, 则 p[i] q[i] *(p+i) *(q+i) 形参数组实质上是指针变量,即int q[ ] int *q 在定义指针变量(不是形参)时,不能把int *p 写成int p[]; 系统只给p分配能保存一个指针值的内存区(一般2字节);而给q分配2*10字节的内存区
9.4 多维数组与指针 对于一维数组: (1)数组名array表示数组的首地址,即array[0]的地址; (2)数组名array是地址常量 (3)array+i是元素array[i]的地址 (4)array[i] *(array+i) array int array[10];
二维数组理解 a[0] <例> int a[3][4]; a[1] a[2] 1 4 5 2 3 a[0][1] a[0][2] 1 4 5 2 3 a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[0][0] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3] a[1][2] 6 7 10 11 8 9 a[0] a[1] a[2] 二维数组a是由3个元素组成 <例> int a[3][4]; 2016 17 2018 19 2020 21 2022 23 2008 9 2010 11 2012 13 2014 15 2000 1 2002 3 2004 5 2006 7 a[0] a[1] a[2] 行名 每个元素a[i]由包含4个元素 的一维数组组成
int a[3][4]; a a+1 a+2 行指针与列指针 *(a[0]+1) *(*(a+0)+1) 2000 2000 a[0] 2008 2016 a a+1 a+2 2000 2002 2008 2010 2016 2018 a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] a[0][2] a[0][3] a[1][2] a[1][3] a[2][2] a[2][3] a[0]+1 a[1]+1 a[2]+1 *(a+0)+1 *(a+1)+1 *(a+2)+1 对于二维数组: (1)a是数组名, 包含三个元素 a[0],a[1],a[2] (2)每个元素a[i] 又是一个一维 数组,包含4个 元素 基类型
int a[3][4]; a a+1 a+2 对二维数组 int a[3][4],有 2000 2008 2016 2002 2010 2018 a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] a[0][2] a[0][3] a[1][2] a[1][3] a[2][2] a[2][3] a a+1 a+2 对二维数组 int a[3][4],有 a-----二维数组的首地址,即第0行的首地址 a+i-----第i行的首地址 a[i] *(a+i)------第i行第0列的元素地址 a[i]+j *(a+i)+j -----第i行第j列的元素地址 *(a[i]+j) *(*(a+i)+j) a[i][j] a+i=&a[i]=a[i]=*(a+i) =&a[i][0], 值相等,含义不同 a+i &a[i],表示第i行首地址,指向行 a[i] *(a+i) &a[i][0],表示第i行第0列元素地址,指向列
int a[3][4]; 地址表示: (1) a+1 (2) &a[1][0] (3) a[1] (4) *(a+1) 行指针 列指针 地址表示: (1) &a[1][2] (2) a[1]+2 (3) *(a+1)+2 (4)&a[0][0]+1*4+2 二维数组元素表示形式: (1)a[1][2] (2)*(a[1]+2) (3)*(*(a+1)+2) (4)*(&a[0][0]+1*4+2)
*(a[1]+2),*(*(a+1)+2),a[1][2] 表示形式 含义 地址 a 二维数组名,数组首地址 a[0],*(a+0),*a 第0行第0列元素地址 a+1 第1行首地址 a[1],*(a+1) 第1行第0列元素地址 a[1]+2,*(a+1)+2,&a[1][2] 第1行第2列元素地址 *(a[1]+2),*(*(a+1)+2),a[1][2] 第1行第2列元素值 2000 2008 2012 13
p 二维数组的指针变量 main() { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; 指向二维数组元素的指针变量——列指针变量 int a[3][4]; a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] a[0][2] a[0][3] a[1][2] a[1][3] a[2][2] a[2][3] p <例> 指向二维数组元素的列指针变量 main() { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int *p; for(p=a[0];p<a[0]+12;p++) { if((p-a[0])%4==0) printf("\n"); printf("%4d ",*p); } p=*a; p=&a[0][0]; p=*(a+0); p=a;
二维数组的指针变量 p的值是一维数组的 首地址,p是行指针 ( )不能少 int (*p)[4]与int *p[4]不同 指向二维数组元素的指针变量——列指针变量 指向一维数组的指针变量——行指针变量 定义形式: 数据类型 (*指针名)[一维数组维数]; 例 int (*p)[4]; 可让p指向二维数组某一行 如 int a[3][4], (*p)[4]=a; p的值是一维数组的 首地址,p是行指针 ( )不能少 int (*p)[4]与int *p[4]不同 一维数组指针变量维数和 二维数组列数应该相同
a a+1 a+2 p p+1 p+2 p[0]+1或 *p+1 p[1]+2或 *(p+1)+2 *(*p+1)或 (*p)[1] int a[3][4]; a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] a[0][2] a[0][3] a[1][2] a[1][3] a[2][2] a[2][3] a a+1 a+2 p p+1 p+2 p[0]+1或 *p+1 p[1]+2或 *(p+1)+2 *(*p+1)或 (*p)[1] *(*(p+1)+2)
p p p <例>行指针变量举例 main() int a[3][4]; a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] a[0][2] a[0][3] a[1][2] a[1][3] a[2][2] a[2][3] p main() { static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int i,j,(*p)[4]; for(p=a,i=0;i<3;i++,p++) for(j=0;j<4;j++) printf("%d ",*(*p+j)); printf("\n"); } p p[0][j] p p=a[0]; p=*a; p=&a[0][0]; p=&a[0];
printf("%d,%d\n",*((int *)p),*q); <例> 二维数组与指针运算 main() { int a[3][4]={{1,2,3,4},{3,4,5,6},{5,6,7,8}}; int i; int (*p)[4]=a,*q=a[0]; for(i=0;i<3;i++) { if(i==0) (*p)[i+i/2]=*q+1; else p++,++q; } printf("%d,",a[i][i]); printf("%d,%d\n",*((int *)p),*q); p q p q p q 1 2 3 4 5 6 7 8 2 下一节 运行结果:2,4,7,5,3
若int a[3][4]; int (*p1)[4]=a; int *p2=a[0]; 二维数组的指针作函数参数 用指向变量的指针变量 用指向一维数组的指针变量 用二维数组名 实参 形参 数组名int x[][4] 指针变量int (*q)[4] 指针变量int (*q)[4] 数组名a 指针变量p1 若int a[3][4]; int (*p1)[4]=a; int *p2=a[0]; 指针变量p2 指针变量int *q
<例>3个学生各学4门课,计算总平均分,并输出第n个学生成绩 函数说明 void average(float *p,int n) { float *p_end, sum=0,aver; p_end=p+n-1; for(;p<=p_end;p++) sum=sum+(*p); aver=sum/n; printf("average=%5.2f\n",aver); } void search(float (*p)[4], int n) { int i; printf(" No.%d :\n",n); for(i=0;i<4;i++) printf("%5.2f ",*(*(p+n)+i)); main() { void average(float *p,int n); void search(float (*p)[4],int n); float score[3][4]= {{65,67,79,60},{80,87,90,81}, {90,99,100,98}}; average(*score,12); search(score,2); } 列指针 p 行指针 float p[][4] p 65 52 79 60 80 87 90 81 99 100 98 p[n][i]
<例>3个学生各学4门课,计算总平均分,并查找一门以上课不及格学生, 输出其各门课成绩 void search(float (*p)[4], int n) { int i,j,flag; for(j=0;j<n;j++) { flag=0; for(i=0;i<4;i++) if(*(*(p+j)+i)<60) flag=1; if(flag==1) { printf("No.%d is fail,his scores are:\n",j+1); printf("%5.1f ",*(*(p+j)+i)); printf("\n"); } main() { void search(float (*p)[4], int n); float score[3][4]={{...},{...},{...}}; search(score,3); 65 52 79 60 80 87 90 81 99 100 98 p p[j][i]
二维数组与一维数组指针变量的关系 如 int a[5][10] 与 int (*p)[10]; 二维数组名是一个指向有10个元素的一维数组的指针常量 p=a+i 使 p指向二维数组的第i行 *(*(p+i)+j) a[i][j] 二维数组形参实际上是行指针变量, 即 int p[ ][10] int (*p)[10] 变量定义(不是形参)时两者不等价 系统只给p分配能保存一个指针值的内存区(一般2字节);而给a分配2*5*10字节的内存区
9.5 字符串与指针 字符串表示形式 用字符数组实现 <例>main( ) o v e C h i string[0] string[1] string[2] string[3] string[4] string[5] string[6] string[7] string[8] string[9] string string[10] string[11] string[12] string[13] n ! a \0 <例>main( ) { char string[]=“I love China!”; printf(“%s\n”,string); printf(“%s\n”,string+7); }
字符串表示形式 用字符数组实现 用字符指针实现 字符指针初始化:把字符串首地址赋给string char *string; l o v e C h i string n ! a \0 字符指针初始化:把字符串首地址赋给string char *string; string=“I love China!”; <例> main( ) { char *string=“I love China!”; printf(“%s\n”,string); string+=7; while(*string) { putchar(string[0]); string++; } string *string!=0
注意:其它类型的数组,是不能用数组名来一次性输出它的全部元素的,只能逐个元素输出。 int a[5]={1,2,3,4,5}; printf("%d\n",a); ...... char a[]="good"; char *p=a; printf("%s\n",a); printf("%s\n",p); 下面判断正确的是( )。 A. char *s="girl"; 等价于 char *s; *s="girl"; B. char s[10]={"girl"}; 等价于 char s[10]; s[10]={"girl"}; C. char *s="girl"; 等价于 char *s; s="girl"; D. char s[4]= "boy", t[4]= "boy"; 等价于 char s[4]=t[4]= "boy" C
字符串指针作函数参数 void copy_string(char from[],char to[]) { int i=0; e c h \0 r . from b y u a r s t n d e to o . \0 I a e c h \0 r . t m void copy_string(char from[],char to[]) { int i=0; while(from[i]!='\0') { to[i]=from[i]; i++; } to[i]='\0'; main() { char a[]="I am a teacher."; char b[]="You are a student."; printf("string_a=%s\n string_b=%s\n",a,b); copy_string(a,b); printf("\nstring_a=%s\nstring_b=%s\n",a,b); <例>用函数调用实现字符串复制 void copy_string(char *from,char *to) { for(;*from!='\0';from++,to++) *to=*from; *to='\0'; } main() { char *a="I am a teacher."; char *b="You are a student."; printf("string_a=%s\nstring_b=%s\n",a,b); copy_string(a,b); printf("\nstring_a=%s\nstring_b=%s\n",a,b); (1)用字符数组作参数 (2)用字符指针变量作参数
字符指针变量与字符数组 char *cp; 与 char str[20]; str由若干元素组成,每个元素放一个字符;而cp中存放字符串首地址 char str[20]; str=“I love China!”; () char *cp; cp=“I love China!”; () str是地址常量;cp是地址变量 cp接受键入字符串时,必须先开辟存储空间 <例>char str[10]; scanf(“%s”,str); () 而 char *cp; scanf(“%s”, cp); () 改为: char *cp,str[10]; cp=str; scanf(“%s”,cp); ()
字符串与数组关系 字符串用一维字符数组存放 字符数组具有一维数组的所有特点 数组名是指向数组首地址的地址常量 数组元素的引用方法可用指针法和下标法 数组名作函数参数是地址传递等 区别 存储格式:字符串结束标志 赋值方式与初始化 输入输出方式:%s %c scanf(“%s”,str); printf(“%s”,str); gets(str); puts(str); char str[]={“Hello!”}; () char str[]=“Hello!”; () char str[]={‘H’,‘e’,‘l’,‘l’,‘o’,‘!’}; () char *cp=“Hello”; () int a[]={1,2,3,4,5}; () int *p={1,2,3,4,5}; () char str[10],*cp; int a[10],*p; str=“Hello”; () cp=“Hello!”; () a={1,2,3,4,5}; () p={1,2,3,4,5}; ()
9.6 函数与指针 函数指针:函数在编译时被分配的入口地址,用函数名表示 指向函数的指针变量 定义形式: 数据类型 (*指针变量名)(); 9.6 函数与指针 函数指针:函数在编译时被分配的入口地址,用函数名表示 max …... 指令1 指令2 指向函数的指针变量 定义形式: 数据类型 (*指针变量名)(); 如 int (*p)(); 函数指针变量赋值:如p=max; ( )不能省 int (*p)() 与 int *p()不同 专门存放函数入口地址 可指向返回值类型相同的不同函数 函数返回值的数据类型 函数调用形式: c=max(a,b); c=(*p)(a,b); c=p (a,b); 对函数指针变量pn, p++, p--无意义 函数指针变量指向的函数必须有函数说明
<例>用函数指针变量调用函数,比较两个数大小 main() { int max(int ,int); int a,b,c; scanf("%d,%d",&a,&b); c=max(a,b); printf("a=%d,b=%d,max=%d\n",a,b,c); } int max(int x,int y) { int z; if(x>y) z=x; else z=y; return(z); main() { int max(int ,int), (*p)(); int a,b,c; p=max; scanf("%d,%d",&a,&b); c=(*p)(a,b); printf("a=%d,b=%d,max=%d\n",a,b,c); } int max(int x,int y) { int z; if(x>y) z=x; else z=y; return(z);
返回指针值的函数 返回指针值的函数(简称指针函数)的定义格式如下: 函数类型 *函数名([形参表]) 函数类型 *函数名([形参表]) int *f1(int *x,int *y) { if(*x>*y) return x; else return y; } main() { int a=2,b=3; int *p; p=f1(&a, &b); printf("%d\n",*p); a b p 2 3 2002# ×× 2000# 2002# x 2000# 2002# y 输出:3 <例>写一个函数,求两个int型变量中居于较大值的变量的地址
9.7 指针数组 指针数组 定义:数组中的元素为指针变量 定义形式:[存储类型] 数据类型 *数组名[数组长度说明]; 9.7 指针数组 如果在一个数组中,若其中的所有元素均为指针类 型的数据,则称为指针数组。也就是说,指针数组中的 每一个元素都相当于一个指针变量。 指针数组 定义:数组中的元素为指针变量 定义形式:[存储类型] 数据类型 *数组名[数组长度说明]; 例 int *p[4]; int *p[3]; int a[3]={1,3,5}; 区分int *p[4]与int (*p)[4] 指针所指向变量的数据类型 p[0]=&a[0]; p[0] p[1] p[2] 1 3 5 a[0] a[1] a[2] p[1]=&a[1]; p[2]=&a[2];
&a a &b b p[0] p[1] p[2] p[0] p[1] p[2] main() {int *p[3],a=2,b=3; p[1]=&b; p[2]=NULL; ................ } 2 a &a &b p[0] p[1] p[2] 3 b main() {char *p[]={"Follow", "BASIC", "FORTRAN"}; ....................... } p[0] p[1] p[2] F o l w \0 B A S I C O R T N p 指针数组
{ int b[2][3],*pb[ ]={b[0],b[1]}; …….. } 指针数组赋值与初始化 赋值: main() { int b[2][3],*pb[2]; pb[0]=b[0]; pb[1]=b[1]; …….. } int *pb[2] pb[0] pb[1] int b[2][3] 1 2 3 4 6 初始化: main() { int b[2][3],*pb[ ]={b[0],b[1]}; …….. } int *pb[2] pb[0] pb[1] int b[2][3] 1 2 3 4 6
p[0]=a; p[1]=b; p[2]=c; p[3]=NULL; …….. } 或: { char *p[4]; 指针数组赋值与初始化 L i s p \0 F o r t r a n \0 B a s i c \0 p[0] p[1] p[2] p[3] 赋值: main() { char a[]="Fortran"; char b[]="Lisp"; char c[]="Basic"; char *p[4]; p[0]=a; p[1]=b; p[2]=c; p[3]=NULL; …….. } 或: { char *p[4]; p[0]= "Fortran"; p[1]= "Lisp"; p[2]= "Basic"; p[3]=NULL; 初始化: main() { char *p[]={"Fortran", "Lisp", "Basic",NULL}; …….. } L i s p \0 F o r t r a n \0 B a s i c \0 p[0] p[1] p[2] p[3]
printf(“%c”, *p[i]); printf(“%s\n”, p[i]); main() { int i; char *p[4]={“aa”, “bbb”, “c”, “dddd” }; for(i=0;i<4;i++) } printf(“%c”, *p[i]); printf(“%s\n”, p[i]); p[0] p[1] p[2] p[3] a \0 aa b \0 bbb c \0 c dddd a b c d d \0
二维数组与指针数组区别: 指针数组元素的作用相当于二维数组的行名 二维数组存储空间固定 但指针数组中元素是指针变量 char name[5][9]={“gain”,“much”,“stronger”, “point”,“bye”}; g a i n \0 s t r o n g e r \0 p o i n t \0 m u c h \0 b y e \0 g a i n \0 s t r o n g e r \0 p o i n t \0 m u c h \0 name[0] name[1] name[2] name[3] name[4] b y e \0 char *name[5]={“gain”,“much”,“stronger”, “point”,“bye”}; 二维数组存储空间固定 字符指针数组相当于可变列长的二维数组 分配内存单元=数组维数*2+各字符串长度 指针数组元素的作用相当于二维数组的行名 但指针数组中元素是指针变量 二维数组的行名是地址常量
<例>对字符串排序(简单选择排序) main() { void sort(char *name[],int n), print(char *name[],int n); char *name[]={"Follow me","BASIC", "Great Wall","FORTRAN","Computer "}; int n=5; sort(name,n); print(name,n); } void sort(char *name[],int n) { char *temp; int i,j,k; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if(k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp;} name[0] name[1] name[2] name[3] name[4] name Great Wall FORTRAN Computer Follow me BASIC k k j j j j i=0
<例>对字符串排序(简单选择排序) main() { void sort(char *name[],int n), print(char *name[],int n); char *name[]={"Follow me","BASIC", "Great Wall","FORTRAN","Computer "}; int n=5; sort(name,n); print(name,n); } void sort(char *name[],int n) { char *temp; int i,j,k; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if(k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp;} name name[0] Follow me k name[1] BASIC j name[2] Great Wall k j name[3] FORTRAN k j name[4] Computer i=1
<例>对字符串排序(简单选择排序) main() { void sort(char *name[],int n), print(char *name[],int n); char *name[]={"Follow me","BASIC", "Great Wall","FORTRAN","Computer "}; int n=5; sort(name,n); print(name,n); } void sort(char *name[],int n) { char *temp; int i,j,k; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if(k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp;} name name[0] Follow me name[1] BASIC k name[2] Great Wall k j name[3] FORTRAN j name[4] Computer i=2
<例>对字符串排序(简单选择排序) main() { void sort(char *name[],int n), print(char *name[],int n); char *name[]={"Follow me","BASIC", "Great Wall","FORTRAN","Computer "}; int n=5; sort(name,n); print(name,n); } void sort(char *name[],int n) { char *temp; int i,j,k; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if(k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp;} name name[0] Follow me name[1] BASIC name[2] Great Wall k name[3] FORTRAN k j name[4] Computer i=3
<例>对字符串排序(简单选择排序) main() { void sort(char *name[],int n), print(char *name[],int n); char *name[]={"Follow me","BASIC", "Great Wall","FORTRAN","Computer "}; int n=5; sort(name,n); print(name,n); } void sort(char *name[],int n) { char *temp; int i,j,k; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(strcmp(name[k],name[j])>0) k=j; if(k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp;} name name[0] Follow me name[1] BASIC name[2] Great Wall name[3] FORTRAN name[4] Computer
C:\TC> copy[.exe] source.c temp.c 指针数组作main函数的参数 命令行:在操作系统状态下,为执行某个程序而键入的一行字符 命令行一般形式:命令名 参数1 参数2………参数n C:\TC> copy[.exe] source.c temp.c 带参数的main函数形式: 有3个字符串参数的命令行 main(int argc, char *argv[]) { ……… } 命令行实参 main(形参) 系统自动调用 main函数时传递 命令行参数传递 元素指向命令行参数 中各字符串首地址 形参名任意 命令行中参数个数 第一个参数: main所在的可执行文件名
main(int argc, char *argv[]) { while(argc-->0) <例> 输出命令行参数 main(int argc, char *argv[]) { while(argc-->0) printf("%s\n",*argv++); } /*test.c*/ main(int argc, char *argv[]) { while(argc>1) { ++argv; printf("%s\n",*argv); --argc; } argv[0] argv[1] argv[2] char *argv[] world test hello argv argc=3 1. 编译、链接test.c,生成可执行文件test.exe 2. 在DOS状态下运行(test.exe所在路径下) 例如: C:\TC> test[.exe] hello world! 运行结果:hello world! 运行结果:test hello world!
9.8多级指针 定义: 指向指针的指针 一级指针:指针变量中存放目标变量的地址 <例>int *p; int i=3; 单级间接寻址 二级指针:指针变量中存放一级指针变量的地址 <例> int **p1; int *p2; int i=3; p2=&i; p1=&p2; **p1=5; p1 &p2 &i 3 P2(指针变量) i(整型变量) 二级指针 一级指针 目标变量 二级间接寻址
定义形式:[存储类型] 数据类型 **指针名; 定义形式:[存储类型] 数据类型 **指针名; 如 char **p; *p是p间接指向对象的地址 **p是p间接指向对象的值 指针本身的存储类型 最终目标变量的数据类型 <例> int i=3; int *p1; int **p2; p1=&i; p2=&p1; **p=5; i p1 p2 3 &i &p1 **p2, *p1 *p2 <例>int i, **p; p=&i; ()//p是二级指针,不能用变量地址为其赋值 多级指针 例 三级指针 int ***p; 四级指针 char ****p;
char *name[]={"hello","good","world","bye",""}; p=name+1; <例>用二级指针处理字符串 用*p可输出地址(%o或%x), 也可用它输出字符串(%s) #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++); } name[0] name[1] name[2] name[3] name[4] char *name[5] world bye \0 hello good name p *(p++) p 运行结果: 644 : good bye
二级指针与指针数组的关系 int **p 与 int *q[10] 指针数组名是二级指针常量 p=q; p+i 是q[i]的地址 指针数组作形参,int *q[ ]与int **q完全等价;但作为变量定义两者不同 系统只给p分配能保存一个指针值的内存区;而给q分配10块内存区,每块可保存一个指针值
指针的数据类型 定义 含义 int i; 定义整型变量i p为指向整型数据的指针变量 int *p; int a[n]; int *p[n]; int (*p)[n]; int f(); int *p(); int (*p)(); int **p; 定义整型变量i p为指向整型数据的指针变量 定义含n个元素的整型数组a n个指向整型数据的指针变量组成的指针数组p p为指向含n个元素的一维整型数组的指针变量 f为返回整型数的函数 p为返回指针的函数,该指针指向一个整型数据 p为指向函数的指针变量,该函数返回整型数 p为指针变量,它指向一个指向整型数据的指针变量 指针的数据类型
<例>下列定义的含义 (1)int *p[3]; (2)int (*p)[3]; (3)int *p(int); (4)int (*p)(int); (5)int *(*p)(int); (6)int (*p[3])(int); (7)int *(*p[3])(int); 指针数组 指向一维数组的指针 返回指针的函数 指向函数的指针,函数返回int型变量 指向函数的指针,函数返回int 型指针 函数指针数组,函数返回int型变量 函数指针数组,函数返回int型指针