128
第第第 第第 第第第第第第第第第 第第第第第第第第第 第: [email protected] 第: C 第第第第第第

第七章 指针

Embed Size (px)

DESCRIPTION

第七章 指针. 教 材 : C 程序设计导论. 主 讲 : 谭 成 予 [email protected]. 武汉大学计算机学院. 指针定义和引用. 地址运算. 多级指针. 数组与指针关系、字符指针. 指针做函数参数、命令行参数. 本讲重点. 指针的优点. C 程序设计中使用指针可以 : 使程序 简洁 、 紧凑 、 高效 有效 地表示复杂的数据结构 动态 分配内存 得到 多于一个 的函数返回值. 内存. 0. …. 2000. 2001. 2002. 2003. 2005. - PowerPoint PPT Presentation

Citation preview

Page 1: 第七章     指针

第七章 指针 第七章 指针

武汉大学计算机学院武汉大学计算机学院武汉大学计算机学院武汉大学计算机学院

主 讲 : 谭 成 予

[email protected]

教 材 : C 程序设计导论

Page 2: 第七章     指针

本讲重点本讲重点

地址运算

数组与指针关系、字符指针

多级指针

指针定义和引用

指针做函数参数、命令行参数

Page 3: 第七章     指针

C 程序设计中使用指针可以 :– 使程序简洁、紧凑、高效– 有效地表示复杂的数据结构– 动态分配内存– 得到多于一个的函数返回值

指针的优点指针的优点

Page 4: 第七章     指针

指针与地址变量与地址

程序中 : int i;

float k;

内存中每个字节有一个编号 ----- 地址

….

..…

...

2000

2001

2002

2005

内存0

2003

i

k

编译或函数调用时为其分配内存单元

变量是对程序中数据存储空间的抽象

Page 5: 第七章     指针

指针与指针变量

•指针:一个变量的地址;•指针变量:专门存放变量地址的变量。

Page 6: 第七章     指针

….

..…

...

2000

2004

2006

2005

整型变量 i10

变量 i_pointer

2001

2002

2003

2000

指针

指针变量

变量的内容 变量的地址

指针与指针变量

Page 7: 第七章     指针

指针变量

变量

变量地址 ( 指针 )

变量值指向 地址存入

指针变量

指针与指针变量

Page 8: 第七章     指针

例 i=3; ----- 直接访问

指针变量

….

..…

...

2000

2004

2006

2005

整型变量 i10

变量 i_pointer

2001

2002

2003

2000

3

例 *i_pointer=20; ----- 间接访问

20

直接访问与间接访问•直接访问:按变量地址存取变量值•间接访问:通过存放变量地址的变量去访问变量

Page 9: 第七章     指针

指针变量的定义和引用

指针变量的定义• 一般形式: [ 存储类型 ] 数据类型 * 指针名;

3

变量 i

2000

i_pointer

*i_pointer

i *i_pointer&i i_pointer

i=3; *i_pointer=3

合法标识符指针变量本身的存储类型指针的目标变量的数据类型表示定义指针变量不是‘ *’ 运算符

Page 10: 第七章     指针

指针变量的定义和引用

例 int *p1,*p2; float *q ; static char *name;

注意:1 、 int *p1, *p2; 与 int *p1, p2;2 、指针变量名是 p1,p2 , 不是 *p1,*p23 、指针变量只能指向定义时所规定类型的变量4 、指针变量定义后,变量值不确定,应用前必须先赋值

Page 11: 第七章     指针

指针变量的初始化一般形式: [ 存储类型 ] 数据类型 * 指针名 = 初始地址值;

赋给指针变量,不是赋给目标变量

例 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型指针

用已初始化指针变量作初值

Page 12: 第七章     指针

#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

随机

Page 13: 第七章     指针

• 零指针: ( 空指针 )– 定义 : 指针变量值为零– 表示: int * p=0;

p 指向地址为 0的单元,系统保证该单元不作它用表示指针变量值没有意义

#define NULL 0int *p=NULL:

零指针与空类型指针

Page 14: 第七章     指针

– p=NULL 与未对 p 赋值不同– 用途 :

避免指针变量的非法引用在程序中常作为状态比较

例 int *p; ...... while(p!=NULL) { ...… }

