Download presentation
Presentation is loading. Please wait.
Published byAde Sasmita Modified 5年之前
1
欲穷千里,更上层楼 第十章 指 针 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; 能很方便地使用数组和字符串; 并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了C语言的功能。 学习指针是学习C语言中最重要的一环, 能否正确理解和使用指针是我们是否掌握C语言的一个标志。同时, 指针也是C语言中最为抽象的一部分,在学习中除了要正确理解基本概念,还必须要多编程,上机调试。只要做到这些,指针也是不难掌握的。
2
欲穷千里,更上层楼 一、指针的基本概念 在计算机中,所有的数据都是存放在存储器中的。 一般把存储器中的一个字节称为一个内存单元, 不同的数据类型所占用的内存单元数不等,如整型量占2个单元,字符量占1个单元等。 为了正确地访问这些内存单元, 必须为每个内存单元编上号。内存单元的编号叫做地址。通常把这个地址称为指针。 内存单元的指针和内存单元的内容是两个不同的概念。 对于一个内存单元来说,单元的地址即为指针, 其中存 放的数据才是该单元的内容。 在C语言中, 允许用一个变量来存放指针,这种变量称为指针变量。一个指针变量的值就是某个内存单元的地址或称为该内存单元的指针。
3
内存单元地址与内存单元内容: 2000 变量i (i=3;) 3 2002 变量j 6 2004 变量k 9 3010 2000
欲穷千里,更上层楼 内存单元地址与内存单元内容: 2000 变量i (i=3;) 3 2002 变量j 6 2004 变量k 9 3010 2000 变量i_pointer(指针变量) i_pointer=&i; 指针i_pointer 指向变量i
4
二、指针变量的定义和使用 1、类型说明 其一般形式为: 类型说明符 *变量名; 其中,*表示这是一个指针变量,变量名即为定义的指针变量名,
欲穷千里,更上层楼 二、指针变量的定义和使用 1、类型说明 其一般形式为: 类型说明符 *变量名; 其中,*表示这是一个指针变量,变量名即为定义的指针变量名, 类型说明符表示本指针变量将来可指向的变量的数据类型。例如: int *p1; 表示p1是一个指针变量,它的值将是某个整型变量的地址。 或者 说p1将指向一个整型变量。至于p1究竟指向哪一个整型变量, 应 由向p1赋予的地址来决定。
5
static int *p2; /*p2是指向静态整型变量的指针变量*/ float *p3; /*p3是指向浮点变量的指针变量*/
欲穷千里,更上层楼 再如: static int *p2; /*p2是指向静态整型变量的指针变量*/ float *p3; /*p3是指向浮点变量的指针变量*/ char *p4; /*p4是指向字符变量的指针变量*/ 应该注意的是,一个指针变量只能指向同类型的变量,如P3 只 能指向浮点变量,不能时而指向一个浮点变量, 时而又指向一个字 符变量。
6
2、指针变量的赋值 指针变量的赋值只能赋予地址,在C语言中, 变量的地址是由 编译系统分配的,用户不知道变量的具体地址。 因此,C语言
欲穷千里,更上层楼 2、指针变量的赋值 指针变量的赋值只能赋予地址,在C语言中, 变量的地址是由 编译系统分配的,用户不知道变量的具体地址。 因此,C语言 中提供了地址运算符&来表示变量的地址。其一般形式为: & 变量名; 如&a变示变量a的地址,&b表示变量b的地址。 设有指向整型变量的指针变量p,如要把整型变量a 的地址 赋予p可以有以下两种方式:
7
不允许把一个数值赋予指针变量,故下面的赋值是错误的: int *p; p=1000;
欲穷千里,更上层楼 (1)指针变量初始化的方法 int a; int *p=&a; (2)赋值语句的方法 int a; int *p; p=&a; 不允许把一个数值赋予指针变量,故下面的赋值是错误的: int *p; p=1000; 被赋值的指针变量前不能再加“*”说明符,如写为*p=&a 也是 错误的。
8
3、指针变量的引用 两个指针运算符: (1)取地址运算符:& (2)取内容运算符:*
欲穷千里,更上层楼 3、指针变量的引用 两个指针运算符: (1)取地址运算符:& (2)取内容运算符:* 例如: &a为变量a的地址,*p为指针变量p所指向的变量 #include ”stdio.h” #include “conio.h” main( ) { int a=5,*p=&a; printf ("%d",*p); getch( ); } /*表示指针变量p取得了整型变量a的地址*/ /*本语句表示输出变量a的值。*/
9
输入a和b两个整数,按大小顺序输出a和b #include ”stdio.h” #include “conio.h” 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\n",a,b); printf("max=%d,min=%d\n",*p1,*p2); getch(); } 输入: 5,9 输出:a=5,b=9 max=9,min=5 欲穷千里,更上层楼 p1 a &a p 5 p2 b &b 9 p1 a 5 &b p p2 b &a 9
10
4、指针变量作为函数参数 函数的参数不仅可以是整型、浮点型、字符型等数据,还可以是指针类型。它的作用是将 一个变量的地址传送到另一个函数中,达到在该被调函数中对主调函数中的变量操作的目的。 #include ”stdio.h” #include “conio.h” main( ) void swap(int *p1,int *p2) { void swap(int *p1,int *p2); { int a,b; int temp; int *pointer1=&a,*pointer2=&b; temp=*p1; scanf(“%d%d”,&a,&b); *p1=*p2; if(a<b) swap(pointer1,pointer2); *p2=temp; getch( ); } }
11
三、数组指针变量 指向数组的指针变量称为数组指针变量。 一个数组是由连续的一块内存单元组成的。数组名就是这块连 续内存单元的首地址。
欲穷千里,更上层楼 三、数组指针变量 指向数组的指针变量称为数组指针变量。 一个数组是由连续的一块内存单元组成的。数组名就是这块连 续内存单元的首地址。 一个指针变量既可以指向一个数组,也可以指向一个数组元素。 数组的指针是指数组的起始地址。可把数组名或第一个元素的 地址赋予它。 数组元素的指针是数组元素的地址。如要使指针变量指向第i 元素可以把第i数组元素的地址赋予它或把数组名加i赋予它。
12
1、数组指针变量的说明和使用 1)数组指针变量的定义 指向数组的指针变量和指向普通变量的指针变量的说明是相同的,如: int a[10];
欲穷千里,更上层楼 1、数组指针变量的说明和使用 1)数组指针变量的定义 指向数组的指针变量和指向普通变量的指针变量的说明是相同的,如: int a[10]; int *p; 经过赋值语句 p=&a[0]; 后p指向数组a。 p=&a[0]; p=a; 等价。
13
欲穷千里,更上层楼 从下图中我们可以看出有以下关系: a数组 p a[0] &a[0] a[1] a[9]
14
2)数组指针变量的使用 假设p为指针变量,且执行了: p=&a[2]; 则 *p=1; 表示对数组元素a[2]赋以值1
欲穷千里,更上层楼 2)数组指针变量的使用 假设p为指针变量,且执行了: p=&a[2]; 则 *p=1; 表示对数组元素a[2]赋以值1 C规定p+1指向下一个元素(实际含义为p+1*d,d为一个数组元素 所占字节数) 如果p的初值为&a[0],则: p+i和a+i就是a[i]的地址,或者说它们指向a数组的第i元素 *(p+i) 或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。
15
p,a,&a[0]均指向同一单元,它们是数组a的首地址,也是第0 元素a[0]的地址。
欲穷千里,更上层楼 a数组 p a[0] p,a,&a[0]均指向同一单元,它们是数组a的首地址,也是第0 元素a[0]的地址。 p+1,a+1,&a[1]均指向第1元素a[1]。类推可知p+i,a+i,&a[i]指向第i元素a[i]。 应该说明的p是变量,而a,&a[i]都是常量。在编程时应予以注意。 p+1,a+1 a[1] p+i,a+i *(p+i) a[i] p+9,a+9 a[9]
16
下标法 输出数组的10个元素 #include ”stdio.h” #include “conio.h” main( ) {
欲穷千里,更上层楼 输出数组的10个元素 下标法 #include ”stdio.h” #include “conio.h” main( ) { int a[10],i; for(i=0;i<10;i++){ a[i]=i; printf("%d",a[i]); } printf("\n"); /*主函数*/ /*分别定义一个整型数组和变量*/ /*循环语句*/ /*给数组赋值*/ /*打印每一个数组的值*/ /*输出换行*/
17
通过数组名计算数组元素 地址,找出元素的值. #include ”stdio.h” #include “conio.h” main( )
欲穷千里,更上层楼 通过数组名计算数组元素 地址,找出元素的值. #include ”stdio.h” #include “conio.h” main( ) { int a[10],i; for(i=0;i<10;i++) *(a+i)=i; printf("%d",*(a+i)); } printf("\n"); getch(); /*主函数*/ /*分别定义一个整型数组和变量*/ /*循环语句*/ /*给数组赋值*/ /*打印每一个数组的值*/ /*输出换行*/
18
用指针变量指向数组元素 #include ”stdio.h” #include “conio.h” main( ){
欲穷千里,更上层楼 用指针变量指向数组元素 #include ”stdio.h” #include “conio.h” main( ){ int a[10],i; int *p; for(p=a;p<(a+10);p++) { *p=i; printf("%d",*p); } printf("\n"); getch(); /*主函数*/ /*分别定义一个整型数组和变量*/ /*定义指针变量*/ / *循环语句*/ /*给数组赋值*/ /*打印每一个数组的值*/ /*输出换行*/
19
2、数组名和数组指针变量作函数参数 在第七章中曾经介绍过用数组名作函数的实参和形参的问题。 在学习指针变量之后就更容易理解这个问题了。
欲穷千里,更上层楼 2、数组名和数组指针变量作函数参数 在第七章中曾经介绍过用数组名作函数的实参和形参的问题。 在学习指针变量之后就更容易理解这个问题了。 数组名就是数组的首地址,实参向形参传送数组名实际上就是 传送数组的地址, 形参得到该地址后也指向同一数组。 这就好象 同一件物品有两个彼此不同的名称一样。 同样,指针变量的值也是地址, 数组指针变量的值即为数组的 首地址,当然也可作为函数的参数使用。 例如:
20
…... …... …... 数组名作函数的形参和实参时: main( ) { int array[10];
欲穷千里,更上层楼 数组名作函数的形参和实参时: main( ) { int array[10]; f(array,10); } f(int arr[ ], int n) { } …... …... …... array[0] array arr arr[0]
21
…... …... …... 实参用数组名,形参用指针变量 main( ) { int array[10]; f(array,10);
} 欲穷千里,更上层楼 f(int *x,int n) { } …... …... …... array[0] array x
22
…... …... …... 实参形参都用指针变量 main( ) {int array[10],*p; p=array;
f(p,10); } 欲穷千里,更上层楼 f(int *x,int n) { } …... …... …... array[0] p x
23
…... …... …... 实参用指针变量,形参用数组名 main( ) {int array[10],*p; p=array;
f(p,10); } 欲穷千里,更上层楼 f(int x[],int n) { } …... …... …... array[9] array[0] p x[0] a[9]
24
四、指向多维数组的指针和指针变量 1、多维数组的地址
例: int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; 说明: 1)a是二维数组名。 2)a数组包含3行,即3个元素:a[0],a[1],a[2]。 3)a[0],a[1],a[2]又分别是一个一维数组。各包含4个元素。如a[0]包含 a[0] [0],a [0][1],a [0][2],a [0][3]。 从二维数组的角度看,a代表整个二维数组的首地址,a+0代表二维数组a的第0元素,a+1代表二维数组的第1元素。同理,a+2代表二维数组的第2元素。 假设a数组的首地址为2000,则a+1为2008。即:2000+1*2*4(2为int变量字节数,4为二维数组每个元素本身又包含的元素数);同理,a+2为2016。
25
a[0],a[1],a[2]既然是一维数组名,而C语言规定数组名代表数组的地址,因此, a[0]代表第0行一维数组中第0列元素的地址,即&a[0][0]。a[1]的值是&a[1][0],a[2]的值是&a[2][0] 。 2、多维数组元素的表示方法 1)表示元素a[i][j]的地址: &a[i][j] /*取地址运算*/ a[i]+j /*指针方法*/ 2)在讲述一维数组时候我们曾经提到: 因为: a[i]和*(a+i) 等价! 所以: a[i]+j= =*(a+i)+j= =&a[i][j] 又因为: a[i]+j 是a[i][j]的地址 所以: *( a[i]+j )==a[i][j] 等价代换:*(a[i]+j)= =*(*(a+i)+j)= =a[i][j]
26
3)进一步说明a[i]的性质 a[i]从形式上看是a数组中的第i元素。 如果a是一维数组名,则a[i]代表a数组第i元素所占的内存单元。 a[i]是有物理地址的,是占内存单元的。 但如果a是二维数组名,则a[i]代表一维数组名, a[i]本身并不占实际的内存单元,也不存放a数组中各个元素。它只是一个地址。 故: a、a+i、 a[i]、 *(a+i) 、 *(a+i)+j都是地址。 但是: a、a+i是行指针,指向二维数组的相应行! a[i]、 *(a+i) 、 *(a+i)+j是列指针,指向二维数组的相应列!
27
3、指向多维数组的指针变量 (1)指向数组元素的指针变量 例如: #inlcude “stdio.h” #include “conio.h” main( ) { int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12},*p; for(p=a[0];p<a[0]+12;p++){ if(p-a[0]%4==0) printf(“\n”); printf(“%4d”,*p); } getch( );
28
(2)指向由m个元素组成的一维数组的指针变量
例如: #include “stdio.h” #include “conio.h” main( ) { int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12},(*p)[4],i,j; p=a; printf(“Please tell me the line and col numbers:”); scanf(“%d%d”,&i,&j); printf(“a[%d][%d]=%d\n”,i,j,*(*(p+i)+j)); } getch(); (*p)[4]表示p是一个指针变量,它指向包含4各元素的一维数组。
29
4、多维数组的指针作函数参数 在用指针变量作形参以接受实参数组名传递过来的地址时, 有两种方法: 1)用指向变量的指针变量。 2)用指向一维数组的指针变量。 例如:有一个班,3个学生,各学4门课,计算总平均分, 以及第2学生的成绩。 本程序用average求总平均成绩,用函数search找出并输出第 2学生的成绩。
30
void average(float *p,int n); void search(float (*p)[4],int n);
#include “stdio.h” #include “conio.h” main() { void average(float *p,int n); void search(float (*p)[4],int n); float score[3][4]={{65,67,70,60}, {80,87,90,81},{90,99,100,98}}; average(*score,12); search(score,2); getch( ); } void search(float (*p)[4],int n) int i; printf(“the score of No.%d are:”,n); for(i=0;i<4;i++) printf(“%5.2f”,*(*(p+n)+i); void average(float *p,int n) { float *p_end,sum=0,aver; p_end=p+n-1; for(;p<=p_end;p++) sum+=(*p); aver=sum/n; printf(“aver=%5.2f\n”,aver); }
31
五、字符串指针变量的说明和使用 1、字符串指针变量的说明 字符串指针变量的定义说明与指向字符变量的指针变量说明是 相同的。如:
欲穷千里,更上层楼 五、字符串指针变量的说明和使用 1、字符串指针变量的说明 字符串指针变量的定义说明与指向字符变量的指针变量说明是 相同的。如: char c,*p=&c; 表示p是一个指向字符变量c的指针变量。而: char *s="C Language"; 则表示s是一个指向字符串的指针变量。把字符串的首地址赋予s。
32
C L a n g u e \0 C L a n g u e \0 2、字符串的表示形式 1) 字符数组 ps a[0] a[1]
欲穷千里,更上层楼 ps a[0] C L a n g u e \0 C L a n g u e \0 2、字符串的表示形式 1) 字符数组 main(){ char a[]="C Language"; printf("%s",a); } 2) 字符指针 char *ps="C Language"; printf("%s",ps); a[1] ………………….. a[10]
33
输出字符串中n个字符后的所有字符。 ps #include “stdio.h” #include “conio.h” main( ) {
欲穷千里,更上层楼 输出字符串中n个字符后的所有字符。 #include “stdio.h” #include “conio.h” main( ) { char *ps=“THIS IS A BOOK"; int n=10; ps=ps+n; printf("%s\n",ps); getch(); } 运行结果为: BOOK 在程序中对ps初始化时,即把字符串首地址赋予ps,当ps= ps +10之后,ps指向字符“B”,因此输出为“BOOK"。 ps T H I S A B O K \0 ps=ps+10
34
对字符串中字符的存取可以用下标法也可以用指针法 例:将字符串a复制到b #include “stdio.h”
欲穷千里,更上层楼 对字符串中字符的存取可以用下标法也可以用指针法 例:将字符串a复制到b #include “stdio.h” #include “conio.h” main( ) {char a[ ] ="I am a boy.",b[20]; int i; for (i=0;*(a+i)!='\0';i++) for (i=0;a[i]!='\0';i++) *(b+i)=*(a+i); b[i]=a[i]; *(b+i)='\0'; 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]); getch(); } 程序运行结果为: string a is : I am a boy. string b is : I am a boy.
35
char a[ ] ="I am a boy.",b[20],*p1,*p2; int i; p1=a;p2=b;
欲穷千里,更上层楼 例:将字符串a复制到b #include “stdio.h” #include “conio.h” 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"); getch(); } a b p1 p2
36
3、字符串指针作函数参数 将一个字符串从一个函数传递到另一个函数,可以用地址传递的方法,即用字符数组名作参数或用指向字符串的指针变量作参数。在被调用函数中可以改变字符串的内容,在主调函数中可以得到改变了的字符串。 例如: 例:用函数调用实现字符串的复制。
37
#include “stdio.h” #include “conio.h” void copy_string(char *from ,char *to) { for(;*from!=‘\0’;from++,to++) *to=*from; *to=‘\0’; } main( ) { char *a=“I M a teacher!”; char *b=“U R a student!”; printf(“\nstring a=%s\nstring b=%s\n”,a,b); copy_string(a,b); printf(“\nstring a=%s\nsting b=%s\n”,a,b); getch();
38
4、对使用字符指针变量和字符数组的讨论 (1)数据结构不同。 (2)动态赋值方式不同。 (3)赋初值方式不同。 (4)内存分配方式不同。 (5)指针变量的值可以改变。 (6)用指针变量可以指向一个字符串。
39
六、函数的指针和指向函数的指针变量 1、用函数指针变量调用函数 可以用指针变量指向整形变量、字符串、数组,也可以指向 一个函数。
一个函数在编译时被分配给一个入口地址,这个入口地址就 称为函数的指针。 可以用一个指针变量指向函数,然后通过该指针变量调用此 函数。
40
例如:求a和b中的大者。 #include<stdio.h> main( ) { int max(int ,int ); int (*p)( ), a,b,c; p=max; scanf(“%d%d”,&a,&b); c=(*p)(a,b); printf(“a=%d,b=%d,max=%d”,a,b,c); } max(int x,int y) int z; if(x>y) z=x; else z=y; return (z); int (*p)( )定义p是一个指向函数的指针变量,此函数带回整型的返回值。 赋值语句p=max;的作用是将函数max的入口地址赋给指针变量p。(函数名代表该函数的入口地址) C=(*p)(a,b);用指针的形式实现函数的调用,相当于 C=max(a,b);
41
说明: 1)指向函数的 指针变量的一般定义形式为 数据类型(* 指针变量名)();/*函数返回值类型*/
2)函数调用可以通过函数名调用,也可以通过函数指针调用。 3)(*p)()表示定义一个指向函数的指针变量,它是专门用来存放函数的入口地址的。在一个程序中,一个指针变量可以先后指向不同的函数。 4)在给函数指针变量赋值时,只需给出函数名而不必给出参数。 5)用函数指针变量调用函数时,只需将(*p)代替函数名,在 (*p)之后的括弧中根据需要写上实参。 6)对指向函数的指针变量,p++、p+n、 p—等运算是无意义的。
42
2、用指向函数的指针作函数参数 指向函数的指针也可以作为参数,以便实现函数地址的传递, 也就是将函数名传给形参。 原理:
有一个函数sub,它有两个形参x1和x2:指向函数的指 针变量。在调用sub时,实参是两个函数名f1,f2,给形参传递函 数地址。从而实现在函数sub中调用f1和f2两个函数。 例:输入a和b两个数,设一个函数process,在调用它时, 每次实现不同的功能:第一次调用process时找出a和b中大者,第 二次找出其中小者,第三次求a与b之和。
43
#include “stdio.h” #include “conio.h” { int max(int,int); int min(int,int); int add(int,int); int a,b; printf(“enter a&b:”); scanf(“%d%d”,&a,&b); printf(“max=“); process(a,b,max); printf(“min=“); process(a,b,min); printf(“sum=“); process(a,b,add); getch(); } max(int x,int y) { return (x>y)?x:y;} min(int x,int y) { return (x<y)?x:y;} add(int x,int y) { return (x+y);} process(int x,int y,(*fun)(int int) { int result; result=(*fun)(x,y); printf(“%d\n”,result); }
44
七、返回指针值的函数 一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。 带回指针值的函数,一般定义形式为: 类型名 *函数名(参数表); int *a(int x, int y); 其中,a是函数名,调用它以后得到一个指向整型数据的指针。x,y是函数a的形参,为整型。 例如:有若干个学生的成绩(每个学生有4门课),要求在 用户输入学生学号以后,能输出该学生的全部成绩。用指针函数实现。
45
#include “stdio.h” #include “conio.h” { 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); p=search(score,m); printf(“The score of NO.%d are:”,m); for(i=0;i<4;i++) printf(“%5.2f”\t”,*(p+i)); getch(); } float *search(float (*pointer)[4],int n) { float *pt; pt=*(pointer+n);return (pt); }
46
八、指针数组和指向指针的指针 一个数组,其元素均为指针类型数据,称为指针数组。 一维指针数组的定义形式为: 类型名 *数组名[数组长度]
由于[]比*优先级高,所以p先与[4]结合,形成p[4]数组形式,再与*结合,表示此数组是指针类型的。每个数组元素都可以指向一个整型变量。 注意:不要写成 int (*p)[4],这是指向一维数组的指针变量! 例如: char *name[5]={“follow me”,”basic”,”great wall”,”fortran”,”computer design”}; for(i=0;i<5;i++) printf(“%s\n”,name[i]); 输出 name[i]指向的字符串
47
指针数组适合于用来指向若干个字符串,使字符串处理更加方便灵活。如果想对字符串排序,不必改动字符串的位置,只需改动指针数组中各元素的指向(即改变各元素的值,这些值是各字符串的首地址)。这样,各字符串的长度可以不同,且排序只是移动地址值,所花费时间少,速度快。 例如:
48
指向指针的指针 指向指针数据的指针变量,简称为指向指针的指针。 定义形式: char **p; 1) char *p表示p是一个指向字符型数据的指针变量。 2)char **p表示指针变量p是一个指向字符指针变量的指针变量,*p就是p所指向的另一个指针变量。 例如: char *name[ ]={“follow me”,”basic”,”great wall”,”fortran”,”computer design”}; p=name+2; printf(“%d\n”,*p); 输出 name[2]的地址值 printf(“%s\n”,*p); 输出字符串“great wall” 例如:
Similar presentations