430 likes | 657 Views
第 10 章 指 针. 指针是C语言中的重要概念,也是C语言的重要特色。使用指针,可以使程序更加简洁、紧凑、高效。. §10.1 指针的概念. 一 . 内存单元的内容、内存单元地址 : 内存区域划分为一个一个的内存单元,一个内存单元 可以存放一字节数据,每一个内存单元都有一个地址。 例: int i=3 ; char j=‘s’;. 地址. 内存单元. 3. 2002 2004 3000. 2000 2001. 变量 i 变量 j. 编译时为变量 i 分配 2000 和 2001 两
E N D
第10章 指 针 指针是C语言中的重要概念,也是C语言的重要特色。使用指针,可以使程序更加简洁、紧凑、高效。
§10.1 指针的概念 • 一.内存单元的内容、内存单元地址: 内存区域划分为一个一个的内存单元,一个内存单元 可以存放一字节数据,每一个内存单元都有一个地址。 例:int i=3; char j=‘s’; 地址 内存单元 3 2002 2004 3000 2000 2001 变量i 变量j 编译时为变量i分配2000和2001两 个字节的内存单元,为j分配2003 一个字节的内存单元 75 2003 通常把内存单元的地址称为指针. 2003 指针变量j-pointer
二.内存单元的访问: j • 直接访问:通过变量名访问。 char j=‘s’; printf(“%c”, j ); • 间接访问:通过该变量的指针来访问。 p=&j; • 称:指针变量p指向变量j,是变量j的指针。 75 2000 p j 75 2000
指针(pointer):是一个变量的地址, 是常量。 • 指针变量:是指取值为地址的变量,其 值是另一个变量的地址。 • 要引用指针,必须定义指针。 • 定义指针,是为了通过指针访问内存单元。 • 一个数组或函数占有一段连续内存单元,假如将一个数组或函数的首地址赋给指针变量,通过指针就可以找到数组或函数。
§10.2 变量的指针 一、指针变量的定义 类型标识符* 标识符 例如:int *cm • “标识符”是指针变量名 • “*”表示定义指针变量 • “类型标识符”表示该指针变量所指向的变量类型。 • 指针是一个变量的地址,为了表示指针与它指向变量的地址之间的关系,用*表示指向.
a 2000 5 b 2002 ... ... p 3000 &a 2000 二、指针变量的引用 • 未经赋值的指针变量不能用。 • 指针变量只能赋予地址。 C语言提供两种相关地址运算符: &: 取地址运算符 *: 取内容运算符 * \ &优先级相同. 例:int a=5,b; int *p; p=&a; b=*p; 类型说明中的*表示 定义指针变量 p a 表达式中的*是运算符 &a 5
三、指针变量的赋值: /*整型变量a的地址赋给指针变量pa*/ /*指针变量p取得整型变量a的地址.*/ /*指针pa的值赋给指针pb*/ /*将数组的首地址赋给相同类型的指针*/ /*将字符串的首地址赋给指针变量*/ (1)、int a,*pa; pa=&a; (2)、int a=5,*p=&a; (3)、int a,*pa=&a,*pb; pb=pa; (4)、int a[5],*pa; pa=a; (5)、char *pc; pc=“c language”;
[例10.1] • main () • { int a,b; • int *pointer_1, *pointer_2; /* 定义指针变量 */ • a = 100; b = 10; • pointer_1 = &a; • pointer_2 = &b; • printf("%d,%d\n", a,b ); • printf("%d,%d\n", *pointer_1,*pointer_2 ); • } • 程序运行结果: pointer-1 a 100 &a 100,10 100,10 b pointer-2 10 &b
p1 a [例10.2]输入a和b两个整数,按先大后小的顺序输出a和b。 See:9_2.c 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 ); } 运行:5,9 5 &a p &b b p2 9 &b &a a=5,b=9; max=9,min=5 该例不交换变量a、b的值,而是交换指针p1、p2的值。
四、指针变量作为函数的参数 [例10.3]题目要求同[例10.2],输入a和b两个整数,按先大后小的顺序输出a和b。 swap( int* p1,int *p2 ) /*交换指针p1、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的地址传送给形参
1 a[0] pa &a[0] 2 3 4 5 6 a[6] 7 §10.3 数组的指针 • 指针可以指向数组和数组元素,对数组元素的访问: 既可以使用数组下标,也可以使用指针. 一、指向数组元素的指针变量 指向数组元素的指针变量,其类型应与数组元素相同. 例1: int a[7]; int *pa; pa=&a[0]; 或 pa=a; 例2: float b[10],*pb=b ; 或 float b[10],pb=&b[0];
二、通过指针引用数组元素 设:int a[10],*p=&a[0]; (1).p+1和a+1指向下一个元素a[1]; 如果数组元素是整型,p+1表示p的地址加2; 如果数组元素是实型,p+1表示p的地址加4; p+i和a+i指向元素a[i]。 (2).*(p+i)和*(a+i)访问元素a[i]; 例: *(p+5)和*(a+5)与a[5]等效; (3).指向数组指针可以带下标: 例: p[i]与*(p+i)等效.
[例10.5]输出数组的全部元素。(设10个元素,整型)。[例10.5]输出数组的全部元素。(设10个元素,整型)。 • 访问各元素有三种方法: 1、下标法(常用,很直观) main () { int a[10]; int i; for(i=0;i<10;i++) scanf("%d", &a[i]); printf("\n"); for(i=0;i<10;i++) printf("%d ", a[i] ); }
2、用数组名计算数组元素的地址。 (效率与下标法相同,常用) main () { int a[10]; int i; for(i=0;i<10;i++) scanf("%d", &a[i]); printf("\n"); for(i=0;i<10;i++) printf("%d ", *(a+i) ); } 特点:不移动指针
3、用指针访问各元素。(常用,效率高) main () (see:9_5.c) { 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++ ) /* p++使p指向下一个元素 */ printf("%d ", *p ); } 特点:移动执针
例:通过以下程序输出a数组中的10个元素。 main() { int a[10],*p,i; p=a; for(i=0;i<10;i++) scanf("%d",p++); printf("\n"); for(i=0;i<10;i++,p++) printf("%d\t",*p); } ??请问大家:以上程序可以输出a数组中的10个元素吗?
三、数组名作函数参数1、形参和实参都用数组名三、数组名作函数参数1、形参和实参都用数组名 f( int arr[ ],int n) { … } 例:main() {int array[10]; … f(array,10); … }
2、实参用数组名,形参用指针变量。 例:main() {int array[10]; … f(array,10); … } f( int *arr,int n) { … }
3、实参、形参都用指针变量。 例:main() {int array[10],*p; p=array; … f(p,10); … } f( int *arr,int n) { … }
4、实参用指针变量,形参用数组名 。 例:main() {int array[10],*p; p=array; … f(p,10); … } f( int arr[ ],int n) { … }
实际上,c编译系统将形参数组名作为指针变量处理.实际上,c编译系统将形参数组名作为指针变量处理. 若有定义 :f(int arr[ ],int n) 编译后:f(int *arr,int n)
§10.4 字符串的指针 一、字符串的表现形式 C语言中,有两种方式可以实现字符串: 字符数组、字符指针。 [例10.16]:字符数组 main() { char string[ ] ={"I love China!”}; printf("%s\n",string); } 用字符串对字符数组赋值 string是数组名,代表字符数组的首地址。 数组可以用下标访问,也可以用指针访问
Mary Hare name1 name2 name1是一个指针变量 [例10.17]字符指针 main() { char *name1 =”Mary"; char *name2=“Hare”; printf("%s%s\n",name1,name2); } char *name1 =“Mary”;等价于: char * name1; name1 = ”Mary"; 它把字符串常量的首地址赋给指针name1.
数组a a 数组b I b a m a b o y . \0 用数组名计算数组元素的地址 [例10.18]将字符串a复制到字符串b。 main () { char a[ ] = "I am a boy."; char 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"); } 特点:不移动指针
p1 数组a p2 数组b I a m a b o y . \0 特点:移动执针 [例10.19]:用指针处理字串复制. main () { char a[ ] = "I am a boy.",b[20],*p1,*p2; int i; p1=a;p2=b; for ( ; *p1 !='\0'; p1++,p2++) *p2 = *p1; *p2 = '\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"); } I a m a b o y . \0
例10.21: • main() • { char *a=“I love china!”; • a=a+7; • printf(“%s”,a); • } • 运行结果如下: • China! (a+7)是用指针指向数组元素a[i]的第7个字符
§10.5 函数指针 一、用函数指针变量调用函数 • 函数指针定义的一般形式: 函数返回值类型(*指针变量名)( ) 例: int (*p) ( ) • 说明:(*p)( )表示定义一个指向函数的指针变量,它不固定指向哪一个函数,它是专门用来存放函数的入口地址的,该函数返回一个整型值.。 • C规定:函数的名称就是函数的入口地址
[例9.23]求a和b中的大者。 用函数名调用函数max() • int max(int x, int y); /*原型*/ • {int z; • if (x>y) z = x; • else z = y; • return z; • } • main () • {int a,b,c; • scanf("%d,%d", &a, &b); • c = max(a, b); • printf("a=%d,b=%d,max=%d",a,b,c); • }
用函数指针调用函数max() • int max(int x, int y) /*原型*/ • {int z; • if (x>y) z = x; • else z = y; • return z;} • main () • { int (*p) ( );/*定义函数指针p */ • int a,b,c; • p = max; /*函数max的入口地址赋给函数指针p*/ • scanf("%d,%d", &a, &b); • c = (*p)(a,b); /*用函数指针p调用函数 */ • printf("a=%d,b=%d,max=%d",a,b,c);} 用函数指针变量调用函数时,只须将(*p)代替函数名max,并加上实参
§10.6 返回指针的函数(自学) 一般形式: 类型标识符 * 函数名(参数表) 例、int * a (int x, int y) • 声明一个函数,函数名为a,其返回值类型是“指向整型的指针”, • 函数形式参数为int x 和 int y。 注意:在a的两侧分别为*运算符和( )运算符,a先与( )结合,这是函数形式;函数前有个*号,表示该函数为指针型函数.
§10.7 指针数组和指向指针的指针(自学) 一、指针数组 概念:指针数组是一个数组,该数组中的 每一个元素是指针变量。 形式:类型标识符 *数组名[数组元素个数] 例: int * p[4]; 定义一个指针数组,数组名p,有4个元素, 每一个元素是指向整型变量的指针。
p name[0] name[1] name[2] Ann Hare Jack 二、指向指针的指针 定义举例: char * * p; char *name[]={“Ann”,“Hare”,“Jack”}; p=name; p是一个指向指针的指针.被p指向的指针指向字符变量。 name指针数组 二维字符数组
p name[0] name[1] name[2] Ann Hare Jack • main() • {int i; • char *name[]={"Ann","Hare","Jack","Mary"}; • /* storage address of per string*/ • char **p; • printf("\n Students's names are:\n"); • for(i=0;i<4;i++) • { p=name+i;/*point into address of next char string */ • printf("%6s\t",*p); } • } • 运行结果: • Ann Hare Jack Mary name指针数组 二维字符数组 name[3] mary
§10.8 指针使用小结 一、有关指针的数据类型 定义: • int i;定义整型变量i • int *p; p是指向整型数据的指针变量 • int a[n];定义数组a,元素类型为int,元素个数是n • int *p[n];p是指针数组,包含n个指针,每一个指针可以指向整型数据 • int f( ); f是函数,返回值是int • int (*p)( ); p是函数指针,所指向的函数返回整型数据 • int *p( );p是函数,返回值是指针,该指针指向整型数据 • int **p; p是指针,指向一个指向整型数据的指针
二、指针运算小结 1、指针变量加/减运算 • p++、p--、p+i、p-i、p+=i、p-=i 加1表示指向下一个数据。 2、指针变量赋值 • p = &a; 变量a的地址赋给p,即指针p指向a • p = array; 数组array首地址赋给p • p = &array[i]; 数组元素array[i]的地址赋给p • p = max; 函数max的入口地址赋给p • p1 = p2; 指针p2的值赋给指针p1, 即p1、p2所指的数据相同 • p=NULL; 指针变量可以由空值,NULL的值为0
3、空指针 p = NULL • 空指针p=NULL表示p不指向任何数据。 • 在stdio.h中,NULL被定义为0: • #define NULL 0 习惯上,不使用 p = 0而使用 p = NULL • 指针变量p可以与NULL作比较, 例:if (p = = NULL) ... 注意:空指针不指向任何数据,与p未赋值不同。 当p未赋值时,其值是不确定的,而空指针 的值是确定数0。
4、指针变量相减。 • 当p1、p2指向同一个数组的元素,指针相减p2-p1等于p1、p2间的元素个数。 • 注意:指针相加无意义。 5、两个指针的比较 • 当p1、p2指向同一个数组的元素时,可以比较,如、p1 > p2。若p1、p2不是指向同一个数组的元素,比较无意义。
三、空类型指针 void * • void *p,表示p是空类型指针,它可以指向任何数据类型。 • 空类型指针与其他类型指针之间赋值时,应进行强制类型转换. 例、 char *p1; void *p2; p1 = (char *)p2; p2 = (void *)p1;
第10 章 习 题 10.1 指针变量a所指的字符串长度为, 这个长度是可以用strlen(a)测出来的。 char *a=“\nMY Name is\”zhang li\”.\n”; (1)28 (2) 27 (3) 26 (4) 24 (5)23 10.2 下面程序的作用是,将两个变量中的值互换, 请检查程序是否正确,如不正确的,改正之。 main ( ) {int a=3,b=4; int *p1,*p2,*p; p1=&a,p2=&b; p=p1;p1=p2;p2=p; printf(“a= %d,b= %d\n”,a,b); } *p1,*p2
10.3 已设p1和p2为指针变量,且已指向同一个整 型数组中的元素,a是一个整型变量,问下面 哪一个语句不能正确执行? (1) a=*p1 (2) a=*p1+*p2 (3) a=*p1-*p2 (4)p1=a-p2 10.4 有一个二维数组a[3,4],2行3列元素的正确表 示方法为。 (1) &a[2][3] (2)a[2]+3 (3) *(a+2)+3 (4)*(a[2]+3) 10.5 下列程序的输出结果是 A)44 B)22 C)24 D)结果错误 main() {int a[5]={2,4,6,8,10},*p,**k; p=a;k=&p; printf("%d",*(p)); printf("%d\n",**k); }
10.6 若有语句:int a=4,*p=&a; 下面均代表地址的一组选项是: 1) a,p,&*a 2) *&a,&a,*p 3) &a,p,&*p 4) *&p,*p,&a 10.7 以下程序段的输出结果为: char a[]=“Program”,*ptr; ptr =a; for ( ;ptr<a+7;ptr+=2)putchar(*ptr); 1) Program 2) Porm 3) 有语法错误; 4) Por
10.8下面说明不正确的是: 1) int *a[4]; 2) float (*p)(); 2) int **p; 4) char *(*a)[];
作业 10.1 10.2 10.6 10.11 • 上机实验 学生成绩管理(使用指针): 1.学生姓名与成绩录入; 2.求学生平均成绩并输出; 3.将不及格的学生姓名与成绩输出; 4.按从高到低成绩排序并输出.