第8章 指针
主要内容 地址和指针的概念 变量的指针和指向变量的指针变量 数组与指针 字符串与指针 指向函数的指针 返回指针值的函数 指针数组和指向指针的指针 有关指针的数组类型和指针运算的小结
本章学习目标: 认识到用地址作为一种数据类型的重要性。 理解指针包括地址和类型两种属性。 掌握指针运算符&和*。 能够通过地址引用调用在被调函数与主调函数之间共享数据。 理解指针和数组的关系。 理解指向函数的指针的用法。
8.1 地址和指针的概念 指针:一个变量的地址。 指针变量:存放某一变量的地址(即指针)。 内存用户数据区 变量 i 变量 j 8.1 地址和指针的概念 内存用户数据区 变量 i 变量 j 变量 i_pointer 3 6 2000 2002 3010 指针:一个变量的地址。 指针变量:存放某一变量的地址(即指针)。
8.2 变量的指针和指向变量的指针变量 定义一个指针变量 定义的一般形式:基类型 *指针变量名; 例 int *p1,*p2; 8.2 变量的指针和指向变量的指针变量 定义一个指针变量 定义的一般形式:基类型 *指针变量名; 基类型:用来指定指针变量可以指向的变量的类型。 将决定指针移动和运算时的移动量。 * :表示该变量为指针类型 例 int *p1,*p2; float *q ; static char *name; 注意: 1、int *p1, *p2; 与 int *p1, p2; 2、指针变量名是p1,p2 ,不是*p1,*p2 3、指针变量只能指向定义时所规定类型的变量 4、指针变量定义后,变量值不确定,应用前必须先赋值
&与*运算符: 10 指针运算符(“间接访问”运算符) 取地址运算符 含义: 取指针所指向变量的内容 含义: 取变量的地址 …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 指针变量 2000 10 i_pointer *i_pointer &i_pointer i i_pointer-----指针变量,它的内容是地址量 *i_pointer----指针的目标变量,它的内容是数据 &i_pointer---指针变量占用内存的地址
指针变量的初始化 一般形式:[存储类型] 数据类型 *指针名=初始地址值; 例 int i; int *p=&i; 赋给指针变量, 一般形式:[存储类型] 数据类型 *指针名=初始地址值; 例 int i; int *p=&i; 赋给指针变量, 不是赋给目标变量 变量必须已说明过 类型应一致 指针变量赋值: int i, j; int *p1, *p2; p1=&i; p2=&j ; i=3; *p1=5; j=6; *p2=8; 例 int i; int *p=&i; int *q=p; 例 int *p=&i; int i; 用已初始化指针变量作初值
指针变量的引用 例8.1 通过指针变量访问整型变量 #include <stdio.h> void main( ) 例8.1 通过指针变量访问整型变量 #include <stdio.h> void main( ) {int a, b, *p1, *p2 ; a=100; b=10; p1=&a; p2=&b; printf(“a=%d, b=%d\ n”,a, b); printf(“* p1=%d, * p2=%d\ n”, *p1, * p2); printf(“&a=%x,& b=%x\ n”,&a, &b); printf(“p1=%x, p2=%x\ n”, p1, p2); printf(“& p1=%x, &p2=%x\ n”, &p1, & p2); } 运行结果: a=100, b=10 *p1=100, *p2=10 &a=ffd4, &b=ffd6 p1=ffd4, p2=ffd6 &p1=ffd8, &p2=ffda 100 10 ffd4 ffd6 a b p1 p2 ffd8 ffda
关于 & 和 * 运算符的进一步说明: * 、&:优先级同为2级, 结合性:从右向左。 关于 & 和 * 运算符的进一步说明: * 、&:优先级同为2级, 结合性:从右向左。 1.若已执行: int a, b, * p1, * p2; p1=&a; p2=&b; a=100; b=10; 则 ① &* p1 &a (p1) &* p2 &b (p2) ②p2=&* p1 p2=&a 2. * & a:先进行&a得a的地址,再对a的地址进行* 运算 即指向a地址所指向的变量,就是a ,其值是100 3. 运算符 * ,++ :优先级为2, 结合性:从右到左 (* p1)++ a++ * p1++ * (p1++) 意即: p1原指向a , 现在指向下一个地址了。 &a p2 p1 10 100 b a &a &b p1 p2 10 100 b a
换后 换前 ffd0 ffd2 ffd4 ffd8 ffd6 p1 p2 p a b #include <stdio.h> void 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(“\na=%d, b=%d\n”, a, b); printf(“max=%d, min=%d\ n”, *p1, *p2); } 运行情况: 5,9 a=5, b=9 max=9,min=5 只交换了指针的值,没有交换变量的值 换后 换前 ffd8 ffd6 5 5 ffd0 ffd2 ffd4 ffd8 ffd6 ffd6 ffd8 20 ffd6 9 9 p1 p2 p a b
指针变量作为函数参数——地址传递 例8.3a 将数从大到小输出 #include <stdio.h> …... 2000 2008 200A 2002 2004 2006 例8.3a 将数从大到小输出 #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); } 变量a 变量b (main) 5 5 9 9 变量temp 变量y 变量x (swap) COPY 9 5 5 运行结果: 5 , 9 5 , 9
#include <stdio.h> void swap(int *p1, int *p2) { int *p; p=p1; 例8.3b 将数从大到小输出 #include <stdio.h> void swap(int *p1, int *p2) { int *p; p=p1; p1=p2; p2=p;} void 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 (swap) 指针p1 指针p2 指针p **** 2002 2000 2000 运行结果:5,9
#include <stdio.h> void main() { void swap(int *p1, int *p2); 例8.3 将数从大到小输出 (使用指针变量作函数参数) #include <stdio.h> void main() { void swap(int *p1, int *p2); 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);} void swap(int *p1, int *p2) { int temp; temp=*p1; *p1=*p2; *p2=temp;} …... 2000 2008 200A 2002 2004 2006 200C 200E 2010 ... 整型变量a 整型变量b (main) 指针pointer_1 指针pointer_2 9 5 地址传递 5 9 2000 2000 2002 2002 (swap) 指针p1 指针p2 整型temp 5 运行情况: 5,9 9,5 问题:函数调用结束后, 分配给p1,p2,temp单元释放否?
#include <stdio.h> void main( ) 例8.5 输入a,b,c3个整数,按从大到小输出 #include <stdio.h> void main( ) {void exchange(int *q1, int *q2, int *q3); int a,b,c,*p1,*p2,*p3; scanf(“%d,%d,%d”,&a,&b,&c); p1=&a; p2=&b; p3=&c; exchange(p1, p2, p3); printf(“\n%d, %d, %d\ n”,a, b, c);} 运行情况: 9 , 0 , 10 10 , 9 , 0 ffbc ffc2 ffc4 ffca ffcc ffce ffd0 ffd2 ffd4 ffd6 ffd8 ffda temp pt1 pt2 q2 q1 q3 a b c p1 p2 p3 9 10 10 9 0 ffd0 ffd2 ffd4 ffd4 0 0 9 10 9 0 void exchange(int *q1, int *q2, int *q3) {void swap(int *pt1, int *pt2); if(*q1 < *q2) swap(q1, q2); if(*q1 < *q3) swap(q1, q3); if(*q2 < *q3) swap(q2, q3);} void swap(int *pt1, int *pt2) {int temp; temp=*pt1; *pt1=*pt2; *pt2=temp;}
8.3 数组与指针 Q1:如何用指针指向数组? Q2:如何用指针访向数组元素?
回顾 数组名代表什么? 如何访向数组元素及地址? int a[5]; // a代表这组数的首地址 指针变量里存放的是什么? int *p; // p里存放的是内存单元地址
Q1:如何用指针指向数组? int a[5]={1,2,3,4,5}, *p; p= ; a &a[2]
P++;//指针移动 P--; *P=5; Q2:如何用指针访向数组元素? int a[5]={1,2,3,4,5}, *p=a;
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)
例8.6 用三种方法输出数组中全部元素的值 ⑴ 下标法: #include <stdio.h> void main() 例8.6 用三种方法输出数组中全部元素的值 ⑴ 下标法: #include <stdio.h> void main() {int a[10]; int i; for(i=0; i<10; i++) scanf("%d",&a[i]); printf("\n"); printf("%d",a[i] );} ⑷ 指针法和指针下标: #include <stdio.h> void main() {int a[10]; int *p,i; for(i=0; i<10; i++) scanf("%d",&a[i]); printf("\n"); for(p=a,i=0; i<10; i++) printf("%d",*(p+i));} ⑵ 用数组名: #include <stdio.h> void main() {int a[10]; int i; for(i=0; i<10; i++) scanf("%d",&a[i]); printf("\n"); printf("%d",*(a+i));} ⑶ 指针法: #include <stdio.h> void main() {int a[10]; int *p,i; for(i=0; i<10; i++) scanf("%d",&a[i]); printf("\n"); for(p=a; p<(a+10); p++) printf("%d",*p );} 运行情况: 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
#include <stdio.h> void main() { int *p,i,a[10]; p=a; 例8.7 用指针变量输出元素 #include <stdio.h> void main() { int *p,i,a[10]; p=a; for(i=0;i<10;i++) scanf(“%d”,p++); printf(“\n”); for(i=0;i<10;i++,p++) printf(“%d”,*p); } 例8.7 用指针变量输出元素 #include <stdio.h> void main() { int *p,i,a[10]; p=a; for(i=0;i<10;i++) scanf(“%d”,p++); printf(“\n”); p=a; /*或者p=&a[0]*/ for(i=0;i<10;i++,p++) printf(“%d”,*p); } 原因? 能正确输出吗? 运行情况: 1 2 3 4 5 6 7 8 9 0 22153 234 0 0 30036 25202 11631 8259 8237 28483 运行情况: 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
编译时arr按指针变量处理,所以,此句与f(int *arr , int n)等价。 用数组名作函数参数 数组名作函数参数 void main( ) {f(int arr[ ], int n); int array[10]; ┆ f(array, 10); } void f(int arr[ ], int n) { arr[0] array[0] ┋ arr[1] array[1] arr[9] array[9] 编译时arr按指针变量处理,所以,此句与f(int *arr , int n)等价。
例8.8 将数组a中n个整数按相反顺序存放。 思路:数组元素头尾对调。四种调用方式。 m=4 ⑴ 实参与形参均用数组 #include <stdio.h> void main() {void inv(int x[ ], int n); int i,a[10]={3,7,9,11,0,6,7,5,4,2}; printf("The original array:\n"); for(i=0;i<10;i++) printf("%d,"a[i]); printf("\n"); inv(a,10); printf("The array has been inverted:\n"); for(i=0;i<10;i++) printf("%d,",a[i]); } void inv(int x[ ], int n) { int temp,i,j,m=(n-1)/2; for(i=0;i<=m;i++) { j=n-1-i; temp=x[i]; x[i]=x[j]; x[j]=temp; } return; 例8.8 将数组a中n个整数按相反顺序存放。 思路:数组元素头尾对调。四种调用方式。 m=4 i j i j i j i j j i 3 7 9 11 0 6 7 5 4 2 0 1 2 3 4 5 6 7 8 9 2 3 4 7 5 9 11 7 6
⑵ 实参用数组,形参用指针变量 #include <stdio.h> void main() {void inv(int *x, int n); int i,a[10]={3,7,9,11,0,6,7,5,4,2}; printf("The original array:\n"); for(i=0;i<10;i++) printf("%d,"a[i]); printf("\n"); inv(a,10); printf("The array has been inverted:\n"); for(i=0;i<10;i++) printf("%d,",a[i]); } void inv(int *x, int n) { int temp,*p,*i,*j,m=(n-1)/2; i=x; j=x+n-1; p=x+m; for(;i<=p;i++,j--) { temp=*i; *i=*j; *j=temp; } return; 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 2 3 i j 4 7 i j 5 9 i j 7 11 j i 6
此句用意? ⑶例8.8 实参与形参均用指针变量 #include <stdio.h> void main() {void inv(int *x, int n); int i,arr[10],*p=arr; printf("The original array:\n"); for(i=0;i<10;i++,p++) scanf("%d",p); p=arr; inv(p,10); printf("The array has been inverted:\n"); for(p=arr;p<arr+10;p++) printf("%d",*p); printf(“\n"); } void inv(int *x, int n) { int *p, m, temp,*i,*j; m=(n-1)/2; i=x; j=x+n-1; p=x+m; for(;i<=p;i++,j--) { temp=*i; *i=*j; *j=temp; } return;} 此句用意?
⑷ 实参用指针变量,形参用数组 #include <stdio.h> void main() {void inv(int x[ ], int n); int i,a[10],*p=a; for(i=0;i<10;i++,p++) scanf("%d",p); p=a; inv(p,10); printf("The array has been inverted:\n"); for(p=arr;p<arr+10;p++) printf("%d ",*p); printf(“\n "); } 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; } return;
为了得到两个结果值,用两个全局变量max和min。 例 从10个数中找出其中最大值和最小值 为了得到两个结果值,用两个全局变量max和min。 ⑴ 实参和形参均用数组 int max, min; /* 全局变量*/ void max_min_value(int array[ ],int n) { int *p, *array_end; array_end=array+n; /*指向数组最后一个元素的后面 */ max=min=*array; /* 相当于max=min=array[0] */ for(p=array+1; p<array_end; p++) /* p指向array[1] */ if(*p > max) max=*p; else if(*p<min) min=*p; } main( ) { int i, number[10]; printf(“enter 10 integer numbers:\ n”); for(i=0;i<10;i++) scanf(“%d”,&number[i]); max_min_value(number,10); printf(“\nmax=%d, min=%d\n”, max, min); }
⑵ 实参和形参均用指针变量 int max, min; /* 全局变量*/ void max_min_value(int *array,int n) { int *p, *array_end; array_end=array+n; /*指向数组最后一个元素的后面 */ max=min=*array; /* 相当于max=min=array[0] */ for(p=array+1; p<array_end; p++) /* 使p指向array[1] */ if(*p > max) max=*p; else if(*p<min) min=*p; } main( ) { int i, number[10],*p; p=number; /* 使p指向number数组 */ printf(“enter 10 integer numbers:\ n”); for(i=0;i<10;i++,p++) scanf(“%d”,p); printf("the 10 integer numbers:\n"); for(p=number,i=0;i<10;i++,p++) printf("%d ",*p); p=number; max_min_value(p,10); printf(“\nmax=%d, min=%d\n”, max, min); }
归纳:用数组做函数参数有如下四种情况: 1、实参形参都用数组名: int a[10]; inv(int x[ ],int n) inv(a,10) { …... } 2、实参用数组名,形参用指针变量: int a[10]; inv(int *x,int n) 3、实参形参都用指针变量: int *p=a; {……} inv(p,10) 4、实参用指针变量,形参用数组名: int *p=a; {…...}
例8.9 用选择法对10个整数排序 ⑴ 实参用指针变量,形参用数组 #include <stdio.h> void main() {void sort(int x[ ], int n); int *p,i,a[10]; p=a; for(i=0;i<10;i++) scanf("%d",p++); p=a; sort(p,10); for(p=a,i=0;i<10;i++) {printf("%d ",*p); p++;} } void sort(int x[ ], int n) { int i,j,k,t; for(i=0;i<=n-1;i++) { k=i; for(j=i+1;j<n;j++) if(x[j]>x[k]) k=j; if(k!=i) {t=x[i];x[i]=x[k];x[k]=t;} } } 例8.9 用选择法对10个整数排序
列元素地址:a[1]+3 *(a+1)+3 &a[1][3] 多维数组与指针 int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}}; 列元素地址 a[2]+0 1 3 5 7 9 11 13 15 17 19 21 23 a[0]+0 a[0]+1 a[0]+2 a[0]+3 a+0 a+1 a+2 a[1]+3 行地址 列元素地址:a[1]+3 *(a+1)+3 &a[1][3]
*(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
指向多维数组的指针变量 指向数组元素的指针变量 例8.12 用指针变量输出数组元素的值 #include <stdio.h> void 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+1)或 (*p)[1] 指向由m个元素组成的一维数组的指针变量 定义形式: 数据类型 (*指针名)[一维数组维数]; 例 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 ( )不能少 int (*p)[4]与int *p[4]不同 可让p指向二维数组某一行 如 int a[3][4], (*p)[4]=a; p[0]+1或 *p+1 p[1]+2或 *(p+1)+2 p的值是一维数组的 首地址,p是行指针 *(*p+1)或 (*p)[1] *(*(p+1)+2) 一维数组指针变量维数和 二维数组列数必须相同
例8.13 输出二维数组任一行任一列元素的值 #include <stdio.h> void main() { static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int (*p)[4], i, j; p=a; scanf(“i=%d, j=%d”, &i, &j); printf(“a[%d][%d]=%d\ n”, i, j, *(*(p+i)+j)); } 1 3 5 7 9 11 13 15 17 19 21 23 a,a+0,p+0 a+1,p+1 a+2,p+2 运行结果: i=1, j=2 a[1][2]=13
8.4 字符串与指针 字符串的表示形式 char string[ ]=“I love China!”; 8.4 字符串与指针 字符串的表示形式 字符串: 用双引号括起的一串字符。 I l 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 char string[ ]=“I love China!”; char *string="I love China!"; string+7
#include <stdio.h> void main( ) { char *string=“I love China!”; 改动后的例8.16 #include <stdio.h> void main( ) { char *string=“I love China!”; printf(“%s\n”,string); string+=7; while(*string) { putchar(*string); string++; } I l o v e C h i string n ! a \0 *string!=‘\0’ string 输出: I love China! China!
用下标法存取字符串中的字符 地址访问: a[ ]复制到b[ ] 例8.18 将字符串a复制为字符串b #include <stdio.h> void main( ) { char a[ ]="I am boy.",b[20]; int i; for(i=0;*(a+i)!='\0';i++) *(b+i)=*(a+i); *(b+i)='\0'; printf("string a is: %s\n",a); printf("string b is: "); for(i=0;b[i]!='\0';i++) printf("%c",b[i]); printf("\n"); } b[i]=a[i] 下标法输出 运行结果: string a is: I am boy. string b is: I am boy.
运行结果: 运行结果: 例8.19 用指针变量实现例8.18 #include <stdio.h> void main( ) b y p1 . \0 p2 例8.19 用指针变量实现例8.18 #include <stdio.h> void main( ) { char a[ ]=“I am boy.”,b[20]; char *p1=a,*p2=b; int i; for( ; *p1!=‘\0’ ; p1++,p2++) {*p2=*p1;} *p2='\0'; printf(“string a is: %s\n”, a ); printf(“string b is: %s\n”, b ); } p1=a; p2=b; p1 p2 p1 p2 运行结果: string a is: I am boy. string b is: I am boy. 运行结果: string a is: string b is:
字符指针作函数参数 例8.20 用函数调用实现字符串的复制 a I m t 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 例8.20 用函数调用实现字符串的复制 ⑴用字符数组作参数 #include <stdio.h> void main() {void copy_string(char from[],char to[]); char a[ ]="I am a teacher."; char b[ ]="You are a student."; printf("string_a=%s\n string_b=%s\n",a,b); printf(“copy string_a to string_b: \n"); copy_string(a,b); /* 数组名作参数是地址传递*/ printf(“\nstring_a=%s\nstring_b=%s\n",a,b); } void copy_string(char from[],char to[]) { int i=0; while(from[i]!='\0') { to[i]=from[i]; i++; } to[i]='\0';
⑵字符指针变量作形参 #include <stdio.h> void main() { void copy_string(char *from,char *to); char *a="I am a teacher."; char *b="You are a student."; printf("string_a=%s\n string_b=%s\n",a,b); printf(“copy string_a to string_b: \n"); 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';
对使用字符指针变量和字符数组的讨论 char *cp; 与 char str[20]; 的区别 str由若干元素组成,每个元素放一个字符;而cp中存放字符串首地址 赋值方式: 字符数组只能对元素赋值。 char str[20]; str=“I love China!”; () 字符指针变量可以用: char *cp; cp=“I love China!”; () 赋初值:char *cp="China!"; 等价 char *cp; cp=“China!”; char str[14]={"China"};不等价char str[14]; str[ ]="China" ()
用指针变量指向的格式字符串代替printf中的格式字符串(可变格式输出函数) char *format; format="a=%d,b=%f\n"; printf(format,a,b); 相对于: printf("a=%d,b=%f\n"a,b); 可以用字符数组实现: char format[ ]="a=%d,b=%f\n";
判断和修正: 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}; () int *p=&a; char str[10],*cp; int a[10],*p; str=“Hello”; () cp=“Hello!”; () a={1,2,3,4,5}; () p={1,2,3,4,5}; () str[10]=“Hello”; a[10]={1,2,3,4,5}; p=&a;
8.5 指向函数的指针 用函数指针变量调用函数。 函数指针:函数在编译时被分配的入口地址,用函数名表示。函数指针指向的是程序代码存储区。 8.5 指向函数的指针 用函数指针变量调用函数。 函数指针:函数在编译时被分配的入口地址,用函数名表示。函数指针指向的是程序代码存储区。 函数指针变量定义形式: 数据类型 (*指针变量名)( ); 如 int (*p)(); max …... 指令1 指令2 函数指针变量赋值:如 p=max; ( )不能省 int (*p)() 与 int *p()不同 专门存放函数入口地址 可指向返回值类型相同的不同函数 函数返回值的数据类型 函数调用形式: c=max(a,b); c=(*p)(a,b); 对函数指针变量pn, p++, p--无意义 函数指针变量指向的函数必须有函数说明
例8.22 求a和b中的大者 ⑴一般方法: ⑵通过指针变量访问函数 #include <stdio.h> void main() { int max(int , int); int (*p)(int , int); 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);} ⑴一般方法: #include <stdio.h> void 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);}
接受search函数返回的指向某学生0门课程地址的指针变量 #include <stdio.h> void main() { float score[ ][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}}; float *search(float (*pointer)[4],int n); float *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)); printf("\n");} float *search(float (*pointer)[4], int n) { float *pt; pt=*(pointer+n-1); return(pt);} 8.6 返回指针值的函数 函数定义形式: 类型标识符 *函数名(参数表); 例 int *f(int x, int y) 接受search函数返回的指向某学生0门课程地址的指针变量 例8.25:有若干学生成绩,要求输入学生序号后,能输出 该学生全部成绩。用指针函数实现。 p p p p pointer pointer+1 34 78 90 66 56 89 67 88 60 70 80 score数组 指向实型变量的指针变量 形参指向一维数组的指针变量
< > 例8.26 找出例8.25中有不及格课程的学生及其学号 #include <stdio.h> 例8.26 找出例8.25中有不及格课程的学生及其学号 #include <stdio.h> void main() { float score[ ][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}}; float *search(float (*pointer)[4]); float *p; int i,j; for(i=0;i<3;i++) { p=search(score+i); if (p==*(score+i)) { printf("No. %d scores: ",i); for(j=0;j<4;j++) printf("%5.2f\t",*(p+j));} } } float *search(float (*pointer)[4]) { int i; float *pt; pt=NULL; for(i=0;i<4;i++) if(*(*pointer+i)<60) {pt=*pointer;break;} return(pt); } *pointer pt (当有不及格时) NULL pt (当无不及格时) pointer score+1 34 78 90 66 56 89 67 88 60 70 80 score数组 < >
例 int *p[4]; 8.7 指针数组和指向指针的指针 指针数组的概念 用于处理二维数组或多个字符串 定义:数组中的元素均为指针变量 8.7 指针数组和指向指针的指针 用于处理二维数组或多个字符串 指针数组的概念 定义:数组中的元素均为指针变量 定义形式:[存储类型] 类型名 *数组名[数组长度]; 例 int *p[4]; 指针所指向变量的数据类型 区分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 赋值: main() { int b[2][3],*pb[2]; pb[0]=b[0]; pb[1]=b[1]; ┊ }
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”}; 二维数组与指针数组区别: 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”}; 指针数组元素的作用相当于二维数组的行名 但指针数组中元素是指针变量 二维数组的行名是地址常量 二维数组存储空间固定 字符指针数组相当于可变列长的二维数组
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] 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) 1 2 3 4 6
k k j j j j i=0 例8.26 对字符串排序(简单选择排序) name Follow me name[0] name[1] 例8.26 对字符串排序(简单选择排序) 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
例8.26 对字符串排序(简单选择排序) #include <stdio.h> #include <string.h> void main() { void sort(char *name[ ],int n); void print (char *name[ ],int n); char *name[]={"Follow me","BASIC", "Great Wall","FORTRAN","Computer design"}; 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;} } } void print(char *name[ ], int n) { int i ; for(i=0; i<n; i++) printf("%s\n",name[ i ]; } 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 定义形式:[存储类型] 数据类型 **指针名; 如:char **p 如果:char *name[ ]={"Follow me"……}; char **p; p=name+2; 则:printf("%s\n",*p); 输出:Great Wall 指针本身的存储类型 最终目标变量的数据类型 name name[0] name[1] name[2] name[3] name[4] name Great Wall FORTRAN Computer Follow me BASIC p
二级指针与指针数组的关系 int **p 与 int *q[10] 指针数组名是二级指针常量 p=q; p+i 是q[i]的地址 指针数组作形参,int *q[ ]与int **q完全等价;但作为变量定义两者不同 系统只给p分配能保存一个指针值的内存区;而给q分配10块内存区,每块可保存一个指针值
指针数组作main函数的形参(掌握) main函数的一般调用形式: main( ) main函数的有参数调用形式 : main( int argc , char *argv[ ] ) 其中( int argc , char *argv[ ] 是形参,可以多个: int argc; 命令名和所有参数个数之和 char *argv[ ]; 各元素是指针,分别指向各参数字符串 在实际运行程序时,实参和命令名(C程序编译和连接后得到的可执行文件名)以命令行形式给出: 命令名 参数1 参数2 …… 参数n [回车]
main(int argc, char *argv[]) { while(argc>1) { ++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!
main(int argc, char *argv[]) { while(argc-->0) printf("%s\n",*argv++); } argv[0] argv[1] argv[2] char *argv[] world test hello argv argc=3 例如: C:\TC> test[.exe] hello world! 运行结果:test hello world!
malloc(100) calloc(3, 50) free(p) realloc(p,50) 8.8 动态内存分配与指向它的指针变量 8.8 动态内存分配与指向它的指针变量 1、内存动态分配相关函数(头文件:stdlib.h) 示例 作用 malloc(100) 申请一个100个字节大小的连续空间 calloc(3, 50) 申请3个50个字节大小的连续空间 free(p) 释放指针变量所指向的动态空间 realloc(p,50) 将p所指向的动态空间大小改变为50个字节
2、void指针类型 定义时不指定指向哪一类数据 用动态存储分配函数时返回void指针 它的值赋给另一指针变量时,要强制类型转换 例:char *p1; void *p2; ┊ p1=(char *)p2; 或p2=(void *)p1; void *fun(char ch1,char ch2) 返回“空类型”地址 P1=(char *)fun(ch1,ch2); 引用时要类型转换
8.9 有关指针的数据类型和指针运算的小结 有关指针的数据类型的小结 定 义 含 义 int i; 定义整型变量i int *p ; 8.9 有关指针的数据类型和指针运算的小结 有关指针的数据类型的小结 定 义 含 义 int i; 定义整型变量i int *p ; p为指向整型数据的指针变量 int a[n] ; 定义有n个元素的整型数组a int *p[n] ; 定义由n个指向整型数据的指针元素组成的指针数组p int (*p)[n] ; p为指向含n个元素的一维数组的指针变量 int *p( ) ; p为返回一个指针的函数,该指针指向整型数据 int (*p)( ) ; p为指向函数的指针,该函数返回一个整型值 int **p ; p为一个指针变量,它指向一个指向整型数据的指针变量 int f( ) ; f为可返回整型函数值的函数
指针运算小结 ⑴ 指针变量加(减)一个整数 如:p++;p--;p+i;p-i;p+=i;p-=i等,加减的值与类型有关。 ⑵ 指针变量赋值 p=&a (将变量a的地址赋给p) p=array;(将数组array首地址赋给p) p=&array[i];(将数组array第i个元素的地址赋给p) p=max;(max为已定义的函数,将max函数的入口地址赋给p) p1=p2;(p1和p2都是指针变量,将p2的值赋给p1) (3) 指针变量不指向任何变量,即取空值。表示为:p=NULL; (4) 两个指针变量可以相减 如果两个指针变量指向同一个数组为元素,则两个指针变量值之差是 两个指针之间的元素个数。但p1+p2并无实际意义。 (5) 两个指针变量比较 如果两个指针变量指向同一个数组为元素,则可以进行地址比较。
例 下列定义的含义 ⑴ int *p[3]; ⑵ int (*p)[3]; ⑶ int *p(int); ⑷ int (*p)(int); ⑸ int *(*p)(int); ⑹ int (*p[3])(int); ⑺ int *(*p[3])(int); 指针数组 指向一维数组的指针 返回指针的函数 指向函数的指针,函数返回int型变量 函数指针数组,函数返回int型变量 函数指针数组,函数返回int型指针
本章小结 指针C语言的一个重要概念,也是C语言的一个重要特色。正确灵活地运用指针可以有效地表达复杂的数据结构,动态地分配内存以及方便地处理字符串,有效而方便地使用数组,可以直接处理内存等。