零指针与空类型指针

Page 15: 第七章     指针

• void * 类型指针– 表示 : void *p; – 使用时要进行强制类型转换

例 char *p1; void *p2; p1=(char *)p2; p2=(void *)p1;

表示不指定 p是指向哪一种类型数据的指针变量

零指针与空类型指针

Page 16: 第七章     指针

空指针到底是什么?

空指针不同于未初始化的指针。空指针可以确保不指向任何对象或函数 ; 未初始化指针则可能指向任何地方。每种指针类型都有一个空指针 , 而不同类型的空指针的内部表示可能不尽相同。尽管程序员不必知道内部值 , 但编译器必须时刻明确需要那种空指针 , 以便在需要的时候加以区分。

每一种指针类型都有一个特殊值—— “空指针” :它与同类型的其它所有指针值都不相同 , 它“与任何对象或函数的指针值都不相等”。对 malloc() 的成功调用也不会返回空指针 , 如果失败 , malloc() 的确返回空指针 , 这是空指针的典型用法 : 表示“未分配” 或者“尚未指向任何地方” 的指针。

Page 17: 第七章     指针

指针变量的引用: & 与 * 运算符• 含义

含义 : 取变量的地址单目运算符优先级 : 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)

Page 18: 第七章     指针

指针变量…

...

….

..

2000

2004

2006

2005

整型变量 i10

变量 i_pointer

2001

2002

2003

2000

整型变量 k

例 k=i; -- 直接访问 k=*i_pointer; -- 间接访问

10

例 k=i; k=*i_pointer;

Page 19: 第七章     指针

例 指针的概念

#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

Page 20: 第七章     指针

例 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

Page 21: 第七章     指针

malloc() 函数

 void *malloc(unsigned int size) ; 功能:在动态存储区域中分配一个 size 字节的连续空间。

函数的返回值为指针,它的值是所分配的存储区域的起始地址。

如没有足够的存储空间分配,则返回 0 (记为 NULL )值。

动态存储分配 – 实际编程中可能遇到所需内存空间无法预先确定的情况,需

要根据实际的数据的多少来确定。– C 语言提供动态分配内存函数 ( stdlib.h) 。

Page 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

Page 23: 第七章     指针

动态分配内存

calloc() 函数 calloc 函数的原型为: void *calloc(unsigned int n, unsigned int size);

函数的功能是:在动态存储区域中分配 n 个为 size 字节的连续空间,并将该存储空间自动置初值 0 。函数的返回值为指针,它的值是所分配的存储区域的起始地址。如分配不成功,则返回 0 值。

n 表示对象的个数 size 每个对象占用内存单元的字节数

Page 24: 第七章     指针

#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

Page 25: 第七章     指针

动态分配内存free() 函数    

        void free(void *ptr) ;   函数的功能是:释放由 ptr 所指向的存储空间。 ptr 的值必须是 malloc 或 calloc 函数返回的地址。此函数无返回值。

Page 26: 第七章     指针

动态分配内存realloc 函数 realloc 函数的原型为: void *realloc(void *ptr,size_t size);

realloc 函数的作用是对 p 所指向的存储区进行重新分配即改变大小;

ptr: 是已经由 malloc 或 calloc 函数分配的存储区的指针 ;size: 是重新分配的存储区的大小(字节数),新存储区包含着和旧存储区相同的内容,如果新存储区较大,则新增加的部分未被初始化。此函数返回值是新存储区的首地址;如果没有足够的内存空间则返回 NULL ,此时旧存储区内容不变。

Page 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) {

printf(“Allocation error\n”);exit(1); }

strcat(p,”.”); printf(“%x,%s\n”,p,p); /* 输出字符串首地址和内容 */free(p); /* 释放存储区 */return 0;

}

Page 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;

}

Page 29: 第七章     指针

free() 怎么知道有多少字节需要释放?

malloc/free 的实现会在分配的时候记下每一块的大小 , 所以在释放的时候就不必再考虑了。

calloc() 和 malloc() 有什么区别?利用 calloc的零填充功能安全吗? free() 可以释放 calloc()分配的内存吗 , 还是需要一个 cfree()?calloc(m, n) 本质上等价于 p = malloc(m * n);memset(p, 0, m * n);填充的零是全零 , 因此不能确保生成有用的空指针值或浮点零值。free() 可以安全地用来释放 calloc() 分配的内存。

