500 likes | 611 Views
第九章 指 针. 第九章 指针. 第一节 指针基本概念 第二节 变量的指针 第三节 指针与数组 第四节 指针与字符串 第五节 指针与函数. 9.1 指针的概念. 关于地址:. &. 变量是以字节为单位存放在内存上的,每一个字节成为一个单元,在内存中每个单元有一个编号,这个编号就是 地址,内存 是按字节排列的连续空间。. 定义变量: int k ; 编译系统根据类型为 k 分配内存。. 输入变量的值: scanf(“%d”,&k); &k 就代表了变量 k 在内存中的地址 。.
E N D
第九章 指 针
第九章 指针 第一节 指针基本概念 第二节 变量的指针 第三节 指针与数组 第四节 指针与字符串 第五节 指针与函数
9.1 指针的概念 关于地址: & 变量是以字节为单位存放在内存上的,每一个字节成为一个单元,在内存中每个单元有一个编号,这个编号就是地址,内存是按字节排列的连续空间。 定义变量: int k; 编译系统根据类型为k分配内存。 输入变量的值:scanf(“%d”,&k); &k就代表了变量k在内存中的地址。 以往对变量的访问是通过变量名,这种操作称为直接访问: int k; scanf(“%d”,&k); printf(“%d\n”,k);
int k=100 100 int *pk &k &k 9.1 指针的概念(续) C 提供了另一种方式,将变量 k的地址存放在另一个变量处(假定为pk),通过访问 pk,就可以间接地访问变量k,这种方式称为间接访问。 变量 int k; 变量 pk 中存放 k 在内存中的地址。 变量pk 就是一个指针变量,指向变量k,存放k的地址 因此:指针就是地址。 谁的地址? 有何特点? *
9.1 指针的概念(续) main( ) { int a , b , c, *pc ; a=5 ; b=10 ; pc=&c; c=a+b; printf(“%d,%d,%d”, a,b,*pc); } 利用变量 c 的变量名直接访问。 main( ) { int a , b , c ; a=5 ; b=10 ; c=a+b; printf(“%d,%d,%d”, a,b,c); } 比较两个程序 利用指针变量 * pc 间接访问变量c。
9.1 指针的概念(完) 指针可以指向所有类型的数据。 指针指向变量--变量的指针(存变量的地址)。 指针指向数组--数组的指针(存数组的起始地址)。 指针指向函数--函数型指针(存放函数的入口地址)。 指针指向指针--指针型指针(存放某指针的地址)。 由指针组成的数组--指针数组。 指针是定位其他数据类型的特殊类型的数据(存放其他数据的地址)。
9.2 变量的指针 一个指针,指向一个变量 一 、 指针变量的定义 int *pa; 1 格式: 类型 * 标识符 其中: 指针的类型,是指针所指的变量 的类型(基类型) *为指针说明符,说明后面的标识符为指针变量(也叫间接访问符) pa=&a; 将变量a的地址赋给指针pa int a , *pa; *pa=a; 通过间接访问符可以访问变量a (1) 一个指针指向同一种类型的变量 2 说明: (2) * 和 & 在C中有两个作用: *: 乘号(双目)和指针说明符(单目) &: 取址符(单目)和位操作中的“与”
9.2 变量的指针(续) (3) *与& 之间是互逆的 int k; int *pk ; 当指针 pk 指向变量 k 时,有: pk=&k; *pk=k; 所以: *(&k) = *pk=k &(*pk)=&k=pk 地址:数组名 &变量名 二、 指针变量的初始化 1、 指针变量使用前必须有值 2、 指针变量的初值必须是地址值(不能是整数) 3、 方法有两种: (1) pk =&k ; (2) int k ; int *pk = &k ;(此句不是赋值语句)
9.2 变量的指针(续) 4、 可以为指针赋空值(NULL),此时指针不指向任何变量 pk=NULL; 区别:定义指针 *pk 引用指针:*pk 三 、指针变量的引用 对变量k的访问就可以有两种方法: 直接访问:通过变量名--k 间接访问:通过指针--pk int k , *pk; pk=&k; printf(“%d”,k); 与 printf(“%d”,*pk);完全等价 *pk=0; 与 k=0; *pk+=1; 与 k+=1; (*pk)++ 与 k++;
p 100 a 200 b 300 c 9.2 变量的指针(续) main( ) { int a,b,c; int *p ; (指针说明) a=100; b=200; p=&c; (将c的地址赋给p) *p=a+b; (将a+b的值存储在p指向的地址) printf(“%d+%d=%d\n”,a,b,*p); printf(“%x,%x,%x,%x,%x\n”,&a,&b,&c,p,&p);} main( ) { float a,b,c; float *p; a=100.0,b=200.0; p=&c; *p=a+b; printf(“%f+%f = %f \n”, a, b, *p); printf(“%x,%x,%x,%x,%x\n”, &a, &b, &c, p,&p); }
9.2 变量的指针(续) 1、 程序中一旦进行了变量说明 int a , b , c ; 则机器自动设置a,b,c的存储区域,每个整数两个字节。 2、 当a=100,b=200 时, 则分别存在 a , b 的空间内。 3、 为便于了解存储区的位置,用&a表示a的地址,&b表示b的地址, &c表示c的地址。 4、 变量p存放地址, p=&c, 称为指针变量。 5 、指针变量p自身也有地址,用&p表示。 printf(“%x”,&p)可求得指针自身的存储地址。
主函数内 ,输入两个整数,调用一个功能函数,交换两个整数的大小。 9.2 变量的指针(续) ? 四 、变量的指针应用---作函数的参数 输入3,5 main( ) { int a , b; printf(“Enter two numbers:\n”); scanf(“%d,%d”,&a,&b); printf(“ 1: %d,%d\n”,a,b); swap( a, b ); printf(“3:%d,%d\n”,a,b); } swap(int x,int y) { int c; c=x ; a=y ; y=c ; printf(“2: %d,%d\n”,x, y); } 输出1:3,5 输出3:3,5 swap( a, b ); 输出2:5,3 3 ,5 形参 x , y 是局部变量, 函数内定义,函数内有效,函数调用结束,值不保留
9.2 变量的指针(续) 形参为指针 swap(int *p1, int *p2) { int pk; pk=*p1; *P1=*P2; *p2=pk; printf(“2: %d,%d\n”, *p1, *p2); } main( ) { int a , b; int *po1,*po2; scanf(“%d,%d”,&a,&b); printf(“1: %d,%d\n”,a,b); po=&a,po2=&b; if(a<b) swap( po1,po2 ); printf(“3:%d,%d\n”,a,b); } 交换 实参为指针
po1 po1 &a &a a po1 a a po1 a p1 p1 9 &a 9 &a 5 5 &a &a po2 b po2 b po2 b po2 b &b &b 5 9 &b &b 9 5 p2 p2 &b &b 9.2 变量的指针(续)
题目 分析 9.2 变量的指针(完) 指针的值为地址 1、 功能函数 swap(a,b)的作用是实现两个数的交换,当参数为指针 型时,实参,形参都为指针变量。 2、 当函数被调用时,实参值传给形参(值传递)结果为:形参指针p1,实参 指针po1指向同一个变量a。 3、 当函数调用结束时,交换的是指针所指的变量的 值。pk=*p1;*p1=*p2;*p2=pk;此时,pk是一个int 变量,不是指针。 main( ) { int a,b; scanf(“%d,%d”,&a,&b); if(a<b) swap(&a,&b); printf(“%d,%d\n”,a,b); } 主调函数中不定义指针,直接用变量的地址作为实参,作用相同,函数swap接受的都是地址值。 题目中的主函数可改为:
memory pa a[0] Pa+1 a[1] Pa+2 a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] Pa+9 9. 3 指针与数组 一个指针,指向一个数组 * 通过指针引用数组中的任意元素 * 一个指针与一个一维数组的等价性 * 字符指针的特殊使用 * 关于“数组指针”的概念和使用 掌 握 要 点 int a[10],*pa=a; 与 int (*pa)[4]; 数组名就是数组的起始地址
memory pa a[0] pa+1 a[1] pa+2 a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] pa+9 9.3 指针与数组(续) 一、 用指针引用数组元素 定义指针 int a[10],*pa; 定义数组 pa=&a[0]; 指针初始化 将数组中第一个元素的地址赋给指针变量: 等价于: pa=a; pa=a; 间接访问:*pa=a[0]; 可以移动指针使其指向数组中后面的元素。 向后移动i个元素 pa+i 指针指向元素a[i]。
memory pa a[0] pa+1 a[1] pa+2 a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] pa+9 地址值 9.3 指针与数组(续) pa+i=&a[i] (将a[ i ]的地址赋给指针pa) pa+i=a+i=a+i*m (每个元素占字节) *(pa+i)=*(a+i)=a[ i ] (间接访问) 当i的值在下标范围内变化,就可访问数组中所有元素。 pa+i m 下标法: 循环语句,逐一访问 a[ i] 指针法: 移动指针访问 *(pa+i)=a[i]=*(a+i) 访问数组元素
9.3 指针与数组(续) main( ) { static int a [ ]={ 1,2,3,4,5,6}; int i; for(i=0;i<6;i++) printf(“%d”,a[i ]); } main( ) { static int a[]={1,2,3,4,5,6}; int i; int *pa=a; for( i=0;i<6;i++) printf(“%d”,*(pa+i)); } 下标法 指针法 main( ) { static int a[ ]={1,2,3,4,5,6}; int i; for(i=0;i<6;i++) printf(“%d”,*(a+i)); } a[i] *(a+i) *(pa+i) 地址法
9.3 指针与数组(续) 二、 指针与数组的等价性: 1、 当指针变量pa的值与地址常量a相同时,如果变量i为一整数,则a[ i ],*(a+i)和*(pa+i) 三个表达式功能相同,意义相同,程序中可混用。 2、 a[ i ],*(a+i),*(pa+i)遵循统一的地址计算规则,如果数组元素占 m个字节,则第i个元素的地址为: a+i*m 或 pa+i*m 3、指针变量法和数组下标法均允许对数组元素随机存取 数组名 地址 指针 地址 数组 指针 等价性
9.3 指针与数组(续) 字符数组在C中可以用字符串常量初始化,可以整串的输入,输出,由于指针与数组的等价性,字符指针也有某些特点. 三 、字符指针 1、 字符指针定义: char *pc; 2、 字符指针的赋值: char c; pc=&c; 3、 可以用字符串常量为字符指针初始化 char *pc=“Turbo C”; 字符数组初始化 char c[ ]=“Turbo C”; 是将字符串常量的内容全部赋给数组 注 意 ! 字符指针初始化: char *pc=“Turbo C”; 仅将串首地址赋给指针,也可写成: char *pc; pc=“Turbo C”;
9.3 指针与数组(续) 地址=指针 三种表示方法等价: (1) char *pc;pc=“Book”; (2) char *pc=“Book”; (3) char name[ ]=“Book”; char *pc=name; 4、字符数组与字符指针的区别: (1 )字符数组可以写成: char c[ ]=“book”; 但不能写成: char c; c[ ]=“book”; 字符指针可以写成:char *pc=“book”; 也可以:char *pc; pc=“book”;
T u r b o c \0 9.3指针与数组(续) a (2) 当用字符串常量初始化时: 字符数组获得了串中所有的字符(内容) 字符指针获得了串首的地址(与串内字符无关) 5、 字符指针的应用举例 main( ) { char *a=“Turbo C”; char *p; p=a+4; printf(“%c\n”,*a); printf(“%s\n”,a); printf(“%c\n”,*p); printf(“%x,%x,%x,%x\n”,a,p,&a,&p); } p ‘o’的地址 ‘T’的地址
9.3 指针与数组(续) 四、 指针的运算 C仅允许指针作三种运算 假定两个指针变量 p, q,当两个指针指向同一个数组时,以下比较运算结果为逻辑值。 ** 指针的比较运算(关系) ** 指针的减法运算 ** 指针与一个整数的加减运算 1. 指针的比较运算 p>q p 指针所指元素位于q所指元素之后时为1 p p<q p 指针所指元素位于q所指元素之前时为1 q p>=q p指针所指元素位于q所指元素之后(或两指针指向同一元素)时为1。 int a[6]
9.3 指针与数组(续) p<=q p指针所指元素位于 q 指针所指元素之前(或两指针指向同一元素)时为 1 。 p==q 两指针指向同一元素时为 1。 p!=q 两指针不指向同一元素时为 1。 2. 指针的减法运算 两指针p,q指向同一数组 p-q的结果为p所指元素与q所指元素之间相隔的元素的个数(不是地址值之差,也不是变量值之差)。 3. 指针与整数的加减运算 当一个指针指向一个数组时,指针与一个整数n 相加或相减,表示将指针向后或向前移动了n个元素。
9.3 指针与数组(完) 1、 若定义char *p=“I am a student”; 则 *(p+4)的值为: 练习 2、 若定义int a[4]={ 0,1,2,3},*p; p=&a[1]; 则++(*p)的值为: 3、下列程序中,数组a的地址为ffe2,问第二个printf语句执行后的结果为: main( ) { static int a[10]={1,2,3,4,5,6,7,8,9,0}; int *p; p=a; printf(“%x”,p); printf(“%x”,p+9); }
指针与数组的等价关系 1、int a[10], int *pa, pa=a ,pa+i=a+i ,*(pa+i)=*(a+i)=a[i] 2、int a[3][3],*pa[3]={a[0],a[1],a[2]}, *(pa[i]+j),*(*(pa+i)+j), *(*(a+i)+j)都可以实现对数组中 任意元素a[i][j]的访问。 3、char *pc[ ]={“AAA”, “BBB”, “CCC”};的含义。 已有定义int k=2,int *ptr1,*ptr2;且ptr1和ptr2均已指向同一个变量k,下面不正确执行的赋值语句是: A) k=*ptr1+*ptr2 ; B) ptr2=k; C) ptr1=ptr2; D)k=*ptr1*(*ptr2);
9.4 指针数组 由若干个指针变量组成的数组,称为指针数组 指针数组的定义 指针数组的初始化(用地址) 指针数组的应用 指针数组与二维数组的等价性 掌握 要点 一 、指针数组的定义 定义数组,不是定义一个指针 格式:类型 *标识符[长度] int *pa[3]; 数组中所有指针的类型(均指向同一类型的变量) 数组名 数组中所含指针的个数
1 2 3 4 5 6 7 8 9 *pa[0] *pa[1] *pa[2] a[0] a[1] a[2] 9.4 指针数组(续) 必须用地址值为指针数组初始化 二 、指针数组的初始化 static int a[3][3]={1,2,3,4,5,6,7,8,9}; int *pa[3]={a[0],a[1],a[2]}; 指针数组*pa[3] 相当于有三个指针,*pa[0],*pa[1],*pa[2],初始化的结果: 定义了静态的二维数组,所有元素有固定的地址 用该二维数组的每一行的行首地址为指针数组初始化,行首地址就是该行的名(数组名为起始地址) 行首地址 行首地址就是该行的名(数组名为起始地址)
*pa[0] *pa[1] *pa[2] 1 2 3 4 5 6 7 8 9 a[0] a[1] a[2] 第j列 a[I][j] 9.4 指针数组 pa[0]=&a[0][0]=a[0]; pa[1]=&a[1][0]=a[1]; pa[2]=&a[2][0]=a[2]; i pa[i]=&a[i][0]=a[i]; 对任意行i,可以有: pa[i]=&a[i][0]=a[i]; 分析:pa[i]=a[i];等价于: pa=a, pa+j=a+j *(pa+j)=*(a+j)=a[j]; 相当于一个指针,指向一个一维数组 对指针数组中的任意一个pa[i],移动j个元素, 指针与数组的等价性: *(a+i )=*(pa+i )=a[ i ]; pa[i]+j=a[i]+j=&a[i][j]; *(pa[i]+j)=*(a[i]+j)=a[i][j];
9. 4 指针数组(续) *(pa[ i ]+j)=*(a[ i ]+j )=*(*(a+i )+j ) =a[ i ] [ j ] 指针数组与二维数组是等价的,通过指针数组可以引用二维数组中的元素 三 、指针数组的应用 1 用指针数组引用二维数组中的元素 main( ) { int i,j; static int a[3][3]={1,2,3,4,5,6,7,8,9}; int *pa[3]={a[0],a[1],a[2]}; for(i=0;i<3;i++) for(j=0;j<3;j++) printf(“%d,%d,%d\n”,a[i][j],*(*(a+i)+j),*(pa[i]+j) ); }
9.4 指针数组(续) 2. 处理二维字符数组 #define SIZE 4 main( ) { int I,k,j; static char *str[SIZE]={“Program”,”Fortran”, ” C” ,”Basic”}; char *temp; for(I=0;I<SIZE;I++) for(j=I+1;j<SIZE;j++) if(strcmp(str[I],str[j] )>0 ) { temp=str[j] ; str[j]=str[I]; str[I]=temp; } for(I=0;I<SIZE;I++) printf(“%s\n”,str[I] ); } 用字符串为字符指针初始化
Program Pstr[0] Pstr[1] Fortran Pstr[2] C Basic Pstr[3] 9.4 指针数组(续) 如果用二维数组? 利用指针*temp,作为指针交换的临时变量 通过改变指针的指向,改变输出顺序,不真正交换字符串的位置
9.4 指针数组(续) 命令名 参数1 参数2….参数n 3.作main函数的形参 main(参数1,参数2) int 参数1; /*命令行字符串的个数*/ char *参数2[ ]; /*各字符串在内存存放的首地址*/ { …… } 形参值从何而来? DOS系统执行.EXE的命令行提供的。 运行该程序的同时给出实参的值 file1 China Beijing 运行程序的名
9 .5 指针与函数 一个指针,指向一个函数 一 、 函数型指针 定义了函数之后,系统为该函数分配一段存储空间,其中函数的起始地址,称为该函数的入口地址,将此地址赋给另一个变量,则该变量为一个指向函数的指针 只能指向函数 二、 函数型指针的定义 1 格式: 类型 (*标识符)( ) 所指函数的返回值 2 说明:类型为被指针所指函数的返回值的类型,标识符为一个指针名,(不是函数名),括号中为空,但必须有,表示该指针是专指函数的
9 .5 指针与函数 括号 定义中的(*…..)括号不能省略 三 、函数型指针的赋值 用函数名为指针初始化,表示指针指向该函数 int (*pf)( ); int f( ); pf=f; (指针 pf 指向函数f( ) ) 应用: 增加了一种函数调用的方式 通过指针调用函数
9 .5 指针与函数 main() { int (*pf)( ); int a,b,k; scanf(“%d,%d”,&a,&b); pf=f ; k=(*pf)(a,b); printf(“%d,%d,%d\n”,a,b,k); } f(x,y) int x,y; { retrun(y-x); } 用函数名为指针赋值 相当于k=f(a,b) 输入:100 ,200 输出:100,200,100
9 .5 指针与函数 定义函数型指针 求a和b中的较大的 main() { int (*p)( ); int a,b,c; p=max; scanf(“%d,%d”,&a,&b); c=(*p)(a,b); printf(“%d,%d,%d”,a,b,c); } 用函数名为指针赋值 max(x,y) int x,y; { return(x>y?x:y); } 通过指针调用函数
9 .5 指针与函数 说明: 函数的调用可以用函数名(第j九章),也可以用指针 (*p)()定义之后,不是固定指向某一个函数,可以先后指向不同的函数(用新的函数名重新赋值) 用函数名为指针赋值时,不必用参数 用函数指针调用函数时,用(*p)代替原函数名 区别: int (*s)( ) , int *s( )[ 3 ] , int *s [ 3 ]
9 .5 指针与函数 关于函数,返回值为指针 四 、指针型函数的概念 函数调用的结果,通常得到一个返回值,带回主调函数,如果返回值为一个指针,则该函数就是指针型函数 五 、 指针型函数的定义 定义函数,不是指针 格式:类型 *标识符(参数表) 说明:类型为指针所指变量的类型,标识符为函数名,不是指针名,参数为函数的形参
本题求已给字符在给定的字符串中首次出现的位置和该字符的存储地址本题求已给字符在给定的字符串中首次出现的位置和该字符的存储地址 9 .5 指针与函数 char *str_chr(char *s,char ch); main() { char *p,ch=‘C’; char str[]=“Pascal,C,Fortran”; printf(“string starts at adress%s\n”,str); p=str_chr(str,ch); printf(“First occurrence of char%c is adress%u\n”,ch,p); printf(“This is position %u from0 \n”,p-str); } char *str_chr(char *s,char ch) { while (*s!=ch) s++; return(s); } ch=‘C’ 实参为数组名 str和字符ch 地址传递 移动指针
main() { 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)); } 说明一个指 针型函数 9.7 返回指针的函数 指向一个一维 数组的指针 数组名作函数参数 float search(float(*pointer)[4],int n) { float *pt; pt=*(pointer+n); return(pt); } 返回指针
a[3][4] 9 .5 指针与函数(完) 指向数组的指针 int (*pa)[4] 定义格式:int (*标识符)[N] 一个指针 四个元素 Pa+i 指向数组中的第 i 行 (不是第 i 列) pa+i 被调函数中:pt =*(pointer + n) ; 指向二维数组中的第 n 行 主调函数中: p=search(score,m); 函数的返回值赋给指针(行首地址),p++ 在该行移动,访问各列的元素
9 .6 指针型指针 一个指针,指向另一个指针 一 、指针型指针的定义: 1 格式: 类型 **标识符[长度] 2 说明: 类型为被指针型指针所指的指针所指的变量的类型 int *px=x x=100 *ppx=&x int x,*px,**ppx; px=&x; *px=x; ppx=&px; *ppx=px; **ppx=*px=x; int 100 &x &px int &px &x
指针与一维数组等价 指针数组与二维数组等价 二级指针与指针数组等价 9. 6 指针型指针 二 、 指针型指针的应用: 1. 对数据的多级间接访问 (一级间接访问) int i, *pi , **ppi ; pi=&i, *pi=i ppi=π *ppi=pi; **ppi=*(*ppi)=*pi=i; (二级间接访问) 2 指针型指针(二级指针)与指针数组,二维数组等价性
9. 6 指针型指针 指针数组,每个指 针用字符串的首 地址初始化 多 级 指 针 初 始 化 #define SIZE 4 main( ) { static char *p[ ]={ “program”,”basic”,”fortran”,”c language”}; char **pp; int i; for (i=0;i<SIZE;i++) { pp=p+i; printf(“%s\n”,*pp); } } 多级指针 用指针数组的数组名初始化
b c a s i ‘\0’ 9. 7 应用举例 指针 使用指针,编写完成字符串反向的递归函数 递归函数 算法 设计 b c c b 1. 将给定字符串分为两个部分: 第 1部分是第 1 个字符和最后一个字符(不是‘\0’) 第 2部分是从第 2 个字符到倒数第 2 个字符 2. (1)交换第 1 部分的两个字符。 (2)将第2部分构成一个字符串,递归 3. (1)定义两个字符指针,分别指向字符串的首字符和最后一个字符 (2) 将所指的两个字符交换 (3) 使中间部分构成一个“新”的字符串,递归
9. 7 应用举例 revstr( char *s ) { char *p =s , c; while ( * p ) p++; p- -; if ( s < p ) { c = *s; *s = *p; * p= ‘\0’; revstr(s+1); *p=c; } } 将指针移到‘\0’之前的最后一个字符 将最后一个移到字符串的最前面 形成一个新的字符串 以新串的起始地址s+1进行递归
第九章 总结 1 指针就是地址 2 指针可以指向任何数据类型,指向谁,就存谁的地址 3 必须用地址值为指针变量初始化(&变量名,&a,或数组名),不许用整数 4 相同类型的指针可以相互赋值 概念
本章介绍的指针类型: 变量的指针 *pi , i, pi=&i; 数组的指针 *pa ,a[10], pa=a, pa+i=a+i=&a[i] 指针数组 *pa[4] 指针的指针 **ppa; 函数的指针 (*pf)( ) 指针型函数 *f( a,b)