Upload
kaitlyn-sweemey
View
98
Download
8
Embed Size (px)
DESCRIPTION
第七章 指针. 教 材 : C 程序设计导论. 主 讲 : 谭 成 予 [email protected]. 武汉大学计算机学院. 指针定义和引用. 地址运算. 多级指针. 数组与指针关系、字符指针. 指针做函数参数、命令行参数. 本讲重点. 指针的优点. C 程序设计中使用指针可以 : 使程序 简洁 、 紧凑 、 高效 有效 地表示复杂的数据结构 动态 分配内存 得到 多于一个 的函数返回值. 内存. 0. …. 2000. 2001. 2002. 2003. 2005. - PowerPoint PPT Presentation
Citation preview
本讲重点本讲重点
地址运算
数组与指针关系、字符指针
多级指针
指针定义和引用
指针做函数参数、命令行参数
C 程序设计中使用指针可以 :– 使程序简洁、紧凑、高效– 有效地表示复杂的数据结构– 动态分配内存– 得到多于一个的函数返回值
指针的优点指针的优点
指针与地址变量与地址
程序中 : int i;
float k;
内存中每个字节有一个编号 ----- 地址
….
..…
...
2000
2001
2002
2005
内存0
2003
i
k
编译或函数调用时为其分配内存单元
变量是对程序中数据存储空间的抽象
指针与指针变量
•指针:一个变量的地址;•指针变量:专门存放变量地址的变量。
….
..…
...
2000
2004
2006
2005
整型变量 i10
变量 i_pointer
2001
2002
2003
2000
指针
指针变量
变量的内容 变量的地址
指针与指针变量
指针变量
变量
变量地址 ( 指针 )
变量值指向 地址存入
指针变量
指针与指针变量
例 i=3; ----- 直接访问
指针变量
….
..…
...
2000
2004
2006
2005
整型变量 i10
变量 i_pointer
2001
2002
2003
2000
3
例 *i_pointer=20; ----- 间接访问
20
直接访问与间接访问•直接访问:按变量地址存取变量值•间接访问:通过存放变量地址的变量去访问变量
指针变量的定义和引用
指针变量的定义• 一般形式: [ 存储类型 ] 数据类型 * 指针名;
3
变量 i
2000
i_pointer
*i_pointer
i *i_pointer&i i_pointer
i=3; *i_pointer=3
合法标识符指针变量本身的存储类型指针的目标变量的数据类型表示定义指针变量不是‘ *’ 运算符
指针变量的定义和引用
例 int *p1,*p2; float *q ; static char *name;
注意:1 、 int *p1, *p2; 与 int *p1, p2;2 、指针变量名是 p1,p2 , 不是 *p1,*p23 、指针变量只能指向定义时所规定类型的变量4 、指针变量定义后,变量值不确定,应用前必须先赋值
指针变量的初始化一般形式: [ 存储类型 ] 数据类型 * 指针名 = 初始地址值;
赋给指针变量,不是赋给目标变量
例 int i; int *p=&i; 变量必须已说明过
类型应一致
例 int *p=&i; int i;
例 int i; int *p=&i; int *q=p;
例 int main( ) { int i; static int *p=&i; ..............
} ()不能用 auto变量的地址去初始化 static型指针
用已初始化指针变量作初值
#include<stdio.h> int main(){ int i=10; int *p; *p=i; printf("%2d",*p); return 0; }
危险!
#include<stdio.h> int main( ){ int i=10,k; int *p; p=&k; *p=i; printf("%2d",*p); return 0;}
指针变量必须先赋值 ,再使用
….
..…
...
2000
2004
2006
2005
整型变量 i10
指针变量 p
2001
2002
2003
随机
• 零指针: ( 空指针 )– 定义 : 指针变量值为零– 表示: int * p=0;
p 指向地址为 0的单元,系统保证该单元不作它用表示指针变量值没有意义
#define NULL 0int *p=NULL:
零指针与空类型指针
– p=NULL 与未对 p 赋值不同– 用途 :
避免指针变量的非法引用在程序中常作为状态比较
例 int *p; ...... while(p!=NULL) { ...… }
零指针与空类型指针
• void * 类型指针– 表示 : void *p; – 使用时要进行强制类型转换
例 char *p1; void *p2; p1=(char *)p2; p2=(void *)p1;
表示不指定 p是指向哪一种类型数据的指针变量
零指针与空类型指针
空指针到底是什么?
空指针不同于未初始化的指针。空指针可以确保不指向任何对象或函数 ; 未初始化指针则可能指向任何地方。每种指针类型都有一个空指针 , 而不同类型的空指针的内部表示可能不尽相同。尽管程序员不必知道内部值 , 但编译器必须时刻明确需要那种空指针 , 以便在需要的时候加以区分。
每一种指针类型都有一个特殊值—— “空指针” :它与同类型的其它所有指针值都不相同 , 它“与任何对象或函数的指针值都不相等”。对 malloc() 的成功调用也不会返回空指针 , 如果失败 , malloc() 的确返回空指针 , 这是空指针的典型用法 : 表示“未分配” 或者“尚未指向任何地方” 的指针。
指针变量的引用: & 与 * 运算符• 含义
含义 : 取变量的地址单目运算符优先级 : 2结合性 : 自右向左
含义 : 取指针所指向变量的内容单目运算符优先级 : 2结合性 : 自右向左
• 两者关系:互为逆运算• 理解
….
..…
...
2000
2004
2006
2005
整型变量 i10
变量 i_pointer
2001
2002
2003
2000 指针变量
i_pointer----- 指针变量,它的内容是地址量*i_pointer---- 指针的目标变量,它的内容是数据&i_pointer--- 指针变量占用内存的地址
2000 10
i_pointer *i_pointer
&i_pointer
i
i_pointer &i &(*i_pointer)i *i_pointer *(&i)i_pointer = &i = &(*i_pointer)i = *i_pointer = *(&i)
指针变量…
...
….
..
2000
2004
2006
2005
整型变量 i10
变量 i_pointer
2001
2002
2003
2000
整型变量 k
例 k=i; -- 直接访问 k=*i_pointer; -- 间接访问
10
例 k=i; k=*i_pointer;
例 指针的概念
#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;}
a:10*pa:10&a:f86(hex)pa:f86(hex)&pa:f88(hex)
….
..…
...
f86
f8a
f8c
f8b
整型变量 a10
指针变量 paf87
f88
f89 f86
例 7.1 输入两个数,并使其从大到小输出 .
#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;}
5,9max=9,min=5
….
..…
...
指针变量 p1
指针变量 p
2000
2008
2002
2004
2006
指针变量 p2
整型变量 b
整型变量 a5
2006
9
20082006
2008
2006
malloc() 函数
void *malloc(unsigned int size) ; 功能:在动态存储区域中分配一个 size 字节的连续空间。
函数的返回值为指针,它的值是所分配的存储区域的起始地址。
如没有足够的存储空间分配,则返回 0 (记为 NULL )值。
动态存储分配 – 实际编程中可能遇到所需内存空间无法预先确定的情况,需
要根据实际的数据的多少来确定。– C 语言提供动态分配内存函数 ( stdlib.h) 。
动态存储分配 /*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
动态分配内存
calloc() 函数 calloc 函数的原型为: void *calloc(unsigned int n, unsigned int size);
函数的功能是:在动态存储区域中分配 n 个为 size 字节的连续空间,并将该存储空间自动置初值 0 。函数的返回值为指针,它的值是所分配的存储区域的起始地址。如分配不成功,则返回 0 值。
n 表示对象的个数 size 每个对象占用内存单元的字节数
#include <stdio.h>#include <stdlib.h>int main(){
unsigned num;int *ptr,I,*p;printf("enter the number of type int to allocate:"); sc
anf("%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;for(I=0;I<num;I++)printf("%d",ptr[I]);printf("\n");
}else
puts("memory allocation error.");free(ptr);return (0);
}
enter the number of type int to allocate:200 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
动态分配内存free() 函数
void free(void *ptr) ; 函数的功能是:释放由 ptr 所指向的存储空间。 ptr 的值必须是 malloc 或 calloc 函数返回的地址。此函数无返回值。
动态分配内存realloc 函数 realloc 函数的原型为: void *realloc(void *ptr,size_t size);
realloc 函数的作用是对 p 所指向的存储区进行重新分配即改变大小;
ptr: 是已经由 malloc 或 calloc 函数分配的存储区的指针 ;size: 是重新分配的存储区的大小(字节数),新存储区包含着和旧存储区相同的内容,如果新存储区较大,则新增加的部分未被初始化。此函数返回值是新存储区的首地址;如果没有足够的内存空间则返回 NULL ,此时旧存储区内容不变。
动态存储分配函数的使用举例/*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) {
printf(“Allocation error\n”);exit(1); }
strcat(p,”.”); printf(“%x,%s\n”,p,p); /* 输出字符串首地址和内容 */free(p); /* 释放存储区 */return 0;
}
动态存储分配函数的使用举例
/*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;
}
free() 怎么知道有多少字节需要释放?
malloc/free 的实现会在分配的时候记下每一块的大小 , 所以在释放的时候就不必再考虑了。
calloc() 和 malloc() 有什么区别?利用 calloc的零填充功能安全吗? free() 可以释放 calloc()分配的内存吗 , 还是需要一个 cfree()?calloc(m, n) 本质上等价于 p = malloc(m * n);memset(p, 0, m * n);填充的零是全零 , 因此不能确保生成有用的空指针值或浮点零值。free() 可以安全地用来释放 calloc() 分配的内存。
本讲重点本讲重点
地址运算
数组与指针关系、字符指针
多级指针
指针定义和引用
指针做函数参数、命令行参数
指针的运算• 指针变量的赋值运算
– p=&a; ( 将变量 a 地址 p)
– p=array; ( 将数组 array 首地址 p)
– p=&array[i]; ( 将数组元素地址 p)
– p1=p2; ( 指针变量 p2 值 p1)
– 不能把一个整数 p, 也不能把 p 的值整型变量– 所有类型的指针都可置为 NULL
如 int i, *p; p=1000; () i=p; ()
指针变量与其指向的变量具有相同数据类型
– 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 无意义
指针的算术运算
例 p 指向 float 数,则 p+1 p+1 4
例 p 指向 int 型数组,且 p=&a[0]; 则 p+1 指向 a[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;
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]
a[8]
a[9]
a 数组p
p+1,a+1
p+i,a+i
p+9,a+9
1
指针的算术运算
指针变量的关系运算
若 p1 和 p2 指向同一数组,则:p1<p2 表示 p1 指的元素在前;p1>p2 表示 p1 指的元素在后;p1==p2 表示 p1 与 p2 指向同一元素。
若 p1 与 p2 不指向同一数组,比较无意义;
p==NULL 或 p!=NULL
例 7.5 堆栈( stack )是一种先进后出( first-in last-out )的表,好比将若干个盘子堆放起来,每次放或者取一个盘子,最先堆放的盘子最后被取走,将一个数据压入堆栈称为入栈,从堆栈中取走一个数据称为出栈操作。现在编程实现该算法。
p1
……
stack[0]
stack[1]
stack[2]
10p1
3p1
push(10)
push(3)
pop()
pop()
#include <stdio.h>#include <stdlib.h>#define SIZE 50void 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;
}
/* 将入栈操作定义成用户自定义函数 */void push(int i){ p1++;
if(p1==(tos+SIZE)) /*判断堆栈是否已满 */{ printf(“ 堆栈已满 \n”);
exit(1);}*p1=i;
}/* 将出栈操作定义成自定义函数 */int pop(void){ if(p1==tos) /*判断堆栈是否空 */
{ printf(“ 堆栈空 \n”);exit(1);
}p1--;return *(p1+1);
}
本讲重点本讲重点
地址运算
数组与指针关系、字符指针
多级指针
指针定义和引用
指针做函数参数、命令行参数
指针变量与数组– 指向数组元素的指针变量
例 int array[10]; int *p; p=&array[0]; // p=array;或 int *p=&array[0];或 int *p=array;
array[0]array[1]array[2]array[3]
array[9]
...
整型指针 p &array[0]
p
数组名是表示数组首地址的地址常量 ,就是数组的指针。
数组元素表示方法
a[0]a[1]a[2]a[3]
a[9]
...
a
a+9
a+1a+2
地址 元素
下标法
a[0]a[1]a[2]
a[9]
a[0]a[1]a[2]a[3]
a[9]
...
p
p+9
p+1p+2
地址 元素
指针法
*p*(p+1)*(p+2)
*(p+9)
[] 变址运算符a[i] *(a+i)
a[i] p[i] *(p+i) *(a+i)
*a*(a+1)*(a+2)
*(a+9)
p[0]p[1]p[2]
p[9]
a[0]a[1]a[2]a[3]a[4]
例 7.6 数组元素的引用方法#include <stdio.h>int main(){ int a[5],*pa,i; for(i=0;i<5;i++)
a[i]=i+1; pa=a; for(i=0;i<5;i++)
printf("*(pa+%d):%d\n",i,*(pa+i)); for(i=0;i<5;i++)
printf("*(a+%d):%d\n",i,*(a+i)); for(i=0;i<5;i++)
printf("pa[%d]:%d\n",i,pa[i]); for(i=0;i<5;i++)
printf("a[%d]:%d\n",i,a[i]); return 0;}
12345
pa
例 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) ()
例 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; }
5 6
pp 5
8
7
6
2
7
3
0
1
2
3
4
5
6
a
例 7.7 注意指针变量的运算
6
#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;}
例 7.8 注意指针的当前值
p=a;
pp 5
8
7
6
2
7
3
0
1
2
3
4
5
6
a
p
p
p
p
p
p
指针变量可以指到数组后的内存单元
一级指针变量与一维数组的关系
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 字节的内存区
在 C语言中“指针和数组等价”到底是什么意思?
不表示它们相同 , 甚至也不能互换。可以用指针方便的访问数组或者模拟数组。
等价的基础:一旦数组出现在表达式中 , 编译器会隐式地生成一个指向数组第一个成员地指针 , 就像程序员写出了 &a[0] 一样。
例外的情况是 , 数组为 sizeof 或 &操作符的操作数 , 或者为字符数组的字符串初始值。
这个定义的后果:编译器不严格区分数组下标操作符和指针。a[i], 根据上边的规则 , 数组蜕化为指针然后按照指针变量的方式如 p[i] 那样寻址 , 尽管最终的内存访问并不一样。如果你把数组地址赋给指针 :p = a; 那么 p[3] 和 a[3] 将会访问同样的成员。
指针与字符串字符串• 字符数组
#include <stdio.h> int main( ) { char string[]=“I love China!”; printf(“%s\n”,string); printf(“%s\n”,string+7); return 0; }
I
love
Chi
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); string+=7; while(*string) { putchar(string[0]); string++; } return 0; }
I
love
Chi
string
n
!a
\0
字符指针初始化 :把字符串首地址赋给 string char *string; string=“I love China!”;
string
*string!=0
字符指针变量与字符数组的区别
char *cp; 与 char str[20];• str 由若干元素组成,每个元素放一个字符;而 cp 中存放字符串
首地址• char str[20]; str=“I love China!”; ()
char *cp; cp=“I love China!”; ()
• str 是地址常量; cp 是地址变量• cp 接受键入字符串时 , 必须先开辟存储空间
例 char str[10]; scanf(“%s”,str); ()
而 char *cp; scanf(“%s”, cp); ()
改为 : char *cp,str[10]; cp=str; scanf(“%s”,cp); ()
或改为 : char *cp; cp=malloc(20) ; scanf(“%s”,cp); ()
字符指针变量与字符数组的区别如果字符指针在定义是初始化,如: 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” 不能向数组名赋值
分析:– 程序中定义两个字符数组 s 和 t 存放输入的两个字符串– 定义两个字符指针 ps 和 pt ,它们的初值为分别指向 s 和 t 两
个字符串首字符。– 逐个判断 s 和 t 的每一对字符是否相等,从左到右直到找到第
一对不相等的字符或者所有字符判断完成为止。
例 7.9 输入两个字符串,变成比较两个字符串的大小。
#include <stdio.h>#define SIZE 20int 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);
elseprintf("%s = %s\n",s,t);
return 0;}
例 7.10 编程从键盘输入一个字符串,然后按照字符顺序从小到大进行排列,并删除重复的字符。
分析:定义字符数组 str 表示输入的字符串,程序中采用冒泡排序法进行排序。
#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(p=str;*p;p++){ for(q=p;*p==*q;q++);
if(p!=q)strcpy(p+1,q);
}printf(“ 结果字符串是: %s\n”,str);return 0;
}
多维数组的指针表示
• 二维数组的地址
对于一维数组 :( 1 )数组名 array 表示数组的首地址,即 array[0] 的地址;( 2 )数组名 array 是地址常量( 3 ) array+i 是元素 array[i] 的地址( 4 ) array[i] *(array+i)
array int array[10];
对于二维数组:( 1 ) a 是数组名, 包含三个元素 a[0],a[1],a[2]
( 2 )每个元素 a[i] 又是一个一维 数组,包含 4 个 元素
a
a+1
a+2
*(*(a+0)+1)
*(a[0]+1)int a[3][4];
a[0]
a[1]
a[2]
2000
2008
2016
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
– 对二维数组 int a[3][4], 有a-----二维数组的首地址,即第 0 行的首地址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];
a[0]
a[1]
a[2]
2000
2008
2016
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
a+1
a+2
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]
二维数组元素表示形式:( 1 ) a[1][2]( 2 ) *(a[1]+2)( 3 ) *(*(a+1)+2)( 4 ) *(&a[0][0]+1*4+2)
地址表示:(1) a+1 (2) &a[1][0](3) a[1](4) *(a+1)(5)(int *) (a+1)
行指针
列指针
地址表示:(1) &a[1][2](2) a[1]+2(3) *(a+1)+2(4)&a[0][0]+1*4+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
2000
2008
2008
2012
13
二维数组的指针变量
例 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;
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
指向一维数组的指针变量定义形式: 数据类型 (* 指针名 )[ 一维数组维数 ];
例 int (*p)[4];
( ) 不能少int (*p)[4] 与 int *p[4] 不同
p 的值是一维数组的首地址, p是行指针
可让 p 指向二维数组某一行 如 int a[3][4], (*p)[4]=a;
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[0]+1 或 *p+1
p[1]+2 或 *(p+1)+2
*(*p+1) 或 (*p)[1]
*(*(p+1)+2)
一维数组指针变量维数和二维数组列数必须相同
例 一维数组指针变量举例#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=a[0]; p=*a; p=&a[0][0]; p=&a[0];
p=a[0]; p=*a; p=&a[0][0]; p=&a[0];
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
p
p
p[0][j]
字符串与数组关系字符串用一维字符数组存放;字符数组具有一维数组的所有特点:
数组名是指向数组首地址的地址常量;数组元素的引用方法可用指针法和下标法;
区别存储格式:字符串结束标志;赋值方式与初始化;输入输出方式: %s %c
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}; ()
scanf(“%s”,str);printf(“%s”,str);gets(str);puts(str);
本讲重点本讲重点
地址运算
数组与指针关系、字符指针
多级指针
指针定义和引用
指针做函数参数、命令行参数
例 二维数组与指针运算#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;
} for(i=0;i<3;i++) printf("%d,",a[i][i]); printf("%d,%d\n",*((int *)p),*q); return 0;}
2 , 4 , 7 , 5 , 3
1 2 3 4
3 4 5 6
5 6 7 8
p
q
2p
q
p
q
二维数组的指针作函数参数用指向变量的指针变量 ;用指向一维数组的指针变量 ;用二维数组名。
实参 形参数组名 int x[][4]
指针变量 int (*q)[4]
数组名 int x[][4]
指针变量 int (*q)[4]
数组名 a
数组名 a
指针变量 p1
指针变量 p1
若 int a[3][4]; int (*p1)[4]=a; int *p2=a[0];
指针变量 p2 指针变量 int *q
二维数组与一维数组指针变量的关系
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 字节的内存区;
指针数组指针数组:用于处理二维数组或多个字符串
• 定义:数组中的元素为指针变量• 定义形式: [ 存储类型 ] 数据类型 * 数组名 [ 数组长度说明 ] ;
例 int *p[4];
指针所指向变量的数据类型指针本身的存储类型区分 int *p[4] 与 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
2
4
6
初始化 :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
2
4
6
指针数组赋值与初始化
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] 0
赋值 :int main(){ char a[]="Fortran"; char b[]="Lisp"; char c[]="Basic"; char *p[4]; p[0]=a; p[1]=b; p[2]=c; p[3]=NULL; ……..}
或 :int main(){ 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] 0
初始化 :int main(){ char *p[]={"Fortran", "Lisp", "Basic",NULL}; ……..}
指针数组赋值与初始化
char name[5][9]={“gain”,“much”,“stronger”, “point”,“bye”};
char *name[5]={“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
name[0]
name[1]
name[2]
name[3]
name[4]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
b y e \0
二维数组与指针数组区别
二维数组存储空间固定字符指针数组相当于可变列长的二维数组分配内存单元 = 数组维数 *2+ 各字符串长度
指针数组元素的作用相当于二维数组的行名但指针数组中元素是指针变量二维数组的行名是地址常量
#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(i=0;i<2;i++) 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
2
4
6
/* 例 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 ; }
例 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]);
}
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;
}
多级指针:指向指针的指针• 定义 : 指向指针的指针• 一级指针 : 指针变量中存放目标变量的地址
p1
&p2 &i 3
P2( 指针变量 ) i( 整型变量 )例 int **p1; int *p2; int i=3; p2=&i; p1=&p2; **p1=5;
• 二级指针 : 指针变量中存放一级指针变量的地址
例 int *p; int i=3; p=&i; *p=5;
&i 3
P( 指针变量 ) i( 整型变量 )
一级指针 单级间接寻址
二级指针 一级指针 目标变量
二级间接寻址
– 定义形式: [ 存储类型 ] 数据类型 ** 指针名;如 char **p;
例 int i, **p; p=&i; ()//p 是二级指针,不能用变量地址为其赋值
指针本身的存储类型最终目标变量的数据类型*p是 p间接指向对象的地址**p 是 p间接指向对象的值例 int i=3;
int *p1; int **p2; p1=&i; p2=&p1; **p=5;
ip1
p2
3&i
&p1
**p2, *p1
*p2
• 多级指针
例 三级指针 int ***p; 四级指针 char ****p;
/*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;}
/*L7-15.C*/#include <stdio.h>int main(void){ int i;
char **p,*name[2]={“1234567”,”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;}
/*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;}
·cpp
cpc
F I R S T \0
P O I N T \0
N E W \0
E N T E R \0
程序运行结果:
POINTERSTEW
·cpp·cpp
例 用二级指针处理字符串/*L7-17.C*/#include <stdio.h>#define NULL 0int 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;}
name[0]
name[1]
name[2]
name[3]
name[4]
char *name[5]
world
bye
\0
hello
good
name
p
644 : good bye
用 *p 可输出地址 (%o 或 %x), 也可用它输出字符串 (%s)
p*(p++)
二级指针与指针数组的关系
int **p 与 int *q[10] – 指针数组名是二级指针常量 ;– p=q; p+i 是 q[i] 的地址 ;– 指针数组作形参, int *q[ ] 与 int **q完全
等价;但作为变量定义两者不同 ;– 系统只给 p 分配能保存一个指针值的内存
区;而给 q 分配 10 块内存区,每块可保存一个指针值 .
定义 含义int i;
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 为指针变量,它指向一个指向整型数据的指针变量
指针的数据类型
例 下列定义的含义( 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 型变量
本讲重点本讲重点
地址运算
数组与指针关系、字符指针
多级指针
指针定义和引用
指针做函数参数、命令行参数
指针变量作为函数参数——指针传递参数地址特点:共享内存 ,“双向”传递
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;}
例 7.18 将数从大到小输出
值传递
….
..…
...
2000
2008
200A
2002
2004
2006
5变量 a
变量 b
(main)
9
5, 95,9
void swap(int *p1, int *p2){ int p; p=*p1; *p1=*p2; *p2=p;}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 ...
59
整型变量 a
整型变量 b
(main)
指针 pointer_1
指针 pointer_220002002
59
例 将数从大到小输出
5,99,5
使用指针传递参数地址
C 有“按引用传递” 吗?没有•C 总是按值传递。你可以自己模拟按引用传递 , 定义接受指针的函数 , 然后在调用时使用& 操作符。事实上 , 当你向函数传入数组 (传入指针的情况参见问题 6.4 及其它 ) 时 , 编译器本质上就是在模拟按引用传递。但是 C 没有任何真正等同于正式的按引用传递或 C++ 的引用参数的东西。
另一方面 , 类似函数的预处理宏可以提供一种“按名称传递”的形式。
swap(int *p1, int *p2){ int *p; *p=*p1; *p1=*p2; *p2=*p;}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;
例 将数从大到小输出
指针变量在使用前必须赋值!
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);}
例 7.19 交换两个数 --- 指针作函数的参数a
5 9b调前:
a5
9b
调swap :
p1
&a
&bp2
a9
5b
交换:
p1
&a
&bp2
a9 5
b返回:
数组作为函数参数– 数组元素作函数实参——值传递
例 两个数组大小比较
4
3
2
1
0
5
a
56
23
12
1076
884
3
2
1
0
5
b
21
23
43
9866
54
n=0m=0k=0
i
n=0m=0k=1
i
n=0m=1k=1
i
n=1m=1k=1
i
n=1m=1k=2
i
n=2m=1k=2
i
n=3m=1k=2
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
#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"); for(i=0;i<10;i++)
scanf("%d",&b[i]); for(i=0;i<10;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);}
数组名作函数参数
• “ 地址传递” : 值传递的特例,形参是指针类型• 在主调函数与被调函数分别定义数组 ,且类型应一致• 形参数组大小 ( 多维数组第一维 ) 可不指定• 形参数组名是地址变量
实参 形参数组名
指针变量数组名
指针变量
数组名数组名指针变量指针变量
一维数组名作函数参数例 求学生的平均成绩
/*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; }
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; }
实参用数组名
形参用数组定义 , int stu[ ]
.
.
2
1
0
9
score
56
23
12
….….
88
stu
例 7.21 数组元素与数组名作函数参数比较
1
2
a
调用前
a[0]
a[1]
1
2
a
调用
a[0]
a[1]
1
2
x
y
2
1
x
y交换
1
2
a
返回
#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
调用前
1
2
a
x
调用
2
1
a
x
交换
2
1
a
返回
#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;}
地址传递
例 7.21 数组元素与数组名作函数参数比较
例 7.22 数组排序 ---- 简单选择排序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); for(i=0;i<10;i++) printf("%d ",a[i]); printf("\n"); return 0;}
0123456789
a 496857329
9927137688
array kjjj kj kjjjjj
9
49
i=0
例 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); for(i=0;i<10;i++) printf("%d ",a[i]); printf("\n"); return 0;}
例 7.23 数组排序 ----简单选择排序
kjj kj
kjjjjj
0123456789
a 496857329
9927137688
array 9
49
k
k
13
68
i=1
例 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); for(i=0;i<10;i++) printf("%d ",a[i]); printf("\n"); return 0;}
i=8
例 7.24 数组排序 ---- 简单选择排序
0123456789
a 9132732495768768899
array
字符串指针作函数参数例 用函数调用实现字符串复制( 1 )用字符数组作参数
( 2 )用字符指针变量作参数
aI
am
a
teac
eh
\0
r.
froma
by
u
ar
a
s
ut
n
de
tob
o
e
t.
\0
I
a
a
e
c
eh
\0
r.
t.
\0
m
t
a
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 */int strlen(char *s){ int n=0; while(*s!=‘\0’)
{ n++;s++;
}return n;
}
/* 版本 2*/int strlen(char *s){ int n=0; while(*s++!=‘\0’)
n++;return n;
}
/* 版本 3*/int strlen(char *s){ int n=0; while(*s++)
n++;return n;
}
/* 版本 4*/int strlen(char *s){ char *p=s; while(*p++); return p-s;}
编写一个函数求字符串的长度
/*L7-25.C: 编写一个函数实现任意两个字符串比较大小 */
int strcmp(char *sa,char *sb)
{
for(;*sa==*sb;sa++,sb++)
if(*sa==‘\0’)
return 0;
return (*sa-*sb);
}
如何调用上面定义的函数?(1) 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”);
(2) 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”);
二维数组的指针作函数参数– 用指向变量的指针变量– 用指向一维数组的指针变量– 用二维数组名– 二维数组形参实际上是一维数组指针变量,
即 int x[ ][10] int (*x)[10]
实参 形参数组名 int x[][4]
指针变量 int (*q)[4]
数组名 int x[][4]
指针变量 int (*q)[4]
数组名 a
数组名 a
指针变量 p1
指针变量 p1
若 int a[3][4]; int (*p1)[4]=a; int *p2=a[0];
指针变量 p2 指针变量 int *q
二维数组名作函数参数例 7.26 求二维数组中最大元素值
1 3 5 7
2 4 6 8
15 17 34 12
i
j
max=1
1 3 5 7
2 4 6 8
15 17 34 12
i
j
max=3
1 3 5 7
2 4 6 8
15 17 34 12
i
j
max=5j
1 3 5 7
2 4 6 8
15 17 34 12
i
max=7
j
1 3 5 7
2 4 6 8
15 17 34 12
i
max=7
j
1 3 5 7
2 4 6 8
15 17 34 12i
max=34
#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]
例 求二维数组中各行元素之和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); for(i=0;i<row;i++) printf("The sum of row[%d]=%d\n",i+1,sum_row[i]); return 0;}
3
1 4
6
7
9a sum_rowx
result
18
12
例 7.27 3 个学生各学 4 门课,计算总平均分,并输出第 n 个学生成绩
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;}
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));}
列指针
行指针
函数说明
float p[][4]
65 52 79 60
80 87 90 81
90 99 100 98
p
p
p[n][i]
例 7.28 3 个学生各学 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); for(i=0;i<4;i++)
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
90 99 100 98
p
p[j][i]
例 二级指针做函数参数
#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
12
变量 a
变量 b
(main)
指针变量 p2000 指针变量 q2002
20062004
COPY
二级指针 s
二级指针 r(swap)
指针变量 t
20002002
2000
例二级指针做函数参数
#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
12
变量 a
变量 b
(main)
指针变量 p2000 指针变量 q200220002002
2,1
例二级指针做函数参数
#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
p
q
b
a
p
q
a
b
r
s
p
q
a
b
r
s
p
q
2,1
例 7.29 编写函数,从一个已经创建好链表中删除指定结点,该链表由整数构成
#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); }
elsereturn (0); }
#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);
文件包含编译预处理命令
函数类型说明
函数定义
函数调用
函数调用
函数返回值
形参
实参
指向函数的指针变量– 函数指针:函数在编译时被分配的入口地址 , 用
函数名表示max
….
..
指令 1指令 2
• 函数指针变量赋值 : 如 p=max;函数返回值的数据类型专门存放函数入口地址
可指向返回值类型相同的不同函数
– 指向函数的指针变量• 定义形式: 数据类型 (* 指针变量名 )();
如 int (*p)();
函数指针变量指向的函数必须有函数说明• 函数调用形式: c=max(a,b); c=(*p)(a,b); c=p (a,b);
• 对函数指针变量 pn, p++, p-- 无意义
( ) 不能省int (*p)() 与 int *p() 不同
例 7.30 用函数指针变量调用函数,比较两个数大小
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);}
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);}
用函数指针变量作函数参数
例 7.31 用函数指针变量作参数,求最大值、最小值和两数之和
例 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=”);printf(“max=”); return(x>y?x:y);return(x>y?x:y);}int min(int x,int y){ printf(“min=”);printf(“min=”); return(x<y?x:y);return(x<y?x:y);}int add(int x,int y){ printf(“sum=”); printf(“sum=”); return(x+y);return(x+y);}
命令行参数
• 命令行:在操作系统状态下,为执行某个程序而键入的一行字符• 命令行一般形式:命令名 参数 1 参数 2……… 参数 n
main(int argc, char *argv[]){ ………}
• 命令行参数传递
带参数的main函数形式:C:\TC> copy[.exe] source.c temp.c
有 3 个字符串参数的命令行
命令行中参数个数 元素指向命令行参数中各字符串首地址
形参名任意
命令行实参 main( 形参 )
系统自动调用main 函数时传递
第一个参数 : main 所在的可执行文件名
例 7.32 输出命令行参数
/*test.c*/main(int argc, char *argv[]){ while(argc>1) { ++argv; printf("%s\n",*argv); --argc; }}
1. 编译、链接 test.c ,生成可执行文件 test.exe2. 在 DOS 状态下运行 (test.exe 所在路径下)
例如: C:\TC> test[.exe] hello world!
helloworld!
argv[0]
argv[1]
argv[2]
char *argv[]
world
test
hello
argv
argc=3
例 7.32 输出命令行参数
int main(int argc, char *argv[]){ while(argc-->0) printf("%s\n",*argv++);}
testhelloworld!
argv[0]
argv[1]
argv[2]
char *argv[]
world
test
hello
argv
argc=3
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*/
例 7.33, 编程模拟实现 UNIX 中的 grep 命令的基本功能
#include <stdio.h>#include <string.h>#define MAXLINE 1000int 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;}
/*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;
}
例 7.34, 改进为带参数的命令
find -x –n pattern或者为find –xn patternX: 表示形式与 pattern不匹配的行N: 要求形式行号
#include <stdio.h>#include <string.h>#define MAXLINE 1000int 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; }
if(argc!=1)printf(“Usage:find –x –n pattern\n”);
elsewhile(getline(line,MAXLINE)>0){ lineno++;
if((strstr(line,*argv)!=NULL)!=except){ if(number)
printf(“%ld:”,lineno);printf(“%s”,line);found++;
}}return found;
}
THE END