Page 30: 第七章     指针

本讲重点本讲重点

地址运算

数组与指针关系、字符指针

多级指针

指针定义和引用

指针做函数参数、命令行参数

Page 31: 第七章     指针

指针的运算• 指针变量的赋值运算

– 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; ()

指针变量与其指向的变量具有相同数据类型

Page 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 无意义

指针的算术运算

Page 33: 第七章     指针

例 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

指针的算术运算

Page 34: 第七章     指针

指针变量的关系运算

若 p1 和 p2 指向同一数组,则:p1<p2 表示 p1 指的元素在前;p1>p2 表示 p1 指的元素在后;p1==p2 表示 p1 与 p2 指向同一元素。

若 p1 与 p2 不指向同一数组,比较无意义;

p==NULL 或 p!=NULL

Page 35: 第七章     指针

例 7.5 堆栈( stack )是一种先进后出( first-in last-out )的表,好比将若干个盘子堆放起来,每次放或者取一个盘子,最先堆放的盘子最后被取走,将一个数据压入堆栈称为入栈,从堆栈中取走一个数据称为出栈操作。现在编程实现该算法。

p1

……

stack[0]

stack[1]

stack[2]

10p1

3p1

push(10)

push(3)

pop()

pop()

Page 36: 第七章     指针

#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;

}

Page 37: 第七章     指针

/* 将入栈操作定义成用户自定义函数 */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);

}

Page 38: 第七章     指针

本讲重点本讲重点

地址运算

数组与指针关系、字符指针

多级指针

指针定义和引用

指针做函数参数、命令行参数

Page 39: 第七章     指针

指针变量与数组– 指向数组元素的指针变量

例 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

数组名是表示数组首地址的地址常量 ,就是数组的指针。

Page 40: 第七章     指针

数组元素表示方法

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]

Page 41: 第七章     指针

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

Page 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) ()

Page 43: 第七章     指针

例 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

Page 44: 第七章     指针

#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

指针变量可以指到数组后的内存单元

Page 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 字节的内存区

Page 46: 第七章     指针

在 C语言中“指针和数组等价”到底是什么意思?

不表示它们相同 , 甚至也不能互换。可以用指针方便的访问数组或者模拟数组。

等价的基础:一旦数组出现在表达式中 , 编译器会隐式地生成一个指向数组第一个成员地指针 , 就像程序员写出了 &a[0] 一样。

例外的情况是 , 数组为 sizeof 或 &操作符的操作数 , 或者为字符数组的字符串初始值。

这个定义的后果:编译器不严格区分数组下标操作符和指针。a[i], 根据上边的规则 , 数组蜕化为指针然后按照指针变量的方式如 p[i] 那样寻址 , 尽管最终的内存访问并不一样。如果你把数组地址赋给指针 :p = a; 那么 p[3] 和 a[3] 将会访问同样的成员。

Page 47: 第七章     指针

指针与字符串字符串• 字符数组

#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

Page 48: 第七章     指针

用字符指针实现

#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

Page 49: 第七章     指针

字符指针变量与字符数组的区别

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); ()

Page 50: 第七章     指针

字符指针变量与字符数组的区别如果字符指针在定义是初始化,如: 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” 不能向数组名赋值

Page 51: 第七章     指针

分析:– 程序中定义两个字符数组 s 和 t 存放输入的两个字符串– 定义两个字符指针 ps 和 pt ,它们的初值为分别指向 s 和 t 两

个字符串首字符。– 逐个判断 s 和 t 的每一对字符是否相等,从左到右直到找到第

一对不相等的字符或者所有字符判断完成为止。

例 7.9 输入两个字符串,变成比较两个字符串的大小。

Page 52: 第七章     指针

#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;}

Page 53: 第七章     指针

例 7.10 编程从键盘输入一个字符串,然后按照字符顺序从小到大进行排列,并删除重复的字符。

分析:定义字符数组 str 表示输入的字符串,程序中采用冒泡排序法进行排序。

Page 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(p=str;*p;p++){ for(q=p;*p==*q;q++);

if(p!=q)strcpy(p+1,q);

}printf(“ 结果字符串是: %s\n”,str);return 0;

}

