第八章 指 针 北京邮电大学出版社
内容提要 指针与指针变量 指针与函数 指针与数组 指针与字符串 指针数组与命令行参数
基本概念--地址 地址:内存单元的编号 (图示) 数据在内存中的存储 (图示) 综合图示 地址:内存单元的编号 (图示) 数据在内存中的存储 (图示) 定义变量 --> 分配内存单元(将变量名转换为地址) --> 赋值 综合图示
基本概念--指针 变量的三要素:名字、类型与值 数据的读写 指针 (pointer) (图示) 每个变量都通过变量名与相应的存储单元相连系,具体分配哪些单元给变量,由C编译系统完成变量名到对应内存单元地址的变换 变量分配存储空间的大小由类型决定 变量的值则是指相应存储单元的内容 数据的读写 根据变量的地址 读写内存单元的内容 指针 (pointer) (图示) 变量的地址
基本概念—间接访问 直接存储:根据变量地址存取变量值 间接存储:根据指针变量存取变量值 变量访问图示 变量地址 内存单元 变量地址 内存单元 间接存储:根据指针变量存取变量值 指针变量 变量地址 内存单元 变量访问图示
10 例 k=i; k=*i_pointer; 例 k=i; --直接访问 k=*i_pointer; --间接访问 10 …... 指针变量 …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 整型变量k 10
基本概念—指向 指针和指针变量 指针:是一个变量的地址 指针变量:用来存储变量地址(指针)的变量 指向 变量的地址(指针) 变量的内存单元
指针变量--(1) 变量的指针:变量的地址 指针变量:用来存储变量地址的变量 指针变量 另一变量 指针与指针变量
指针变量--(2) 指针变量的定义 一般形式: [存储类型] 数据类型 *指针名; 例 int *p1,*p2; float *q ; 一般形式: [存储类型] 数据类型 *指针名; 例 int *p1,*p2; float *q ; static char *name; 表示定义指针变量 不是‘*’运算符 合法标识符 指针变量本身的存储类型 指针的目标变量的数据类型 注意: 1、int *p1, *p2; 与 int *p1, p2; 2、指针变量名是p1,p2 ,不是*p1,*p2 3、指针变量只能指向定义时所规定类型的变量 4、指针变量定义后,变量值不确定,应用前必须先赋值
指针变量--(3) 两个指针运算符:& * 指针变量的类型决定了它所能指向的变量的类型 指针变量存放的是所指向的某个变量的地址值,而普通变量保存的是该变量本身的值 指针变量并不固定指向一个变量,可指向同类型的不同变量 两个指针运算符:& * & : 取变量的地址 p=&i ; * : 取指针所指向的内容 j=*p ;
指针变量--(4) 10 i_pointer = &i = &(*i_pointer) i_pointer &i &(*i_pointer) 2000 10 i_pointer *i_pointer &i_pointer i …... 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---指针变量占用内存的地址 注意:*与&具有相同的优先级,结合方向从右到左。这样,&*p即&(*p),是对变量*p取地址,它与&a等价;p与&(*p)等价,a与*(&a)等价
指针运算--(1) 指针变量的初始化 例 int i; int *p=&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型指针
指针运算--(2) 指针的赋值运算 将变量地址值赋给指针变量 (举例) 相同类型的指针变量之间的赋值 (举例) 综合示例1
指针变量必须先赋值,再使用 例 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); }
指针运算--(3) 零指针与空类型指针 零指针:(空指针) p指向地址为0的单元, 系统保证该单元不作它用 表示指针变量值没有意义 定义:指针变量值为零 表示: 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; 使用时要进行强制类型转换
指针运算--(4) 指针的算术运算 加减运算:一个指针可以加、减一个整数n,其结果与指针所指对象的数据类型有关。指针变量的值(地址)增加或减少“n×sizeof(指针类型)” (举例)
指针运算--(5) 指针的关系运算 指针进行关系运算之前,指针必须初始化,另 外,只有同类型的指针才能进行比较 和基本类型变量一样,指针能进行关系运算 例如:p > q,p < q,p == q,p != q,p >= q等 指针的关系运算在指向数组的指针中广泛的运用,假设 p、q是指向同一数组的两个指针,执行p>q的运算,其含义为,若表达式结果为真(非0值),则说明p所指元素在q所指元素之后。或者说q所指元素离数组第一个元素更近些。 指针进行关系运算之前,指针必须初始化,另 外,只有同类型的指针才能进行比较
指针运算--(6) 指针变量自增、自减运算具有上述运算的特点,但有前置后置、先用后用的考虑,务请小心。 例如: int a[10], *p=a, *x; x=p++; /* x第一个元素分量, p指向第二个元素*/ x=++p; /* x、 p均指向数组的第二个分量*/ * p++相当于*(p+ +)。 *(p++)与(*p)++ 含义不同,前者表示地址自增,后者表示当前所指向的数据自增。
2. *&a的含意是什么? (答:a ) 3. (*p)++相当于什么? (答:a++ ) 思考: 1.若有定义 int a,*p; 执行了“p=&a”,则: “&*p”的含意是什么? (答:相当于&a ) 2. *&a的含意是什么? (答:a ) 3. (*p)++相当于什么? (答:a++ )
多级指针 二级指针 -说明形式: -(举例) 存储类型 数据类型 **指针名
多级指针 定义: 指向指针的指针 一级指针:指针变量中存放目标变量的地址 例 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(整型变量) 二级指针 一级指针 目标变量 二级间接寻址
指针本身的存储类型 最终目标变量的数据类型 i p1 p2 3 &i &p1 **p2, *p1 *p2 定义形式:[存储类型] 数据类型 **指针名; 如 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;
指针作为函数参数 函数行参为指针变量,指针变量作实参 函数行参为指针变量,变量地址作为实参 (举例1) 函数行参为指针变量,变量地址作为实参 (举例2) 注1:在函数中进行对形参指针的交换不能影响到实参 (举例3 ) 注2:函数中交换值时不能使用无初值的指针变量作临时变量。(举例4)
指针函数 指针函数:是指返回值为指针的函数 定义形式:类型名 *函数名(参数表列) ; 程序举例 { 函数体语句 } 定义形式:类型名 *函数名(参数表列) ; 例如: int *fun(int a,int b) { 函数体语句 } 程序举例
指向函数的指针(1) 一个函数包括一组指令序列存储在某一段内存中,这段 内存空间的起始地址称为函数的入口地址 称函数入口地址为函数的指针。函数名代表函数的入口地址 可以定义一个指针变量,其值等于该函数的入口地址,指向这个函数,这样通过这个指针变量也能调用这个函数。这种指针变量称为指向函数的指针变量 指向函数的指针变量 定义:数据类型 (*指针变量名)( ) 如: int (*p)( ); 用指针调用函数的形式:(*指针变量)(实参表) 程序举例 指向函数的指针的使用步骤
指向函数的指针(2) 注意: 用函数指针调用函数是间接调用,没有参数类型说明,C编译系统也无法进行类型检查,因此,在使用这种形式调用函数时要特别小心。实参一定要和指针所指函数的形参类型一致。 函数指针可以作为函数参数,此时,当函数指针每次指向不同的函数时,可执行不同的函数来完成不同的功能
指向一维数组的指针(1) 数组名是一个常量指针,它的值为该数组的首地址 指向数组的指针的定义方法与指向基本类型变量的指针的定义方法相同, 例如: int a[10]={1,3,5,7,9}; int *p; p=&a[2]; (把数组元素a[2]的地址赋给指针变量p) p=a; (把数组的首地址赋给指针变量p)
指向一维数组的指针(2) C语言规定:数组名代表数组首地址,是一个地址常量。 因此,下面两个语句等价: p=&a[0]; p=a; 在定义指针变量的同时可赋初值: int a[10], *p=&a[0]; (或 int *p=a;) 等价于:int *p; p=&a[0]; 两句。
指向一维数组的指针(3) 指向数组的指针变量p a+0 &a[0] 1 3 15 17 19 p a[0] : a[9] p[9]
指向一维数组的指针(4) 通过指针引用数组元素 如果p的初值为&a[0],则: *p=5; 表示对p当前所指的数组元素赋以一个值5。 C规定:p+1指向数组的下一元素(而不是将p值简单地加1)。p+1意味着使p的原值(地址)加d个字节(d为一个数组元素所占的字节数)。 如果p的初值为&a[0],则: p+i和a+i就是a[i]的地址,或者说它们指向a数组的第i个元素(图示)。 *(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。 指向数组的指针变量也可以带下标,如p[i]与*(p+i)、 a[i]等价
指向一维数组的指针(5) 例: 用三种方法输出数组全部元素。 (1)下标法 main() { int a[10]; int i; for (i=0;i<10;i++) scanf("%d",&a[i]); printf("\n"); printf("%d", a[i] ) ; }
指向一维数组的指针(6) (2)通过数组名计算数组元素地址,输出元素的值 main() { int a[10]; int i; for (i=0;i<10;i++) scanf("%d",&a[i]); printf("\n"); printf("%d", *(a+i) ); }
指向一维数组的指针(7) (3)用指针变量指向数组元素 图示 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 ); } 三种方法的比较: 用下标法比较直观,能直接知道是第几个元素; 而使用指针法则执行效率更高 图示
指向一维数组的指针(8) 使用指针变量时,应注意: 指针变量可实现使本身的值改变。 要注意指针变量的当前值 不能&p 增加:p=a; P++合法;但a++不合法(a是数组名,代表数组首地址,在程序运行中是固定不变的。) 要注意指针变量的当前值 main() {int a[10]; int *p, i; p=a; for ( ;p<a+10;p++) scanf("%d",p); printf("\n"); for ( ;p<(a+10);p++) printf("%d", *p ); } 不能&p 增加:p=a;
指向一维数组的指针(9) *p++相当于*(p++),因为*与++优先级相同,且结合方向从右向左,其作用是先获得p指向变量的值,然后执行p=p+1; *(p++)与*(++p)意义不同,后者是先p=p+1,再获得p指向的变量值 若p=a,则输出*(p++)是先输出a[0],再让p指向a[1];输出*(++p)是先使p指向a[1],再输出p所指的a[1] (*p)++表示的是将p指向的变量值+1 举例
例 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) ()
指向二维数组的指针 char a[3] [4]; a是一个长度为3的数组 数组元素是长度为4的数组 a[0] a[1] a[2] a、a+1、a+2都是指针,它们的基类型是长度为4的字符数组,它们与下面定义的指针p同类型 char (*p)[4]; a *a a+1 *(a+1) a+2 *(a+2)
int a[3][4]; 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个 元素 基类型
int a[3][4]; a a+1 a+2 a[0] a[1] a[2] 2000 2008 2016 2002 2010 2018 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]; 地址表示: (1) a+1 (2) &a[1][0] (3) a[1] (4) *(a+1) 行指针 列指针 地址表示: (1) &a[1][2] (2) a[1]+2 (3) *(a+1)+2 (4)&a[0][0]+1*4+2 二维数组元素表示形式: (1)a[1][2] (2)*(a[1]+2) (3)*(*(a+1)+2) (4)*(&a[0][0]+1*4+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列元素值
指向二维数组的指针变量(1) 指向数组元素的指针变量。这种变量的定义与普通指 针变量定义相同,其类型与元素数值类型相同 如: int *p,a[3][4]; p=&a[1][2]; 指向一维数组(二维数组的一行)的指针,也称行指 针 定义形式为: 类型标识符 (*指针变量名)[元素个数] 如:int (*p)[4];
指向二维数组的指针变量(2) 注意: (1)int (*p)[4]; 定义一个指针变量p ,p 指向包含4个元素的一维数组。 (2)p+i与 *(p+i)的区别: p+i是指向第i行的指针(第i行的首地址); *(p+i)是指向第i行第1个元素的地址; 两者数值相等,但含义不同:p+i 的增值将以行长为单 位,而*(p+i)增值将以元素长度为单位
指向二维数组的指针变量(3) 即:p+i+1将指向第i行再下一行的首地址,而*(p+i)+1将指向第i行首元素的下一个元素地址。(见下图) 设 int a[3][4],(*p)[4]; p=a; P,a P+1 P+2 a[0] a[1] a[2] 如果p先指向a[0],则p+1不是指向a[0][1],而是指向a[1]
字符串的指针表示法 指向字符串的指针称字符指针 利用指针来对字符串进行操作: 定义形式:char *指针名 通过在定义时初始化指针变量使指针指向一个字符串 程序举例 用指针变量实现对字符串的访问 程序举例
字符串数组 字符串数组:数组中的每个元素都是一个存放字符串的数组 字符串数组可以用一个二维字符数组来存储 程序举例 例如:char language[3] [10] 数组的第一个下标决定字符串的个数,第二个下标是字符串的最大长度(实际最多9个字符,‘\0’占一位置) 程序举例
指针数组(1) 指针数组是指针变量的集合 定义形式:类型标识符 *数组名[数组长度说明] 字符数组和指针变量,需要注意事项: 举例 如: int *p[10]; 内存存储示例 字符数组和指针变量,需要注意事项: 字符数组中每个元素可存放一个字符,而字符指针变量存放字符串首地址,千万不要认为字符串是存放在字符指针变量中的 对字符数组而言,与普通数组一样,不能对其进行整体赋值,只能给各个元素赋值,而字符指针变量可以直接用字符串常量赋值 举例
指针数组(2) 注意: (1)字符数组中每个元素可存放一个字符,而字符指针变量存放字符串首地址,而不是存放在字符指针变量中。 (2)对字符数组,与普通数组一样,不能对其进行整体赋值,只能给各个元素赋值,而字符指针变量可以直接用字符串常量赋值。例如,若有如下定义: char a[10]; char *p; 则语句 a=”computer”;是非法的,因为数组名a是一个常量指针,不能对其赋值。只能对各个元素分别赋值: a[0]=’c’;a[1]=’o’;a[2]=’m’;a[3]=’p’;……;a[7]=’r’; 但语句: p=”computer”; 是合法的。
指针数组与命令行参数(1) 在操作系统命令状态下可以输入程序或命令使其运行,称命令行状态。输入的命令(或运行程序)及该命令(或程序)所需的参数称为命令行参数 如: copy fd fs copy是文件拷贝命令,fd、fs是命令行参数 main函数是可以有参数的,但与普通函数不同 带形参的main( )函数的一般形式是: main (int argc, char *argv[ ]) 形参argc记录命令行中字符串的个数,argv是一个字符型指针数组,每一个元素顺序指向命令行中的一个字符串
指针数组与命令行参数(2) main()函数的形参与实参 举例 main()函数由系统自动调用,而不是被程序内部的其它函数调用, main()函数所需的实参不可能由程序内部得到,而是由系统传送 main()函数所需的实参与形参的传递方式也与一般函数的参数传递不同,实参是在命令行与程序名一同输入,程序名和各实际参数之间都用空格分隔 格式为:执行程序名 参数1 参数2 …… 参数n 形参argc为命令行中参数的个数(包括执行程序名),其值大于或等于1,而不是象普通C语言函数一样接受第一个实参 形参argv是一个指针数组,其元素依次指向命令行中以空格分开的各字符串 即:第一个指针argv[0]指向的是程序名字符串,argv[1]指向参数1,argv[2]指向参数2,……,argv[n] 指向参数 举例
综合示例 例 输入一个十进制正整数,将其转换成二进制、八进制、十六进制数输出 分析: 将十进制数n转换成r进制数的方法是:n除以r取余数作为转换后的数的最低位。若商不为0,则商继续除以r,取余数作为次低位……,以此类推,直到商为0为止 对于十六进制数中大于9的六个数字是用A,B,C,D,E,F来表示 所得余数序列转换成字符保存在字符数组a中 字符‘0’的ascii码是48,故余数0~9只要加上48就变成字符‘0’~‘9’了;余数中大于9的数10~15要转换成字母,加上55就转换成‘A’、‘B’、‘C’、‘D’、‘E’、‘F’了 由于求得的余数序列是低位到高位,而屏幕显示先显示高位,所以输出数组a时要反向进行
综合示例(2) #include "stdio.h" main() {int i,radix; long n; char a[33]; void trans10_2_8_16(char b[],long m,int base); printf("\nInput radix(2,8,16):");/* 输入转换基数*/ scanf("%d",&radix); printf("\nInput a positive integer:");/*输入被转换的数*/ scanf("%ld",&n); trans10_2_8_16(a,n,radix); for (i=strlen(a)-1;i>=0; i--) /*逆向输出字符串*/ printf("%c",*(a+i)); /* *(a+i)即a[i] */ puts("\n"); }
void trans10_2_8_16(char *p,long m,int base) { int r; while (m>0) { r=m%base; /* 求余数 */ if (r<10) *p=r+48; /* 小于10的数转换成字符后送p指向的元素 */ else *p=r+55; /* 数10~15 转换成A~F 后送p指向的元素*/ m=m/base; p++; /*指针下移*/ } *p='\0'; /*在最后加上字符串结束标志*/ 输入:Input radix(2,8,16):16 Input a positive integer:435678 输出:6A5DE
本章结束 同学们: 再见!
*(p+i) a数组 a[0] a[1] a[2] a[i] a[9] p p+1,a+1 p+i, a+i p+9, a+9 综上所述,引用一个数组元素有二法: (1)下标法:如a[i]形式; (2)指针法:如*(a+i)或 *(p+i)。其中a是数组名,p是指向数组的指针变量,其初值p=a。
变量与地址 i k 程序中: int i; float k; 变量是对程序中数据 存储空间的抽象 内存中每个字节有一个编号-----地址 …... 2000 2001 2002 2005 内存 2003 程序中: int i; float k; i 编译或函数调用时为其分配内存单元 k 变量是对程序中数据 存储空间的抽象
指针与指针变量 10 变量的内容 变量的地址 指针变量 变量地址(指针) 地址存入 指向 变量 变量值 指针 …... 整型变量i 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 变量的内容 变量的地址 2000 指针变量 指针变量 变量 变量地址(指针) 变量值 指向 地址存入
指针变量的初始化 main( ) { int a=18 , *p=&a ; printf(“a=%d\n”, a); printf(“*p=%d\n”, *p); } a=18 *p=18 4000 p a(4000) 18 *p
内存的地址编号 0000 … … 2002 00001011 11000001 2003 00000000 2004 11101011 2005 01110000 2006 …
内存中数据的存储(1) main( ) { int i=1 , j=2 , k=3 ; printf("%d, %d, %d " , i , j , k); } F9 开始编译
内存中数据的存储(2) 202 1467 -18 -292 1 int i=1 , j=2 , k=3;(编译) 2002 i 2004 j 2006 k 2008 2010 Ctrl+F9运行
内存中数据的存储(3) 1 2 3 -292 int i=1,j=2,k=3;(运行) 2002 i 2004 j 2006 k 2008 2010
内存中数据的存储(4) 00000001 00000000 00000010 00000011 int i=1,j=2,k=3;(实际情况) 2002 i 2003 2004 j 2005 2006 k 2007
内存中数据的存储(6) (程序结束,恢复原状) -292 1 2002 202 2004 1467 -18 2006 2008 2010
变量的访问 例 i=3; -----直接访问 10 例 *i_pointer=20; -----间接访问 …... 整型变量i 2000 指针变量 …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 例 i=3; -----直接访问 20 3 例 *i_pointer=20; -----间接访问
直接访问 int i=1,j=2,k=3; 202 2002(&i) i 1 2004 2006 -292 2008 1 2010
指针—变量的地址 1 2 3 -292 1 int i=1,j=2,k=3; i j k 2008 2010 2002 ( &i )
间接访问 1 1 int i=1 ; p=&i ; i p 2002 2002 ( &i ) p 2002 2002 3000 ( &p)
指向 int i=1; 2000 i 2002(&i) 1 2004 2006
指针运算 i j p int i=1 , j ; int *p ; p=&i , j=*p ; 2002(&i) 1 2004 1 3000
指针变量的类型 int i=5; float j=2.5; int *point; point = &i ; (语法正确) point = &j ; (语法错误)
指针的赋值运算(1) int a,b,*pa,*pb; a=12;b=18; pa=&a;pb=&b a a pa &a 12 pa *pa
指针的赋值运算(2) a pa &a 12 *pa b pb 18 &b *pb int a,b,*pa,*pb; float *pf; pa=&a;pb=&b; pb=pa; a pa &a 12 *pa b pb 18 &b *pb 注意:只有相同类型的指针变量才能相互赋值,如pf=pa;是不允许的。因为pa是整型指 针,pf是浮点型指针
5 9 main() { int *p1,*p2,*p,a,b; scanf("%d,%d",&a,&b); p1=&a; p2=&b; 例 输入两个数,并使其从大到小输出 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
2 2 2 4 4 4 6 6 6 例:指针的算术运算 int *p,a=2,b=4,c=6; p=&a; p=p+2 p 4000 p 4004 加减运算常用于数组的处理。对指向一般数据的指针,加减运算无实际意义。 例如: int a[10],*p=a,*x;x=p+3; /*实际上是p加上3*2个字节赋给x, x指向数组的第三个分量*/ 对于不同基类型的指针,指针变量“加上”或“减去”一个整数n所移动的字节数是不同的。 例如:float a[10], *p=a, *x;p=p+3; /*实际上是p加上3*4个字节赋给x, x依然指向数组的第三个分量*/
多级指针 pp(4800) p(4100) a(4000) 程序执行结果: *p=22 **pp=22 #inlude <stdio.h> mian() { int a=22,*p,**pp; p=&a; pp=&p; printf(“*p=%d\n”,*p); printf(“**pp=%d\n”,**pp); } 假设变量a的地址为4000,指针p的地址为4100,二级指针pp的地址为4800。 pp(4800) p(4100) a(4000) &p=4100 &a=4000 22 程序执行结果: *p=22 **pp=22
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); 例 将数从大到小输出 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
指针做函数参数(2) 程序执行结果: 输入:12 22 输出:a=22,b=12 #inlude <stdio.h> main() { int a,b; void swap(int *p1,int *p2); scanf(“%d%d”,&a,&b); swap(&a,&b); printf(“\n a=%d,b=%d\n=”,a,b); } void swap(int *p1,int *p2) { int p; p = *p1; *p1 = *p2; *p2 = p; } 程序执行结果: 输入:12 22 输出:a=22,b=12
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("%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 *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); 例 将数从大到小输出 …... 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
指针函数 程序执行结果: 输入:15 10 输出:min=10 minp=10 #include 〈stdio.h〉 void main() int *min(int x,int y) { if(x<y)return(&x); /*x的地址作为指针函数的返回值*/ else return(&y); } #include 〈stdio.h〉 void main() { int a,b,*p; int *min(int x,int y);/*函数提前引用说明*/ int *minp(int *,int *);/*函数提前引用说明*/ scanf(″%d,%d″,&a,&b); p=min(a,b); /*返回最小值指针*/ printf(″\nmin=%d″,*p);/*输出最小值*/ p=minp(&a,&b); /*注意minp的形参类型*/ printf(″\nminp=%d″,*p); /*输出最小值*/ } int *minp(int *x,int *y) { int *q; q=*x<*y?x:y; return (q); /*指针变量q作为指针函数的返回值*/ } 程序执行结果: 输入:15 10 输出:min=10 minp=10 返回值的类型要与函数类型一致
指向函数的指针(1) 程序执行结果: 输出:sump=80.00 sumf=80.00 #include 〈stdio.h〉 #define M 8 void main() { float sumf,sump; float a[M]={11,2,-3,4.5,5,69,7,80}; float (*p)();/*定义指向函数的指针p*/ float max(float a[],int n);/*函数声明*/ p=max; /*函数名(函数入口地址)赋给指针p*/ sump=(*p)(a,M); /*用指针方式调用函数*/ sumf=max(a,M); /*用函数名调用max()*/ printf(″sump=%.2f\n″,sump); printf(″sumf=%.2f\n″,sumf); } float max(float a[],int n) {int k; float s; s=a[0]; for (k=0;k<n;k++) if (s<a[k]) s=a[k]; return s; } 程序执行结果: 输出:sump=80.00 sumf=80.00
指向函数的指针(2) 步骤一:定义一个指向函数的指针变量,形如- float (*p)() 步骤二:为函数指针赋值,格式如下- p=函数名; 注意:赋值时只需给出函数名,不要带参数 步骤三:通过函数指针调用函数,函数格式如下- s=(*p)(实参)
利用定义时初始化指针变量 void main() { char*p= ″computer″; printf (″%s″,p); }
指针变量对字符串的访问 输入:3↙ 输出: computer mputer #include 〈string,h〉 void main() { int i,n; char a[]=″computer″; char b[10],*p,*q; p=a; q=b; scanf(″%d″,&n); if (strlen(a)>=n) p+=n-1;/*指针指向要复制的第一个字符 */ for (;*p!=′\0′;p++,q++) *q=*p; *q=′\0′;/* 字符串以′\0′ 结尾 */ printf(″String a : %s\n″,a); printf(″String b : %s\n″,b); } 输入:3↙ 输出: computer mputer
字符串数组 char language[3] [10] /*数组的第一个下标决定字符串的个数,第二个下标是字符串的最大长度(实际最多9个字符,′\0′占一位置) */ char language[3][10]={″BASIC″,″C++″,″PASCAL″} /*可以对字符串数组赋初值*/ 内存存储情况如下:
指针数组对字符串的处理 B A S I C \0 C + \0 P A S C L \0 指针数组广泛应用于对字符串的处理: char *p[3] /*定义了一个具有3个元素p[0],p[1],p[2]的指针数组 ,每个元素都可以指向一个字符数组或字符串 */ char *p[3]= {″BASIC″,″C++″,″PASCAL″}; /*数组初始化*/ 存储结构如下: B A S I C \0 P[0] P[1] P[2] C + \0 P A S C L \0
字符指针数组举例(1) #include<stdio.h> #include<string.h> main() 例:有若干本书,将书名按字典顺序排序 #include<stdio.h> #include<string.h> main() { char *bname[ ]={"Programming in ANSI C","BASIC","Visual C++ 6.0 Programming ","TRUBO C 2.0"}; int i,m; void sort(char *name[],int); m=sizeof(bname)/sizeof(char *); /*字符串个数*/ sort(bname,m); /* 排序,改变指针的连接关系*/ printf("\n"); for (i=0;i<m;i++) /* 输出排序结果*/ printf("%8s",bname[i]); }
字符指针数组举例(2) void sort(char *name[], int n) /*选择排序*/ { char *t; int i,j,k; /* 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; /* 第j个元素更小*/ if (k!=i) /* 最小元素是该趟的第一个元素 则不需交换 */ { t=name[i];name[i]=name[k];name[k]=t;} } } 输出结果为: BASIC Programming in ANSI C TRUBO C 2.0 Visual C++ 6.0 Programming
命令行参数 #include 〈stdio.h〉 void main (int argc ,char * argv []) { int i=0; printf(″argc=%d\n″,argc); while(argc >=1) { printf(″\n parameter %d:%s″,i,*argv); i++; argc--; argv++;}} 运行该程序时命令行输入的是: examTurbo_cC++Vc↙ 输出结果为: argc=4 parameter 0:exam parameter 1:Turbo_c parameter 2:C++ parameter 3:Vc 程序开始运行后,系统将命令行中的字符串个数送入argc,将4个字符串实参exam、Turbo_c、C++、Vc的首地址分别传给字符指针数组元素argv[0], argv[1],argv[2],argv[3]。如下图所示:
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)
例 注意指针变量的运算 例 void main() { int a []={5,8,7,6,2,7,3}; int y,*p=&a[1]; 例 注意指针变量的运算 例 void main() { int a []={5,8,7,6,2,7,3}; int y,*p=&a[1]; y=(*--p)++; printf(“%d ”,y); printf(“%d”,a[0]); } 5 8 7 6 2 3 1 4 a p 6 p 输出:5 6