Download presentation
Presentation is loading. Please wait.
1
第七章 指针 教 材: C程序设计导论 主 讲: 谭 成 予 武汉大学计算机学院
2
本讲重点 指针定义和引用 地址运算 数组与指针关系、字符指针 多级指针 指针做函数参数、命令行参数
3
指针的优点 C程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值
4
指针与地址 i k 变量与地址 程序中: int i; float k; 变量是对程序中数据 存储空间的抽象
内存中每个字节有一个编号-----地址 …... 2000 2001 2002 2005 内存 2003 程序中: int i; float k; i 编译或函数调用时为其分配内存单元 k 变量是对程序中数据 存储空间的抽象
5
指针与指针变量 指针:一个变量的地址; 指针变量:专门存放变量地址的变量。
6
指针与指针变量 10 变量的内容 变量的地址 指针 …... 整型变量i 2000 2001 2002 2003 2004
2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 变量的内容 变量的地址 2000 指针变量
7
指针与指针变量 指针变量 变量 变量地址(指针) 变量值 指向 地址存入
8
直接访问与间接访问 直接访问:按变量地址存取变量值 间接访问:通过存放变量地址的变量去访问变量 例 i=3; -----直接访问 10
指针变量 …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 例 i=3; 直接访问 20 3 例 *i_pointer=20; 间接访问
9
指针变量的定义和引用 指针变量的定义 一般形式: [存储类型] 数据类型 *指针名; 表示定义指针变量 不是‘*’运算符 合法标识符
3 变量i 2000 i_pointer *i_pointer i &i i=3; *i_pointer=3 指针变量的定义 一般形式: [存储类型] 数据类型 *指针名; 表示定义指针变量 不是‘*’运算符 合法标识符 指针变量本身的存储类型 指针的目标变量的数据类型
10
指针变量的定义和引用 例 int *p1,*p2; float *q ; static char *name; 注意:
1、int *p1, *p2; 与 int *p1, p2; 2、指针变量名是p1,p2 ,不是*p1,*p2 3、指针变量只能指向定义时所规定类型的变量 4、指针变量定义后,变量值不确定,应用前必须先赋值
11
指针变量的初始化 一般形式:[存储类型] 数据类型 *指针名=初始地址值; 例 int i; int *p=&i; 赋给指针变量,
一般形式:[存储类型] 数据类型 *指针名=初始地址值; 例 int i; int *p=&i; 赋给指针变量, 不是赋给目标变量 变量必须已说明过 类型应一致 例 int i; int *p=&i; int *q=p; 例 int *p=&i; int i; 用已初始化指针变量作初值 例 int main( ) { int i; static int *p=&i; } () 不能用auto变量的地址 去初始化static型指针
12
#include<stdio.h> int main() { int i=10; int *p; *p=i;
printf("%2d",*p); return 0; } 指针变量必须先赋值,再使用 …... 2000 2004 2006 2005 整型变量i 10 指针变量p 2001 2002 2003 随机 危险! #include<stdio.h> int main( ) { int i=10,k; int *p; p=&k; *p=i; printf("%2d",*p); return 0; }
13
零指针与空类型指针 零指针:(空指针) p指向地址为0的单元, 系统保证该单元不作它用 表示指针变量值没有意义 #define NULL 0
定义:指针变量值为零 表示: int * p=0; p指向地址为0的单元, 系统保证该单元不作它用 表示指针变量值没有意义 #define NULL 0 int *p=NULL:
14
零指针与空类型指针 p=NULL与未对p赋值不同 用途: 避免指针变量的非法引用 在程序中常作为状态比较 例 int *p; ......
while(p!=NULL) { … }
15
零指针与空类型指针 表示不指定p是指向哪一种 类型数据的指针变量 void *类型指针 例 char *p1; void *p2;
使用时要进行强制类型转换 例 char *p1; void *p2; p1=(char *)p2; p2=(void *)p1;
16
空指针到底是什么? 每一种指针类型都有一个特殊值—— “空指针” :它与同类型的其它所有指针值都不相同, 它“与任何对象或函数的指针值都不相等”。对malloc() 的成功调用也不会返回空指针, 如果失败, malloc() 的确返回空指针, 这是空指针的典型用法:表示“未分配” 或者“尚未指向任何地方” 的指针。 空指针不同于未初始化的指针。 空指针可以确保不指向任何对象或函数; 未初始化指针则可能指向任何地方。 每种指针类型都有一个空指针, 而不同类型的空指针的内部表示可能不尽相同。尽管程序员不必知道内部值, 但编译器必须时刻明确需要那种空指针, 以便在需要的时候加以区分。
17
指针变量的引用:&与*运算符 两者关系:互为逆运算 理解 10 i_pointer &i &(*i_pointer)
含义 含义: 取指针所指向变量的内容 单目运算符 优先级: 2 结合性:自右向左 含义: 取变量的地址 单目运算符 优先级: 2 结合性:自右向左 两者关系:互为逆运算 理解 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---指针变量占用内存的地址
18
10 例 k=i; --直接访问 例 k=i; k=*i_pointer; --间接访问 k=*i_pointer; 10 …...
指针变量 …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 整型变量k 10
19
例 指针的概念 #include<stdio.h> int main() { int a; int *pa=&a; a=10;
&a:f86(hex) pa:f86(hex) &pa:f88(hex) 例 指针的概念 #include<stdio.h> int main() { int a; int *pa=&a; a=10; printf("a:%d\n",a); printf("*pa:%d\n",*pa); printf("&a:%x(hex)\n",&a); printf("pa:%x(hex)\n",pa); printf("&pa:%x(hex)\n",&pa); return 0; } …... f86 f8a f8c f8b 整型变量a 10 指针变量pa f87 f88 f89
20
例7.1 输入两个数,并使其从大到小输出. 5 9 …... #include<stdio.h> int main()
例7.1 输入两个数,并使其从大到小输出. …... 指针变量p1 指针变量p 2000 2008 2002 2004 2006 指针变量p2 整型变量b 整型变量a #include<stdio.h> int 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); return 0; } 2006 2008 2006 2008 2006 5 9 5,9 max=9,min=5
21
动态存储分配 实际编程中可能遇到所需内存空间无法预先确定的情况,需要根据实际的数据的多少来确定。
C语言提供动态分配内存函数 (stdlib.h)。 malloc()函数 void *malloc(unsigned int size); 功能:在动态存储区域中分配一个size字节的连续空间。 函数的返回值为指针,它的值是所分配的存储区域的起始地址。 如没有足够的存储空间分配,则返回0(记为NULL)值。
22
动态存储分配 /*L7-2.C:动态分配内存范例*/ #include <stdio.h>
#include <stdlib.h> char count,*ptr,*p; int main() { ptr=(char *)malloc(30*sizeof(char)); if(ptr==NULL){ puts("memory allocation error."); return (1); } p=ptr; for(count=65;count<91;count++) *p++=count; *p='\0'; puts(ptr); free(ptr); system("PAUSE"); return (0); ABCD……XYZ
23
动态分配内存 calloc()函数 calloc函数的原型为: void *calloc(unsigned int n, unsigned int size); 函数的功能是:在动态存储区域中分配n个为size字节的连续空间,并将该存储空间自动置初值0。函数的返回值为指针,它的值是所分配的存储区域的起始地址。如分配不成功,则返回0值。 n 表示对象的个数 size 每个对象占用内存单元的字节数
24
#include <stdio.h> #include <stdlib.h> int main(){
unsigned num; int *ptr,I,*p; printf("enter the number of type int to allocate:"); scanf("%d",&num); ptr=(int *)calloc(num,sizeof(int)); if(ptr!=NULL){ puts("memory allocation was successful."); p=ptr; for(I=0;I<num;I++) *p++=I; printf("%d",ptr[I]); printf("\n"); } else puts("memory allocation error."); free(ptr); return (0); enter the number of type int to allocate:20
25
动态分配内存 free()函数 void free(void *ptr) ;
函数的功能是:释放由ptr所指向的存储空间。ptr的值必须是malloc或calloc函数返回的地址。此函数无返回值。
26
动态分配内存 realloc函数的作用是对p所指向的存储区进行重新分配即改变大小;
realloc函数 realloc函数的原型为: void *realloc(void *ptr,size_t size); realloc函数的作用是对p所指向的存储区进行重新分配即改变大小; ptr: 是已经由malloc或calloc函数分配的存储区的指针; size: 是重新分配的存储区的大小(字节数),新存储区包含着和旧存储区相同的内容,如果新存储区较大,则新增加的部分未被初始化。 此函数返回值是新存储区的首地址;如果没有足够的内存空间则返回NULL,此时旧存储区内容不变。
27
动态存储分配函数的使用举例 /*L7-3.C*/ #include <stdio.h>
#include <string.h> #include <stdlib.h> int main(void) { char *p; /*定义字符指针*/ p=malloc(19); /*申请30个字节的存储区*/ if(!p){ /*检查返回指针的有效性*/ printf(“Allocation error\n”); exit(1); } /*指针无效,终止程序运行*/ strcpy(p,”this is an example”); printf(“%x,%s\n”,p,p); p=realloc(p,20); /*申请重新分配存储区*/ if (!p) { exit(1); } strcat(p,”.”); printf(“%x,%s\n”,p,p); /*输出字符串首地址和内容*/ free(p); /*释放存储区*/ return 0; } 动态存储分配函数的使用举例
28
动态存储分配函数的使用举例 /*L7-4.C*/ #include <stdio.h>
#include <stdlib.h> int *get_mem(void) { int *p; p=calloc(100,sizeof(int)); if (!p) { printf(“Allocation error\n”); exit(1); /*指针无效,终止程序运行*/ } return p;
29
free() 怎么知道有多少字节需要释放?
malloc/free 的实现会在分配的时候记下每一块的 大小, 所以在释放的时候就不必再考虑了。 calloc() 和malloc() 有什么区别?利用calloc的零填充功能安全吗?free() 可以释放calloc()分配的内存吗, 还是需要一个cfree()? calloc(m, n) 本质上等价于p = malloc(m * n); memset(p, 0, m * n); 填充的零是全零, 因此不能确保生成有用的空指针值或浮点零值。 free() 可以安全地用来释放calloc() 分配的内存。
30
本讲重点 指针定义和引用 地址运算 数组与指针关系、字符指针 多级指针 指针做函数参数、命令行参数
31
指针的运算 指针变量的赋值运算 p=&a; (将变量a地址p) 如 int i, *p; p=array; (将数组array首地址p)
p=&array[i]; (将数组元素地址p) p1=p2; (指针变量p2值p1) 不能把一个整数p, 也不能把p的值整型变量 所有类型的指针都可置为NULL 如 int i, *p; p=1000; () i=p; () 指针变量与其指向的变量具有相同数据类型
32
指针的算术运算 pi p id (i为整型数,d为p指向的变量所占字节数)
p++, p--, p+i, p-i, p+=i, p-=i等 若p1与p2指向同一数组,p1-p2=两指针间元素个数(p1-p2)/d p1+p2 无意义
33
指针的算术运算 1 a数组 p 例 p指向float数,则 p+1 p+1 4 a[0] p+1,a+1 a[1]
p+i,a+i p+9,a+9 例 p指向float数,则 p+1 p+1 4 例 p指向int型数组,且p=&a[0]; 则p+1 指向a[1] 1 例 int a[10]; int *p=&a[2]; p++; *p=1; 例 int a[10]; int *p1=&a[2]; int *p2=&a[5]; 则:p2-p1=3;
34
指针变量的关系运算 若p1和p2指向同一数组,则: p1<p2 表示p1指的元素在前; p1>p2 表示p1指的元素在后;
p==NULL或p!=NULL
35
例 7.5 堆栈(stack)是一种先进后出(first-in last-out)的表,好比将若干个盘子堆放起来,每次放或者取一个盘子,最先堆放的盘子最后被取走,将一个数据压入堆栈称为入栈,从堆栈中取走一个数据称为出栈操作。现在编程实现该算法。 …… stack[0] stack[1] stack[2] p1 push(10) p1 10 push(3) p1 3 pop() pop()
36
#include <stdio.h>
#include <stdlib.h> #define SIZE 50 void push(int i); /*入栈函数使用说明*/ int pop(void); /*出栈函数使用说明*/ int *tos,*p1,stack[SIZE]; /*堆栈、栈顶及栈底指针定义*/ int main(void) { int value; tos=stack; p1=stack; do{ printf(“输入一个整数:“); scanf(“%d”,&value); if (value!=0) push(value); else printf(“出栈数据是%d\n”,pop()); }while(value!=-1); return 0; }
37
/*将入栈操作定义成用户自定义函数*/ void push(int i) { p1++; if(p1==(tos+SIZE)) /*判断堆栈是否已满*/ { printf(“堆栈已满\n”); exit(1); } *p1=i; /*将出栈操作定义成自定义函数*/ int pop(void) { if(p1==tos) /*判断堆栈是否空*/ { printf(“堆栈空\n”); p1--; return *(p1+1);
38
本讲重点 指针定义和引用 地址运算 数组与指针关系、字符指针 多级指针 指针做函数参数、命令行参数
39
指针变量与数组 指向数组元素的指针变量 p 例 int array[10]; array[0] int *p; array[1]
... 整型指针p &array[0] p 例 int array[10]; int *p; p=&array[0]; // p=array; 或 int *p=&array[0]; 或 int *p=array; 数组名是表示数组首地址的地址常量, 就是数组的指针。
40
a[i] p[i] *(p+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] *(a+i) a[i] p[i] *(p+i) *(a+i)
41
例7.6 数组元素的引用方法 #include <stdio.h> int main() { int a[5],*pa,i;
例7.6 数组元素的引用方法 #include <stdio.h> int main() { int a[5],*pa,i; for(i=0;i<5;i++) a[i]=i+1; pa=a; printf("*(pa+%d):%d\n",i,*(pa+i)); printf("*(a+%d):%d\n",i,*(a+i)); printf("pa[%d]:%d\n",i,pa[i]); printf("a[%d]:%d\n",i,a[i]); return 0; } a[0] a[1] a[2] a[3] a[4] pa 1 2 3 4 5
42
例 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) ()
43
例7.7 注意指针变量的运算 例 int main() { int a []={5,8,7,6,2,7,3};
例7.7 注意指针变量的运算 5 8 7 6 2 3 1 4 a 例 int main() { int a []={5,8,7,6,2,7,3}; int y,*p=&a[1]; y=(*--p)++; printf(“%d ”,y); printf(“%d”,a[0]); return 0; } p 6 p 5 6
44
例7.8 注意指针的当前值 #include <stdio.h> int main() { int i,*p,a[7];
例7.8 注意指针的当前值 #include <stdio.h> int main() { int i,*p,a[7]; p=a; for(i=0;i<7;i++) scanf("%d",p++); printf("\n"); for(i=0;i<7;i++,p++) printf("%d",*p); return 0; } 5 8 7 6 2 3 1 4 a p p p p p p p=a; p p 指针变量可以指到数组后的内存单元
45
一级指针变量与一维数组的关系 int *p 与 int q[10] 数组名是指针(地址)常量 p=q; p+i 是q[i]的地址
数组元素的表示方法:下标法和指针法, 即若p=q, 则 p[i] q[i] *(p+i) *(q+i) 形参数组实质上是指针变量,即int q[ ] int *q 在定义指针变量(不是形参)时,不能把int *p 写成int p[]; 系统只给p分配能保存一个指针值的内存区(一般2字节);而给q分配2*10字节的内存区
46
在C语言中“指针和数组等价”到底是什么意思?
不表示它们相同, 甚至也不能互换。 可以用指针方便的访问数组或者模拟数组。 等价的基础:一旦数组出现在表达式中, 编译器会隐式地生成一个指向数组第一个成员地指针, 就像程序员写出了&a[0] 一样。 例外的情况是, 数组为sizeof 或&操作符的操作数, 或者为字符数组的字符串初始值。 这个定义的后果:编译器不严格区分数组下标操作符和指针。 a[i], 根据上边的规则, 数组蜕化为指针然后按照指针变量的方式如p[i] 那样寻址, 尽管最终的内存访问并不一样。如果你把数组地址赋给指针:p = a; 那么p[3] 和a[3] 将会访问同样的成员。
47
指针与字符串 字符串 #include <stdio.h> int main( )
字符数组 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 #include <stdio.h> int main( ) { char string[]=“I love China!”; printf(“%s\n”,string); printf(“%s\n”,string+7); return 0; }
48
用字符指针实现 string *string!=0 string 字符指针初始化:把字符串首地址赋给string I
l o v e C h i string n ! a \0 字符指针初始化:把字符串首地址赋给string char *string; string=“I love China!”; #include <stdio.h> int main( ) { char *string=“I love China!”; printf(“%s\n”,string); string+=7; while(*string) { putchar(string[0]); string++; } return 0; string *string!=0
49
字符指针变量与字符数组的区别 char *cp; 与 char str[20]; 改为: char *cp,str[10];
str由若干元素组成,每个元素放一个字符;而cp中存放字符串首地址 char str[20]; str=“I love China!”; () char *cp; cp=“I love China!”; () str是地址常量;cp是地址变量 cp接受键入字符串时,必须先开辟存储空间 改为: char *cp,str[10]; cp=str; scanf(“%s”,cp); () 例 char str[10]; scanf(“%s”,str); () 而 char *cp; scanf(“%s”, cp); () 或改为: char *cp; cp=malloc(20) ; scanf(“%s”,cp); ()
50
字符指针变量与字符数组的区别 pstr++ 使pstr指向下一个字符 下列表达式是错误的: str++ 数组名是常量,不能参与自增或自减运算
如果字符指针在定义是初始化,如: char *s=”I am a teacher”; 以后使用字符指针s时,字符串的长度不能超过”I am a teacher”的长度,否则数组溢出,而编译程序不进行这种错误的检查,将可能造成“死机”或者程序结果错误。 字符数组名str是非左值表达式,而字符指针pstr是左值表达式。因而以下表达式是正确的: char str[20],*pstr=str; pstr 使pstr指向下一个字符 下列表达式是错误的: str 数组名是常量,不能参与自增或自减运算 str=”I am a teacher” 不能向数组名赋值
51
例7.9 输入两个字符串,变成比较两个字符串的大小。
例7.9 输入两个字符串,变成比较两个字符串的大小。 分析: 程序中定义两个字符数组s和t存放输入的两个字符串 定义两个字符指针ps和pt,它们的初值为分别指向s和t两个字符串首字符。 逐个判断s和t的每一对字符是否相等,从左到右直到找到第一对不相等的字符或者所有字符判断完成为止。
52
#include <stdio.h>
#define SIZE 20 int main(void) { char s[SIZE],t[SIZE],*ps,*pt; int n; ps=s; pt=t; gets(ps); gets(pt); while((*ps==*pt)&&(*ps!='\0')) { ps++;pt++; } n=*ps-*pt; if (n>0) printf("%s>%s\n",s,t); else if(n<0) printf("%s<%s\n",s,t); printf("%s = %s\n",s,t); return 0; }
53
例 7.10 编程从键盘输入一个字符串,然后按照字符顺序从小到大进行排列,并删除重复的字符。
分析:定义字符数组str表示输入的字符串,程序中采用冒泡排序法进行排序。
54
#include <stdio.h>
#include <string.h> int main(void) { char str[100],*p,*q,*r,c; printf(“输入字符串:”); gets(str); /*排序*/ for(p=str;*p;p++) { for(q=r=p;*q;q++) if(*r>*q) r=q; if(r!=p) { c=*r;*r=*p;*p=c; } } /*删除重复的字符*/ { for(q=p;*p==*q;q++); if(p!=q) strcpy(p+1,q); printf(“结果字符串是:%s\n”,str); return 0;
55
多维数组的指针表示 二维数组的地址 int array[10]; array 对于一维数组:
(3)array+i是元素array[i]的地址 (4)array[i] *(array+i)
56
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个 元素 基类型
57
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列元素地址,指向列
58
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)
59
*(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
60
二维数组的指针变量 p 例7.11 指向二维数组元素的指针变量 #include <stdio.h> int main()
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] p 例7.11 指向二维数组元素的指针变量 #include <stdio.h> int 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); } return 0; p=*a; p=&a[0][0]; p=(int *)a; p=a;
61
指向一维数组的指针变量 a a+1 a+2 p p+1 p+2 可让p指向二维数组某一行 如 int a[3][4], (*p)[4]=a;
定义形式: 数据类型 (*指针名)[一维数组维数]; 例 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 可让p指向二维数组某一行 如 int a[3][4], (*p)[4]=a; p[0]+1或 *p+1 p[1]+2或 *(p+1)+2 *(*p+1)或 (*p)[1] *(*(p+1)+2) p的值是一维数组的 首地址,p是行指针 ( )不能少 int (*p)[4]与int *p[4]不同 一维数组指针变量维数和 二维数组列数必须相同
62
例 一维数组指针变量举例 p p p #include <stdio.h> int main()
例 一维数组指针变量举例 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] p #include <stdio.h> int main() { static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int i,j,(*p)[4]; for(p=a,i=0;i<3;i++,p++) for(j=0;j<4;j++) printf("%d ",*(*p+j)); printf("\n"); return 0; } p p[0][j] p p=a[0]; p=*a; p=&a[0][0]; p=&a[0]; p=a[0]; p=*a; p=&a[0][0]; p=&a[0];
63
字符串与数组关系 字符串用一维字符数组存放; 字符数组具有一维数组的所有特点: 数组名是指向数组首地址的地址常量;
数组元素的引用方法可用指针法和下标法; 区别 存储格式:字符串结束标志; 赋值方式与初始化; 输入输出方式:%s %c scanf(“%s”,str); printf(“%s”,str); gets(str); puts(str); 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}; () char str[10],*cp; int a[10],*p; str=“Hello”; () cp=“Hello!”; () a={1,2,3,4,5}; () p={1,2,3,4,5}; ()
64
本讲重点 指针定义和引用 地址运算 数组与指针关系、字符指针 多级指针 指针做函数参数、命令行参数
65
例 二维数组与指针运算 #include <stdio.h> int main()
{ int a[3][4]={{1,2,3,4},{3,4,5,6},{5,6,7,8}}; int i; int (*p)[4]=a,*q=a[0]; for(i=0;i<3;i++) { if(i==0) (*p)[i+i/2]=*q+1; else p++,++q; } printf("%d,",a[i][i]); printf("%d,%d\n",*((int *)p),*q); return 0; p q p q p q 1 2 3 4 5 6 7 8 2 2,4,7,5,3
66
若int a[3][4]; int (*p1)[4]=a; int *p2=a[0];
二维数组的指针作函数参数 用指向变量的指针变量; 用指向一维数组的指针变量; 用二维数组名。 实参 形参 数组名int x[][4] 指针变量int (*q)[4] 指针变量int (*q)[4] 数组名a 指针变量p1 若int a[3][4]; int (*p1)[4]=a; int *p2=a[0]; 指针变量p2 指针变量int *q
67
二维数组与一维数组指针变量的关系 int a[5][10] 与 int (*p)[10]
二维数组名是一个指向有10个元素的一维数组的指针常量; p=a+i 使 p指向二维数组的第i行; *(*(p+i)+j) a[i][j] 二维数组形参实际上是一维数组指针变量, 即 int x[ ][10] int (*x)[10] 变量定义(不是形参)时两者不等价; 系统只给p分配能保存一个指针值的内存区(一般2字节);而给a分配2*5*10字节的内存区;
68
指针数组 指针数组:用于处理二维数组或多个字符串 定义:数组中的元素为指针变量 定义形式:[存储类型] 数据类型 *数组名[数组长度说明];
例 int *p[4]; 指针数组赋值与初始化 指针本身的存储类型 指针所指向变量的数据类型 赋值: main() { int b[2][3],*pb[2]; pb[0]=b[0]; pb[1]=b[1]; …….. } int *pb[2] pb[0] pb[1] int b[2][3] 1 2 3 4 6 区分int *p[4]与int (*p)[4] 初始化: int 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
69
指针数组赋值与初始化 赋值: 或: int main() { char a[]="Fortran"; { char *p[4];
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; 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]
70
指针数组赋值与初始化 初始化: int 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]
71
二维数组与指针数组区别 指针数组元素的作用相当于二维数组的行名 二维数组存储空间固定 但指针数组中元素是指针变量
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 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 name[0] name[1] name[2] name[3] name[4] b y e \0 char *name[5]={“gain”,“much”,“stronger”, “point”,“bye”}; 二维数组存储空间固定 字符指针数组相当于可变列长的二维数组 分配内存单元=数组维数*2+各字符串长度 指针数组元素的作用相当于二维数组的行名 但指针数组中元素是指针变量 二维数组的行名是地址常量
72
例 用指针数组处理二维数组 #include <stdio.h> int main()
例 用指针数组处理二维数组 #include <stdio.h> int 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]); return 0; } 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
73
/*例7.12 输入一个表示月份的整数,输出该月份的英文名称以及各个月份英文名称的首字母。 */
#include <stdio.h> int main(void) { char *month[]= {“Illegal month”, ”January”, ”February”, ”March”, ”April”,“May”,”June”,”July”,”Augest”,”September”,”October”, ”November”,”December”}; int i; /*输入月份*/ printf(“输入月份数字:”); scanf(“%d”,&i); /*输出月份名称*/ if (i>=1&&i<=12) printf(“%d月名称%s\n”,i,month[i]); else printf(“%d是%s\n”,month[0]); /*输出每个月份名称的首字母*/ for(i=1;i<=12’i++) printf(‘%c\t”,*month[i]); printf(“\n”); return 0 ; }
74
例7.13 输入若干字符串,按照从小到大的顺序排序并输出排序的结果。
#include <stdio.h> #include <string.h> #include <stdlib.h> #define NUMBER 6 #define LENGTH 30 int main(void) { char *string[NUMBER]; int i,j,k; char *temp; for(i=0;i<NUMBER;i++) { string[i]=(char *)malloc(LENGTH); gets(string[i]); }
75
for(i=0;i<NUMBER-1;i++)
{ k=i; for(j=i+1;j<NUMBER;j++) if(strcmp(string[k],string[j])>0) k=j; if(k!=i) { temp=string[i]; string[i]=string[k]; string[k]=temp; } for(i=0;i<NUMBER;i++) printf("%s\n",string[i]); return 0;
76
多级指针:指向指针的指针 定义: 指向指针的指针 一级指针:指针变量中存放目标变量的地址 例 int *p; int i=3; p=&i;
单级间接寻址 二级指针:指针变量中存放一级指针变量的地址 例 int **p1; int *p2; int i=3; p2=&i; p1=&p2; **p1=5; p1 &p2 &i 3 P2(指针变量) i(整型变量) 二级指针 一级指针 目标变量 二级间接寻址
77
定义形式:[存储类型] 数据类型 **指针名;
如 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;
78
/*L7-14.C*/ #include <stdio.h> int main(void) { float f,*p,**newbalance; f=100.1; p=&f; newbalance=&p; printf(“%f\n”,**newbalance); return 0; }
79
/*L7-15.C*/ #include <stdio.h> int main(void) { int i; char **p,*name[2]={“ ”,”abcdefgh”}; for(p=name;p<name+2;p++) printf(“%s\n”,*p); for(i=0,p=name;p<name+2;p++,i++) printf(“%c\n”,*(*p+i)); return 0; }
80
· · · /*L7-16.C: 多级指针的应用示例*/ #include <stdio.h> int main() {
char *c[]={“ENTER”,”NEW”,”POINT”,”FIRST”}; char **cp[4];char ***cpp; cp[3]=c;cp[2]=c+1;cp[1]=c+2;cp[0]=c+3; cpp=cp; printf(“%s”,**++cpp); /*1*/ printf(“%s”,*--*++cpp+3); /*2*/ printf(“%s”,*cpp[-2]+3); /*3*/ printf(“%s\n”,cpp[-1][-1]+1); /*4*/ return 0; } 程序运行结果: POINTERSTEW cp c cpp F I R S T \0 P O I N T \0 N E W \0 E N T E R \0 cpp cpp
81
例 用二级指针处理字符串 /*L7-17.C*/ #include <stdio.h> #define NULL 0
例 用二级指针处理字符串 /*L7-17.C*/ #include <stdio.h> #define NULL 0 int main() { char **p; char *name[]={"hello","good","world","bye",""}; p=name+1; printf("%o : %s ", *p,*p); p+=2; while(**p!=NULL) printf("%s\n",*p++); return 0; } 用*p可输出地址(%o或%x), 也可用它输出字符串(%s) name[0] name[1] name[2] name[3] name[4] char *name[5] world bye \0 hello good name p *(p++) p 644 : good bye
82
二级指针与指针数组的关系 int **p 与 int *q[10] 指针数组名是二级指针常量; p=q; p+i 是q[i]的地址;
指针数组作形参,int *q[ ]与int **q完全等价;但作为变量定义两者不同; 系统只给p分配能保存一个指针值的内存区;而给q分配10块内存区,每块可保存一个指针值.
83
指针的数据类型 定义 含义 int i; 定义整型变量i p为指向整型数据的指针变量 int *p; int a[n];
int *p[n]; int (*p)[n]; int f(); int *p(); int (*p)(); int **p; 定义整型变量i p为指向整型数据的指针变量 定义含n个元素的整型数组a n个指向整型数据的指针变量组成的指针数组p p为指向含n个元素的一维整型数组的指针变量 f为返回整型数的函数 p为返回指针的函数,该指针指向一个整型数据 p为指向函数的指针变量,该函数返回整型数 p为指针变量,它指向一个指向整型数据的指针变量 指针的数据类型
84
例 下列定义的含义 (1)int *p[3]; (2)int (*p)[3]; (3)int *p(int); (4)int (*p)(int); (5)int *(*p)(int); (6)int (*p[3])(int); (7)int *(*p[3])(int); 指针数组 指向一维数组的指针 返回指针的函数 指向函数的指针,函数返回int型变量 指向函数的指针,函数返回int 型指针 函数指针数组,函数返回int型变量 函数指针数组,函数返回int型指针
85
本讲重点 指针定义和引用 地址运算 数组与指针关系、字符指针 多级指针 指针做函数参数、命令行参数
86
指针变量作为函数参数——指针传递参数地址
特点:共享内存,“双向”传递 …... 2000 2008 200A 2002 2004 2006 例7.18 将数从大到小输出 变量a 变量b (main) void swap(int x,int y) { int temp; temp=x; x=y; y=temp; } int main() { int a,b; scanf("%d,%d",&a,&b); if(a<b) swap(a,b); printf("\n%d,%d\n",a,b); return 0; 5 9 值传递 5, 9 5,9
87
例 将数从大到小输出 使用指针 传递参数地址 void swap(int *p1, int *p2) { int p; p=*p1;
} int 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); return 0; 使用指针 传递参数地址 …... 2000 2008 200A 2002 2004 2006 200C 200E 2010 ... 整型变量a 整型变量b (main) 指针pointer_1 指针pointer_2 9 5 5 9 2000 2002 5,9 9,5
88
C有“按引用传递” 吗? 没有 另一方面, 类似函数的预处理宏可以提供一种“按名称传递”的形式。
C 总是按值传递。你可以自己模拟按引用传递, 定义接受指针的函数, 然后在调用时使用& 操作符。事实上, 当你向函数传入数组(传入指针的情况参见问题6.4 及其它) 时, 编译器本质上就是在模拟按引用传递。但是C 没有任何真正等同于正式的按引用传递或C++ 的引用参数的东西。 另一方面, 类似函数的预处理宏可以提供一种“按名称传递”的形式。
89
例 将数从大到小输出 swap(int *p1, int *p2) { int *p; *p=*p1; int x; *p1=*p2;
} int 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); return 0; int x; int *p=&x; 指针变量在使用前 必须赋值! 编译警告! 结果不对!
90
例7.19 交换两个数---指针作函数的参数 swap(int *p1,int *p2) { int p; p=*p1; *p1=*p2;
例7.19 交换两个数---指针作函数的参数 swap(int *p1,int *p2) { int p; p=*p1; *p1=*p2; *p2=p; } main() { int a,b; scanf("%d,%d",&a,&b); printf(“a=%d,b=%d\n”,a,b); printf(“swapped:\n”); swap(&a,&b); printf(”a=%d,b=%d\n",a,b); a 5 9 b 调前: a 5 9 b 调swap: p1 &a &b p2 a 9 5 b 交换: p1 &a &b p2 a 9 5 b 返回:
91
数组作为函数参数 数组元素作函数实参——值传递 #include <stdio.h> main()
{ int a[10],b[10],i,n=0,m=0,k=0; printf("Enter array a:\n"); for(i=0;i<10;i++) scanf("%d",&a[i]); printf("Enter array b:\n"); scanf("%d",&b[i]); { if(large(a[i],b[i])==1) n=n+1; else if(large(a[i],b[i])==0) m=m+1; else k=k+1; } /* Output */ int large(int x,int y) { int flag; if(x>y) flag=1; else if(x<y) flag=-1; else flag=0; return(flag); 数组作为函数参数 数组元素作函数实参——值传递 例 两个数组大小比较 4 3 2 1 5 a 56 23 12 10 76 88 b 21 43 98 66 54 a和b为有10个元素的整型数组 比较两数组对应元素 变量n,m,k记录a[i]>b[i], a[i]==b[i], a[i]<b[i]的个数 最后 若n>k,认为数组a>b 若n<k,认为数组a<b 若n==k,认为数组a==b i n=0 m=0 k=1 i n=0 m=1 k=1 i n=1 m=1 k=1 i n=1 m=1 k=2 i n=2 m=1 k=2 i n=3 m=1 k=2 n=0 m=0 k=0
92
数组名作函数参数 “地址传递”:值传递的特例,形参是指针类型 在主调函数与被调函数分别定义数组,且类型应一致
形参数组大小(多维数组第一维)可不指定 形参数组名是地址变量 实参 形参 数组名 指针变量
93
一维数组名作函数参数 例 求学生的平均成绩 float average(int stu[10], int n) { int i;
例 求学生的平均成绩 形参用数组定义, int stu[ ] float average(int stu[10], int n) { int i; float av,total=0; for( i=0; i<n; i++ ) total += stu[i]; av = total/n; return av; } /*L7-20.C*/ #include <stdio.h> float average(int stu[10], int n); int main() { int score[10], i; float av; printf("Input 10 scores:\n"); for( i=0; i<10; i++ ) scanf("%d", &score[i]); av=average(score,10); printf("Average is:%.2f", av); return 0; } . 2 1 9 score 56 23 12 …. 88 stu 实参用数组名
94
#include <stdio.h> void swap2(int x,int y) { int z;
例 数组元素与数组名作函数参数比较 #include <stdio.h> void swap2(int x,int y) { int z; z=x; x=y; y=z; } int main() { int a[2]={1,2}; swap2(a[0],a[1]); printf("a[0]=%d\na[1]=%d\n",a[0],a[1]); return 0; 值传递 1 2 a 调用 a[0] a[1] x y 2 1 x y 交换 1 2 a 调用前 a[0] a[1] 1 2 a 返回
95
#include <stdio.h> void swap2(int x[]) { int z;
例 数组元素与数组名作函数参数比较 #include <stdio.h> void swap2(int x[]) { int z; z=x[0]; x[0]=x[1]; x[1]=z; } int main() { int a[2]={1,2}; swap2(a); printf("a[0]=%d\na[1]=%d\n",a[0],a[1]); return 0; 地址传递 1 2 a 调用前 1 2 a x 调用 2 1 a x 交换 2 1 a 返回
96
例7.22 数组排序----简单选择排序 a k array j j i=0 j k j k j j j j j int main()
void sort(int array[],int n) { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(array[j]<array[k]) k=j; if(k!=i) { t=array[i]; array[i]=array[k]; array[k]=t; } int main() { int a[10],i; for(i=0;i<10;i++) scanf("%d",&a[i]); sort(a,10); printf("%d ",a[i]); printf("\n"); return 0; 例7.22 数组排序----简单选择排序 1 2 3 4 5 6 7 8 9 a 9 k array 49 68 57 32 9 99 27 13 76 88 j j i=0 j k 49 j k j j j j j
97
例7.23 数组排序----简单选择排序 int main() { int a[10],i; for(i=0;i<10;i++)
例7.23 数组排序----简单选择排序 void sort(int array[],int n) { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(array[j]<array[k]) k=j; if(k!=i) { t=array[i]; array[i]=array[k]; array[k]=t; } int main() { int a[10],i; for(i=0;i<10;i++) scanf("%d",&a[i]); sort(a,10); printf("%d ",a[i]); printf("\n"); return 0; }
98
例7.23 数组排序----简单选择排序 a array k j k i=1 j k j j j k j k j j 13 68 49 68
例7.23 数组排序----简单选择排序 1 2 3 4 5 6 7 8 9 a 49 68 57 32 99 27 13 76 88 array 13 k j k i=1 j k j j j k 68 j k j j
99
例7.24 数组排序----简单选择排序 void sort(int array[],int n) int main()
例7.24 数组排序----简单选择排序 void sort(int array[],int n) { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(array[j]<array[k]) k=j; if(k!=i) { t=array[i]; array[i]=array[k]; array[k]=t; } int main() { int a[10],i; for(i=0;i<10;i++) scanf("%d",&a[i]); sort(a,10); printf("%d ",a[i]); printf("\n"); return 0; }
100
例7.24 数组排序----简单选择排序 a array i=8 13 1 27 2 32 3 49 4 57 5 68 6 76 7 88
例7.24 数组排序----简单选择排序 1 2 3 4 5 6 7 8 9 a 13 27 32 49 57 68 76 88 99 array i=8
101
字符串指针作函数参数 void copy_string(char from[],char to[]) { int i=0;
b y u a r s t n d e to o . \0 I a e c h \0 r . t m 字符串指针作函数参数 a I m t e c h \0 r . from void copy_string(char from[],char to[]) { int i=0; while(from[i]!='\0') { to[i]=from[i]; i++; } to[i]='\0'; main() { char a[]="I am a teacher."; char b[]="You are a student."; printf("string_a=%s\n string_b=%s\n",a,b); 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'; } int main() { char *a="I am a teacher."; char *b="You are a student."; printf("string_a=%s\nstring_b=%s\n",a,b); copy_string(a,b); printf("\nstring_a=%s\nstring_b=%s\n",a,b); return 0; (1)用字符数组作参数 (2)用字符指针变量作参数
102
编写一个函数求字符串的长度 /*版本1 */ int strlen(char *s) { int n=0; while(*s!=‘\0’)
/*版本1 */ int strlen(char *s) { int n=0; while(*s!=‘\0’) { n++; s++; } return n; /*版本3*/ int strlen(char *s) { int n=0; while(*s++) n++; return n; } /*版本2*/ int strlen(char *s) { int n=0; while(*s++!=‘\0’) n++; return n; } /*版本4*/ int strlen(char *s) { char *p=s; while(*p++); return p-s; }
103
/*L7-25.C: 编写一个函数实现任意两个字符串比较大小*/ int strcmp(char *sa,char *sb) {
for(;*sa==*sb;sa++,sb++) if(*sa==‘\0’) return 0; return (*sa-*sb); } 如何调用上面定义的函数? char s1[20],s2[20]; gets(s1); gets(s2); if(strcmp(s1,s2)>0) printf(“s1>s2\n”); else if(strcmp(s1,s2)==0) printf(s1==s2\n”); else printf(s1<s2\n”); char *s1,*s2; gets(s1); gets(s2); if(strcmp(s1,s2)>0) printf(“s1>s2\n”); else if(strcmp(s1,s2)==0) printf(s1==s2\n”); else printf(s1<s2\n”);
104
若int a[3][4]; int (*p1)[4]=a; int *p2=a[0];
二维数组的指针作函数参数 用指向变量的指针变量 用指向一维数组的指针变量 用二维数组名 二维数组形参实际上是一维数组指针变量, 即 int x[ ][10] int (*x)[10] 实参 形参 数组名int x[][4] 指针变量int (*q)[4] 指针变量int (*q)[4] 数组名a 指针变量p1 若int a[3][4]; int (*p1)[4]=a; int *p2=a[0]; 指针变量p2 指针变量int *q
105
二维数组名作函数参数 例7.26 求二维数组中最大元素值 j j j i 1 3 5 7 i 1 3 5 7 i 1 3 5 7
例7.26 求二维数组中最大元素值 i j max=1 i j max=3 i j max=5 j i max=7 j i max=7 j i max=34
106
#include <stdio.h> int max_value(int array[3][4])
{ int i,j,k,max; max=array[0][0]; for(i=0;i<3;i++) for(j=0;j<4;j++) if(array[i][j]>max) max=array[i][j]; return(max); } int main() { int a[3][4]={{1,3,5,7}, {2,4,6,8},{15,17,34,12}}; printf("max value is %d\n",max_value(a)); return 0; 多维形参数组第一维维数 可省略,第二维必须相同 int array[][4]
107
例 求二维数组中各行元素之和 get_sum_row(int x[][3], int result[] ,int row, int col)
例 求二维数组中各行元素之和 get_sum_row(int x[][3], int result[] ,int row, int col) { int i,j; for(i=0;i<row;i++) { result[i]=0; for(j=0;j<col;j++) result[i]+=x[i][j]; } int main() { int a[2][3]={3,6,9,1,4,7}; int sum_row[2],row=2,col=3,i; get_sum_row(a,sum_row,row,col); printf("The sum of row[%d]=%d\n",i+1,sum_row[i]); return 0; x result 3 1 4 6 7 9 a sum_row 18 12
108
void average(float *p,int n) { float *p_end, sum=0,aver; p_end=p+n-1;
函数说明 void average(float *p,int n) { float *p_end, sum=0,aver; p_end=p+n-1; for(;p<=p_end;p++) sum=sum+(*p); aver=sum/n; printf("average=%5.2f\n",aver); } void search(float (*p)[4], int n) { int i; printf(" No.%d :\n",n); for(i=0;i<4;i++) printf("%5.2f ",*(*(p+n)+i)); int main() { void average(float *p,int n); void search(float (*p)[4],int n); float score[3][4]= {{65,67,79,60},{80,87,90,81}, {90,99,100,98}}; average(*score,12); search(score,2); return 0; } 列指针 p 行指针 float p[][4] p 65 52 79 60 80 87 90 81 99 100 98 p[n][i]
109
例7.28 3个学生各学4门课,计算总平均分,并查找一门以上课 不及格学生, 输出其各门课成绩
例 个学生各学4门课,计算总平均分,并查找一门以上课 不及格学生, 输出其各门课成绩 void search(float (*p)[4], int n) { int i,j,flag; for(j=0;j<n;j++) { flag=0; for(i=0;i<4;i++) if(*(*(p+j)+i)<60) flag=1; if(flag==1) { printf("No.%d is fail,his scores are:\n",j+1); printf("%5.1f ",*(*(p+j)+i)); printf("\n"); } int main() { void search(float (*p)[4], int n); float score[3][4]={{...},{...},{...}}; search(score,3);return 0; 65 52 79 60 80 87 90 81 99 100 98 p p[j][i]
110
例 二级指针做函数参数 1 2 2002 2000 COPY 2004 2006 2000 #include <stdio.h>
例 二级指针做函数参数 #include <stdio.h> void swap(int **r,int **s) { int *t; t=*r; *r=*s; *s=t; } int main() { int a=1,b=2,*p,*q; p=&a; q=&b; swap(&p,&q); printf("%d,%d\n",*p,*q); return 0; 2000 2008 200A 2002 2004 2006 1 2 变量a 变量b (main) 指针变量p 指针变量q 2002 COPY 2000 二级指针s 二级指针r (swap) 指针变量t 2006 2004 2000
111
例二级指针做函数参数 1 2 2002 2000 2,1 #include <stdio.h>
2008 200A 2002 2004 2006 1 2 变量a 变量b (main) 指针变量p 指针变量q #include <stdio.h> void swap(int **r,int **s) { int *t; t=*r; *r=*s; *s=t; } int main() { int a=1,b=2,*p,*q; p=&a; q=&b; swap(&p,&q); printf("%d,%d\n",*p,*q); return 0; 2002 2000 2,1
112
例二级指针做函数参数 2,1 a b p q #include <stdio.h>
void swap(int **r,int **s) { int *t; t=*r; *r=*s; *s=t; } int main() { int a=1,b=2,*p,*q; p=&a; q=&b; swap(&p,&q); printf("%d,%d\n",*p,*q); return 0; a b r s p q a b r s p q b a p q 2,1
113
#include <stdio.h>
#include <stdlib.h> struct node{ int data; struct node *next; }; int delete(struct node **phead,int n) { struct node *p,*q; p=*phead; while(p->data!=n&&p->next!=NULL) { q=p; p=p->next; } if(p->data==n) /*找到*/ { if(p==*phead) /*被删除的结点是链头*/ *phead=p->next; else /*被删除的结点不是链头*/ q->next=p->next; free(p); return (1); } else return (0); } 例7.29 编写函数,从一个已经创建好链表中删除指定结点,该链表由整数构成
114
long factorial(int n);
#include <stdio.h> long sum(int a, int b); long factorial(int n); int main() { int n1,n2; long a; scanf("%d,%d",&n1,&n2); a=sum(n1,n2); printf("a=%ld",a); } long sum(int a,int b) { long c1,c2; c1=factorial(a); c2=factorial(b); return(c1+c2); long factorial(int n) { long rtn=1; int i; for(i=1;i<=n;i++) rtn*=i; return(rtn); 文件包含编译预处理命令 函数类型说明 long sum(int a, int b); long factorial(int n); 函数调用 实参 函数定义 形参 函数返回值
115
指向函数的指针变量 函数指针:函数在编译时被分配的入口地址,用函数名表示 指向函数的指针变量 定义形式: 数据类型 (*指针变量名)();
max …... 指令1 指令2 指向函数的指针变量 定义形式: 数据类型 (*指针变量名)(); 如 int (*p)(); 函数指针变量赋值:如p=max; ( )不能省 int (*p)() 与 int *p()不同 专门存放函数入口地址 可指向返回值类型相同的不同函数 函数返回值的数据类型 函数调用形式: c=max(a,b); c=(*p)(a,b); c=p (a,b); 对函数指针变量pn, p++, p--无意义 函数指针变量指向的函数必须有函数说明
116
例7.30 用函数指针变量调用函数,比较两个数大小
例7.30 用函数指针变量调用函数,比较两个数大小 int main() { int max(int ,int), (*p)(); 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); return 0; } int max(int x,int y) { int z; if(x>y) z=x; else z=y; return(z); 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);
117
用函数指针变量作函数参数 例7.31 用函数指针变量作参数,求最大值、最小值和两数之和
118
例7.31 用函数指针变量作参数,求最大值、最小值和两数之和
int main() { int a,b,max(int,int), min(int,int),add(int,int); void process(int,int,int (*fun)()); scanf("%d,%d",&a,&b); process(a,b,max); process(a,b,min); process(a,b,add); return 0; } void process(int x,int y,int (*fun)()) { int result; result=(*fun)(x,y); printf("%d\n",result); int max(int x,int y) { printf(“max=”); return(x>y?x:y); int min(int x,int y) { printf(“min=”); return(x<y?x:y); int add(int x,int y) { printf(“sum=”); return(x+y);
119
C:\TC> copy[.exe] source.c temp.c
命令行参数 命令行:在操作系统状态下,为执行某个程序而键入的一行字符 命令行一般形式:命令名 参数1 参数2………参数n C:\TC> copy[.exe] source.c temp.c 带参数的main函数形式: 有3个字符串参数的命令行 main(int argc, char *argv[]) { ……… } 命令行参数传递 命令行实参 main(形参) 系统自动调用 main函数时传递 命令行中参数个数 形参名任意 元素指向命令行参数 中各字符串首地址 第一个参数: main所在的可执行文件名
120
例7.32 输出命令行参数 argc=3 1. 编译、链接test.c,生成可执行文件test.exe
例7.32 输出命令行参数 /*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!
121
例7.32 输出命令行参数 int main(int argc, char *argv[]) { while(argc-->0)
例7.32 输出命令行参数 int 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 test hello world!
122
例7.33,编程模拟实现UNIX中的grep命令的基本功能
grep -- print lines matching a pattern (将符合样式的该行列出) 语法: grep PATTERN 自定义函数 int getline(char *s,int max); /*read a line into s, return length*/ /*max--- maxium input line size*/
123
#include <stdio.h>
#include <string.h> #define MAXLINE 1000 int getline(char *s,int max); /*print lines that match pattern from 1st arg*/ int main(int argc,char*argv[]) { char line[MAXLINE]; int found=0; if(argc!=2) printf(“Usage:find pattern\n”); else while(getline(line,MAXLINE)>0) if(strstr(line,argv[1])!=NULL) { printf(“%s”,line); found++; } return found;
124
/*read a line into s, return length*/
int getline(char *s,int max) { int c,i; for(i=0;i<max-1&&(c=getchar())!=EOF&& c!=‘\n’;++i) s[i]=c; if(c==‘\n’) { s[i]=c ++I; } return i;
125
例7.34,改进为带参数的命令 find -x –n pattern 或者为 find –xn pattern
X: 表示形式与pattern不匹配的行 N: 要求形式行号
126
#include <stdio.h>
#include <string.h> #define MAXLINE 1000 int getline(char *s,int max); /*print lines that match pattern from 1st arg*/ int main(int argc,char*argv[]) { char line[MAXLINE]; long lineno=0;/*行号*/ int c, except=0,number=0;/*分别记录x和n的状态*/ int found=0; while(--argc>0&&(*++argv)[0]==‘-’) while(c==*++argv[0]) switch(c) { case ‘x’:except=1;break; case ‘n’:number=1;break; default: printf(“find:illegal option %c\n”,c); argc=0; found=-1; break; }
127
if(argc!=1) printf(“Usage:find –x –n pattern\n”); else while(getline(line,MAXLINE)>0) { lineno++; if((strstr(line,*argv)!=NULL)!=except) { if(number) printf(“%ld:”,lineno); printf(“%s”,line); found++; } return found;
128
THE END
Similar presentations