Page 55: 第七章     指针

多维数组的指针表示

• 二维数组的地址

对于一维数组 :( 1 )数组名 array 表示数组的首地址,即 array[0] 的地址;( 2 )数组名 array 是地址常量( 3 ) array+i 是元素 array[i] 的地址( 4 ) array[i] *(array+i)

array int array[10];

Page 56: 第七章     指针

对于二维数组:( 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

Page 57: 第七章     指针

– 对二维数组 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

Page 58: 第七章     指针

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

Page 59: 第七章     指针

表示形式 含义 地址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

Page 60: 第七章     指针

二维数组的指针变量

例 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

Page 61: 第七章     指针

指向一维数组的指针变量定义形式: 数据类型 (* 指针名 )[ 一维数组维数 ];

例 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)

一维数组指针变量维数和二维数组列数必须相同

Page 62: 第七章     指针

例 一维数组指针变量举例#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]

Page 63: 第七章     指针

字符串与数组关系字符串用一维字符数组存放;字符数组具有一维数组的所有特点:

数组名是指向数组首地址的地址常量;数组元素的引用方法可用指针法和下标法;

区别存储格式:字符串结束标志;赋值方式与初始化;输入输出方式: %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);

Page 64: 第七章     指针

本讲重点本讲重点

地址运算

数组与指针关系、字符指针

多级指针

指针定义和引用

指针做函数参数、命令行参数

Page 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;

} 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

Page 66: 第七章     指针

二维数组的指针作函数参数用指向变量的指针变量 ;用指向一维数组的指针变量 ;用二维数组名。

实参 形参数组名 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

Page 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 字节的内存区;

Page 68: 第七章     指针

指针数组指针数组:用于处理二维数组或多个字符串

• 定义:数组中的元素为指针变量• 定义形式: [ 存储类型 ] 数据类型 * 数组名 [ 数组长度说明 ] ;

例 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

Page 69: 第七章     指针

指针数组赋值与初始化

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; ……..}

Page 70: 第七章     指针

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}; ……..}

指针数组赋值与初始化

Page 71: 第七章     指针

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+ 各字符串长度

指针数组元素的作用相当于二维数组的行名但指针数组中元素是指针变量二维数组的行名是地址常量

Page 72: 第七章     指针

#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

Page 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 ; }

Page 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]);

}

Page 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;

}

Page 76: 第七章     指针

多级指针:指向指针的指针• 定义 : 指向指针的指针• 一级指针 : 指针变量中存放目标变量的地址

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( 整型变量 )

一级指针 单级间接寻址

二级指针 一级指针 目标变量

二级间接寻址

Page 77: 第七章     指针

– 定义形式: [ 存储类型 ] 数据类型 ** 指针名;如 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;

Page 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;}

Page 79: 第七章     指针

