第六章 指针 指针的概念 指针变量 指针与数组 指针与函数 返回指针值的函数
前言 C程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值
6.1 指针的概念 变量与地址 i k 程序中: int i; float k; 变量是对程序中数据 存储空间的抽象 6.1 指针的概念 变量与地址 内存中每个字节有一个编号-----地址 …... 2000 2001 2002 2005 内存 2003 程序中: int i; float k; i 编译或函数调用时为其分配内存单元 k 变量是对程序中数据 存储空间的抽象
指针与指针变量 指针:一个变量的地址 指针变量:专门存放变量地址的变量叫~ 10 变量的内容 变量的地址 指针变量 变量地址(指针) …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 变量的内容 变量的地址 2000 指针变量 指针变量 变量 变量地址(指针) 变量值 指向 地址存入
&与*运算符 两者关系:互为逆运算 理解 10 i_pointer &i &(*i_pointer) 含义: 取变量的地址 单目运算符 优先级: 2 结合性:自右向左 含义: 取指针所指向变量的内容 单目运算符 优先级: 2 结合性:自右向左 …... 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---指针变量占用内存的地址
2000 10 i_pointer *i_pointer &i_pointer i
直接访问与间接访问 直接访问:按变量地址存取变量值 间接访问:通过存放变量地址的变量去访问变量 例 i=3; -----直接访问 10 指针变量 …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 例 i=3; -----直接访问 20 3 例 *i_pointer=20; -----间接访问
例子图解 10 例 k=i; 例 k=i; --直接访问 k=*i_pointer; k=*i_pointer; --间接访问 10 指针变量 …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 整型变量k 10
6.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 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); }
零指针与空类型指针 零指针:(空指针) 定义:指针变量值为零 表示: 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; 使用时要进行强制类型转换
printf("a=%d,b=%d\n",a,b); printf("max=%d,min=%d\n",*p1,*p2); } 例 输入两个数,并使其从大到小输出 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) …... 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) { int temp; 5 temp=x; x=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
int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); 例 将数从大到小输出 swap(int *p1, int *p2) { int p; p=*p1; *p1=*p2; *p2=p; } main() { int a,b; int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); pointer_1=&a; pointer_2=&b; if(a<b)swap(pointer_1,pointer_2); printf("\n%d,%d\n",a,b); …... 2000 2008 200A 2002 2004 2006 200C 200E 2010 ... 整型变量a 整型变量b (main) 指针pointer_1 指针pointer_2 9 5 5 9 2000 2000 2002 2002 COPY (swap) 指针p1 指针p2 整型p 5
int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); 例 将数从大到小输出(1) swap(int *p1, int *p2) { int p; p=*p1; *p1=*p2; *p2=p; } main() { int a,b; int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); pointer_1=&a; pointer_2=&b; if(a<b)swap(pointer_1,pointer_2); printf("\n%d,%d\n",a,b); …... 2000 2008 200A 2002 2004 2006 200C 200E 2010 ... 整型变量a 整型变量b (main) 指针pointer_1 指针pointer_2 9 5 5 9 地址传递 2000 2002 运行结果:9,5
int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); 例 将数从大到小输出(2) …... 2000 2008 200A 2002 2004 2006 200C 200E 2010 ... swap(int *p1, int *p2) { int *p; *p=*p1; *p1=*p2; *p2=*p; } main() { int a,b; int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); pointer_1=&a; pointer_2=&b; if(a<b) swap(pointer_1,pointer_2); printf("\n%d,%d\n",a,b); 整型变量a 整型变量b (main) 指针pointer_1 指针pointer_2 5 9 int x; int *p=&x;x; 9 9 2000 2000 2002 编译警告! 结果不对! 2002 COPY (swap) 指针p1 指针p2 指针p **** 假设2000 指针变量在使用前 必须赋值! 运行结果:9,9
int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); 例 将数从大到小输出(3) …... 2000 2008 200A 2002 2004 2006 200C 200E 2010 ... /*ch9_32.c*/ swap(int x,int y) { int t; t=x; x=y; y=t; } main() { int a,b; int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); pointer_1=&a; pointer_2=&b; if(a<b) swap(*pointer_1,*pointer_2); printf("\n%d,%d\n",a,b); 整型a 整型b (main) pointer_1 pointer_2 5 值传递 9 2000 5 2002 9 COPY (swap) 整型x 整型b 整型t 9 5 5 运行结果:5,9
int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); 例 将数从大到小输出(4) swap(int *p1, int *p2) { int *p; p=p1; p1=p2; p2=p; } main() { int a,b; int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); pointer_1=&a; pointer_2=&b; if(a<b) swap(pointer_1,pointer_2); printf("%d,%d",*pointer_1,*pointer_2); …... 2000 2008 200A 2002 2004 2006 200C 200E 2010 ... 整型a 整型b (main) pointer_1 pointer_2 5 地址传递 9 2000 2000 2002 2002 COPY (swap) 指针p1 指针p2 指针p **** 2002 2000 2000 运行结果:5,9
指针变量和基类型 指针类型变量 这种变量的内容不再是常规意义上的数值,而是某个内存单元组的首地址定位和其占有字节数的多少,指针变量具有定位指向的控制能力。 基类型 把指针变量所控制的数据结构类型称为“基类型”。注意:指针值具有可列性,指针变量的自增或自减运算是以它基类型的字节数为数据单位 。
如 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的值整型变量 指针变量与其指向的变量具有相同数据类型
pi p id (i为整型数,d为p指向的变量所占字节数) p++, p--, p+i, p-i, p+=i, p-=i等 指针的算术运算: 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 1
例 p指向float数,则 p+1 p+1 4 例 p指向int型数组,且p=&a[0]; 则p+1 指向a[1] 例 int a[10]; int *p1=&a[2]; int *p2=&a[5]; 则:p2-p1=3; 例 int a[10]; int *p=&a[2]; p++; *p=1;
指针变量的关系运算 若p1和p2指向同一数组,则 p1<p2 表示p1指的元素在前 p1>p2 表示p1指的元素在后 p==NULL或p!=NULL
6.3 指针与数组 数组名是代表整个数组首地址的符号常量,若将数组名赋给基类型相同的指针变量,该指针变量就指向数组的首地址。 一维数组 它的基类型就是任一分量的数据类型(列指向); 二维数组 它的基类型可以是任一分量的数据类型(列指向),也可以用数组的“行”作为基类型(行指向)。
指向数组元素的指针变量 p array[0] array[1] 例 int array[10]; int *p; array[2] ... 整型指针p &array[0] p 例 int array[10]; int *p; p=&array[0]; // p=array; 或 int *p=&array[0]; 或 int *p=array; 数组名是表示数组首地址的地址常量
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]; 例 注意指针变量的运算 5 8 7 6 2 3 1 4 a p 6 例 void main() { int a []={5,8,7,6,2,7,3}; int y,*p=&a[1]; y=(*--p)++; printf(“%d”,y); } p 输出: 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 inv(int *x, int 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] x p=x+m a数组 j i 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 array has been reverted:\n"); for(i=0;i<10;i++) printf("%d,",a[i]); printf("\n"); } 2 3 i j 4 7 i j 5 9 i j 7 11 j i 6 实参用数组,形参用指针变量
一级指针变量与一维数组的关系 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字节的内存区
关于指针变量的运算 1、p++ (或p+=1) 使p指向下一个元素的地址。 2、若有*p++ 由于++ 和 * 同优先级,其结合方向为 main() { int a[8]={2,4,6,8,10,12,14,16}, i, *p; for(p=a, i=0; i<8; i++) printf(“%d,”, *p++); }
3、*(p++) 与 *(++p) 作用不同。 2 2 4 *(++p)与*++p 等价 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] 2 4 6 8 10 12 14 16 p *(p++) 2 p *(++p) 4 *(++p)与*++p 等价
4、(*p)++ 表示是 p 所指向的元素值加 1。 2 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] 2 4 6 8 10 12 14 16 3 p (*p)++
5、若有以下操作,则有: 2 *(p--) 5 3 *(++p) 6 *(--p) 3 4 6 8 10 12 14 16 p a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] *(p--) 5 2 4 6 8 10 12 14 16 3 *(++p) 6 p *(--p) 3
练习 若有以下定义,则其值为 3 的表达式是: int a[ ]={1,2,3,4,5,6,7,8}, *p; 1 2 3 4 5 6 7 p=a; a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] 1 2 3 4 5 6 7 8 p A) p+=2, *(p++) B) p+=2, *++p C) p+=3, *p++ D) p+=2, ++*p
练习 以下程序的输出结果是: main() { int a[ ]={1,2,3,4,5,6}, *p; 1 2 3 4 5 6 6 p=a; *(p+3)+=2; printf(“%d,%d\n”, *p, *(p+3)); } a[0] a[1] a[2] a[3] a[4] a[5] A) 0,5 B) 1,5 C) 0,6 D) 1,6 1 2 3 4 5 6 6 p
指针指向二维数组(数组指针) 二维数组的行是一种构造类型,若某指针指向它,就是“行指针”。行成为该指针所指的基类型,也是它的移动单位,定义数组指针的语法形式是: 类型名 (*指针变量名)[常量表达式]; 考察组合运算符( )[ ],属于同一优先级,自左向右,先处理圆括号,里面是指针变量,再处理右边的[ ],表示所指对象是个构造类型,元素个数由常量表达式决定,基类型是行,所以是行指针,行中的任一列元素的基类型由最前面的类型名给出。
二维数组的地址 指针与二维数组 对于一维数组: (1)数组名array表示数组的首地址,即array[0]的地址; int array[10]; 对于一维数组: (1)数组名array表示数组的首地址,即array[0]的地址; (2)数组名array是地址常量 (3)array+i是元素array[i]的地址 (4)array[i] *(array+i)
int a[3][4]; 行指针与列指针(1) 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个 元素 基类型
行指针与列指针(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]; a a+1 a+2 2000 a[0][0] a[0] 2002 a[0][1] a[0][2] a[0][3] 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
*(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]; 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 *p; for(p=a[0];p<a[0]+12;p++) { if((p-a[0])%4==0) printf("\n"); printf("%4d ",*p); }
a a+1 a+2 p p+1 p+2 p[0]+1或 *p+1 p[1]+2或 *(p+1)+2 p的值是一维数组的 首地址,p是行指针 定义形式: 数据类型 (*指针名)[一维数组维数]; 例 int (*p)[4]; 指向一维数组的指针变量 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的值是一维数组的 首地址,p是行指针 ( )不能少 int (*p)[4]与int *p[4]不同 *(*p+1)或 (*p)[1] *(*(p+1)+2) 可让p指向二维数组某一行 如 int a[3][4], (*p)[4]=a; 一维数组指针变量维数和 二维数组列数必须相同
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
6.4 指针与字符串 字符串表示形式 用字符数组实现 例 main( ) { char string[]=“I love China!”; \0 例 main( ) { char string[]=“I love China!”; printf(“%s\n”,string); printf(“%s\n”,string+7); }
字符指针初始化:把字符串首地址赋给string char *string; string=“I love China!”; 用字符指针实现 I 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
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}; ()
6.5 指针与函数 函数指针:函数在编译时被分配的入口地址,用函数名表示 指向函数的指针变量 定义形式: 数据类型 (*指针变量名)(); 6.5 指针与函数 函数指针:函数在编译时被分配的入口地址,用函数名表示 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--无意义 函数指针变量指向的函数必须有函数说明
用函数指针变量作函数参数 例 用函数指针变量作参数,求最大值、最小值和两数之和 void main() { int a,b,max(int,int), min(int,int),add(int,int); void process(int,int,int (*fun)()); scanf("%d,%d",&a,&b); process(a,b,max); process(a,b,min); process(a,b,add); } prvoidocess(int x,int y,int (*fun)()) { int result; result=(*fun)(x,y); printf("%d\n",result); max(int x,int y) { printf(“max=”); return(x>y?x:y); min(int x,int y) { printf(“min=”); return(x<y?x:y); add(int x,int y) { printf(“sum=”); return(x+y);
float *search(float (*pointer)[4],int n), *p; int i,m; 8.6 返回指针值的函数 main() { float score[][4]={{60,70,80,90}, {56,89,67,88},{34,78,90,66}}; float *search(float (*pointer)[4],int n), *p; int i,m; printf("Enter the number of student:"); scanf("%d",&m); printf("The scores of No.%d are:\n",m); p=search(score,m); for(i=0;i<4;i++) printf("%5.2f\t",*(p+i)); } float *search(float (*pointer)[4], int n) { float *pt; pt=*(pointer+n); return(pt); 函数定义形式: 类型标识符 *函数名(参数表); 例 int *f(int x, int y) 例 指针函数实现:有若干学生成绩,要求输入学生序号后, 能输出其全部成绩 p p p p pointer pointer+1 34 78 90 66 56 89 67 88 60 70 80 score数组
例 写一个函数,求两个int型变量中居于较大值的变量的地址(1) 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); …... 2000 2008 200A 2002 2004 2006 变量a 变量b (main) 指针变量p COPY 2 3 ** 指针变量y 指针变量x (f1) 2000 2002
例 写一个函数,求两个int型变量中居于较大值的变量的地址(2) int *f3(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); …... 2000 2008 200A 2002 2004 2006 2 变量a 变量b (main) 3 指针变量p ** 2002
例 写一个函数,求两个int型变量中居于较大值的变量的地址(3) int *f3(int x,int y) { if(x>y) return &x; else return &y; } main() { int a=2,b=3; int *p; p=f3(a, b); printf("%d\n",*p); …... 2000 2008 200A 2002 2004 2006 变量a 变量b (main) 指针变量p COPY 2 3 ** 变量y 变量x (f3) 2 3
例 写一个函数,求两个int型变量中居于较大值的变量的地址(4) int *f3(int x,int y) { if(x>y) return &x; else return &y; } main() { int a=2,b=3; int *p; p=f3(a,b); printf("%d\n",*p); …... 2000 2008 200A 2002 2004 2006 2 变量a 变量b (main) 3 指针变量p ** 200A 不能返回形参或局部变量 的地址作函数返回值
指针数组和多级指针 用于处理二维数组或多个字符串 指针数组 指针数组赋值与初始化 指针本身的存储类型 指针所指向变量的数据类型 赋值: 定义:数组中的元素为指针变量 定义形式:[存储类型] 数据类型 *数组名[数组长度说明]; 例 int *p[4]; 指针数组赋值与初始化 指针本身的存储类型 指针所指向变量的数据类型 赋值: 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 区分int *p[4]与int (*p)[4] 初始化: 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]
二维数组与指针数组区别: 指针数组元素的作用相当于二维数组的行名 二维数组存储空间固定 但指针数组中元素是指针变量 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 name[0] name[1] name[2] name[3] name[4] 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 b y e \0 char *name[5]={“gain”,“much”,“stronger”, “point”,“bye”}; 二维数组存储空间固定 字符指针数组相当于可变列长的二维数组 分配内存单元=数组维数*2+各字符串长度 指针数组元素的作用相当于二维数组的行名 但指针数组中元素是指针变量 二维数组的行名是地址常量
for(j=0;j<3;j++,pb[i]++) printf("b[%d][%d]:%2d\n",i,j,*pb[i]); } 例 用指针数组处理二维数组 main() { int b[2][3],*pb[2]; int i,j; for(i=0;i<2;i++) for(j=0;j<3;j++) b[i][j]=(i+1)*(j+1); pb[0]=b[0]; pb[1]=b[1]; for(j=0;j<3;j++,pb[i]++) printf("b[%d][%d]:%2d\n",i,j,*pb[i]); } int *pb[2] pb[0] pb[1] int b[2][3] 1 2 3 4 6 b[0][0] *pb[0] b[0][1] *(pb[0]+1) b[0][2] *(pb[0]+2) b[1][0] *pb[1] b[1][1] *(pb[1]+1) b[1][2] *(pb[1]+2)
k k j j j j i=0 例 对字符串排序(简单选择排序)图解1 main() 例 对字符串排序(简单选择排序)图解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[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
例 对字符串排序(简单选择排序)图解2 k j k j k j i=1 main() 例 对字符串排序(简单选择排序)图解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 k name[1] BASIC j name[2] Great Wall k j name[3] FORTRAN k j name[4] Computer i=1
k k j j i=2 例 对字符串排序(简单选择排序)图解3 main() 例 对字符串排序(简单选择排序)图解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 k name[2] Great Wall k j name[3] FORTRAN j name[4] Computer i=2
例 对字符串排序(简单选择排序)图解4 k k j i=3 main() 例 对字符串排序(简单选择排序)图解4 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
例 对字符串排序(简单选择排序)图解5 main() 例 对字符串排序(简单选择排序)图解5 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
多级指针 定义: 指向指针的指针 一级指针:指针变量中存放目标变量的地址 例 int *p; int i=3; p=&i; *p=5; 单级间接寻址 二级指针:指针变量中存放一级指针变量的地址 例 int **p1; int *p2; int i=3; p2=&i; p1=&p2; **p1=5; p1 &p2 &i 3 P2(指针变量) i(整型变量) 二级指针 一级指针 目标变量 二级间接寻址
多级指针定义形式 定义形式:[存储类型] 数据类型 **指针名; 如 char **p; *p是p间接指向对象的地址 定义形式:[存储类型] 数据类型 **指针名; 如 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;
#include <stdio.h> void swap(int *r,int *s) { int *t; t=r; r=s; 例 一级指针与二级指针图解1 #include <stdio.h> void swap(int *r,int *s) { int *t; t=r; r=s; s=t; } main() { int a=1,b=2,*p,*q; p=&a; q=&b; swap(p,q); printf("%d,%d\n",*p,*q); 2000 2008 200A 2002 2004 2006 1 2 变量a 变量b (main) 指针变量p 指针变量q COPY 指针变量s 指针变量r (swap) 指针变量t 2002 2000 2002 2000 2000
#include <stdio.h> void swap(int *r,int *s) { int *t; t=r; r=s; 例 一级指针与二级指针图解2 #include <stdio.h> void swap(int *r,int *s) { int *t; t=r; r=s; s=t; } main() { int a=1,b=2,*p,*q; p=&a; q=&b; swap(p,q); printf("%d,%d\n",*p,*q); 2000 2008 200A 2002 2004 2006 1 2 变量a 变量b (main) 指针变量p 指针变量q 输出: 1,2
#include <stdio.h> void swap(int *r,int *s) { int *t; t=r; r=s; 例 一级指针与二级指针图解3 a b p q #include <stdio.h> void swap(int *r,int *s) { int *t; t=r; r=s; s=t; } main() { int a=1,b=2,*p,*q; p=&a; q=&b; swap(p,q); printf("%d,%d\n",*p,*q); a b p q r s a b p q s r a b p q 输出: 1,2
#include <stdio.h> void swap(int **r,int **s) { int *t; t=*r; 例 一级指针与二级指针图解4 #include <stdio.h> void swap(int **r,int **s) { int *t; t=*r; *r=*s; *s=t; } main() { int a=1,b=2,*p,*q; p=&a; q=&b; swap(&p,&q); printf("%d,%d\n",*p,*q); 2000 2008 200A 2002 2004 2006 1 2 变量a 变量b (main) 指针变量p 指针变量q 2002 COPY 2000 二级指针s 二级指针r (swap) 指针变量t 2006 2004 2000
#include <stdio.h> void swap(int **r,int **s) { int *t; t=*r; 例 一级指针与二级指针图解5 #include <stdio.h> void swap(int **r,int **s) { int *t; t=*r; *r=*s; *s=t; } main() { int a=1,b=2,*p,*q; p=&a; q=&b; swap(&p,&q); printf("%d,%d\n",*p,*q); 2000 2008 200A 2002 2004 2006 1 2 变量a 变量b (main) 指针变量p 指针变量q 2002 2000 输出: 2,1
#include <stdio.h> void swap(int **r,int **s) { int *t; t=*r; 例 一级指针与二级指针图解6 a b p q #include <stdio.h> void swap(int **r,int **s) { int *t; t=*r; *r=*s; *s=t; } main() { int a=1,b=2,*p,*q; p=&a; q=&b; swap(&p,&q); printf("%d,%d\n",*p,*q); a b r s p q a b r s p q b a p q 输出: 2,1
C:\TC> copy[.exe] source.c temp.c 命令行参数 命令行:在操作系统状态下,为执行某个程序而键入的一行字符 命令行一般形式:命令名 参数1 参数2………参数n C:\TC> copy[.exe] source.c temp.c 带参数的main函数形式: 有3个字符串参数的命令行 main(int argc, char *argv[]) { ……… } 命令行参数传递 命令行实参 main(形参) 系统自动调用 main函数时传递 命令行中参数个数 形参名任意 元素指向命令行参数 中各字符串首地址 第一个参数: main所在的可执行文件名
n个指向整型数据的指针变量组成的指针数组p 定义 含义 int i; 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为指针变量,它指向一个指向整型数据的指针变量 指针的数据类型