/*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;}

Page 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;}

·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

Page 81: 第七章     指针

例 用二级指针处理字符串/*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++)

Page 82: 第七章     指针

二级指针与指针数组的关系

int **p 与 int *q[10] – 指针数组名是二级指针常量 ;– p=q; p+i 是 q[i] 的地址 ;– 指针数组作形参, int *q[ ] 与 int **q完全

等价;但作为变量定义两者不同 ;– 系统只给 p 分配能保存一个指针值的内存

区;而给 q 分配 10 块内存区,每块可保存一个指针值 .

Page 83: 第七章     指针

定义 含义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 为指针变量,它指向一个指向整型数据的指针变量

指针的数据类型

Page 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 型变量

Page 85: 第七章     指针

本讲重点本讲重点

地址运算

数组与指针关系、字符指针

多级指针

指针定义和引用

指针做函数参数、命令行参数

Page 86: 第七章     指针

指针变量作为函数参数——指针传递参数地址特点:共享内存 ,“双向”传递

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

Page 87: 第七章     指针

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

使用指针传递参数地址

Page 88: 第七章     指针

C 有“按引用传递” 吗?没有•C 总是按值传递。你可以自己模拟按引用传递 , 定义接受指针的函数 , 然后在调用时使用& 操作符。事实上 , 当你向函数传入数组 (传入指针的情况参见问题 6.4 及其它 ) 时 , 编译器本质上就是在模拟按引用传递。但是 C 没有任何真正等同于正式的按引用传递或 C++ 的引用参数的东西。

另一方面 , 类似函数的预处理宏可以提供一种“按名称传递”的形式。

Page 89: 第七章     指针

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;

例 将数从大到小输出

指针变量在使用前必须赋值!

Page 90: 第七章     指针

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返回:

Page 91: 第七章     指针

数组作为函数参数– 数组元素作函数实参——值传递

例 两个数组大小比较

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);}

Page 92: 第七章     指针

数组名作函数参数

• “ 地址传递” : 值传递的特例,形参是指针类型• 在主调函数与被调函数分别定义数组 ,且类型应一致• 形参数组大小 ( 多维数组第一维 ) 可不指定• 形参数组名是地址变量

实参 形参数组名

指针变量数组名

指针变量

数组名数组名指针变量指针变量

Page 93: 第七章     指针

一维数组名作函数参数例 求学生的平均成绩

/*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

Page 94: 第七章     指针

例 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;}

值传递

Page 95: 第七章     指针

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 数组元素与数组名作函数参数比较

Page 96: 第七章     指针

例 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

Page 97: 第七章     指针

例 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;}

Page 98: 第七章     指针

例 7.23 数组排序 ----简单选择排序

kjj kj

kjjjjj

0123456789

a 496857329

9927137688

array 9

49

k

k

13

68

i=1

Page 99: 第七章     指针

例 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;}

Page 100: 第七章     指针

i=8

例 7.24 数组排序 ---- 简单选择排序

0123456789

a 9132732495768768899

array

Page 101: 第七章     指针

字符串指针作函数参数例 用函数调用实现字符串复制( 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;}

Page 102: 第七章     指针

/*版本 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;}

编写一个函数求字符串的长度

Page 103: 第七章     指针

/*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”);

Page 104: 第七章     指针

二维数组的指针作函数参数– 用指向变量的指针变量– 用指向一维数组的指针变量– 用二维数组名– 二维数组形参实际上是一维数组指针变量,

即 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

Page 105: 第七章     指针

二维数组名作函数参数例 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

Page 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]

Page 107: 第七章     指针

例 求二维数组中各行元素之和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

Page 108: 第七章     指针

例 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]

Page 109: 第七章     指针

例 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]

Page 110: 第七章     指针

例 二级指针做函数参数

#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

Page 111: 第七章     指针

例二级指针做函数参数

#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

Page 112: 第七章     指针

例二级指针做函数参数

#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

Page 113: 第七章     指针

例 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); }

Page 114: 第七章     指针

#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);

文件包含编译预处理命令

函数类型说明

函数定义

函数调用

函数调用

函数返回值

形参

实参

Page 115: 第七章     指针

指向函数的指针变量– 函数指针:函数在编译时被分配的入口地址 , 用

函数名表示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() 不同

Page 116: 第七章     指针

例 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);}

Page 117: 第七章     指针

用函数指针变量作函数参数

例 7.31 用函数指针变量作参数,求最大值、最小值和两数之和

Page 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=”);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);}

Page 119: 第七章     指针

命令行参数

• 命令行:在操作系统状态下,为执行某个程序而键入的一行字符• 命令行一般形式:命令名 参数 1 参数 2……… 参数 n

main(int argc, char *argv[]){ ………}

• 命令行参数传递

带参数的main函数形式:C:\TC> copy[.exe] source.c temp.c

有 3 个字符串参数的命令行

命令行中参数个数 元素指向命令行参数中各字符串首地址

形参名任意

命令行实参 main( 形参 )

系统自动调用main 函数时传递

第一个参数 : main 所在的可执行文件名

Page 120: 第七章     指针

例 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

Page 121: 第七章     指针

例 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

Page 122: 第七章     指针

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 命令的基本功能

Page 123: 第七章     指针

#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;}

Page 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;

}

Page 125: 第七章     指针

例 7.34, 改进为带参数的命令

find -x –n pattern或者为find –xn patternX: 表示形式与 pattern不匹配的行N: 要求形式行号

Page 126: 第七章     指针

#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; }

Page 127: 第七章     指针

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;

}

Page 128: 第七章     指针

THE END