slide1 n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
第七章 指针 PowerPoint Presentation
Download Presentation
第七章 指针

Loading in 2 Seconds...

play fullscreen
1 / 128

第七章 指针 - PowerPoint PPT Presentation


  • 113 Views
  • Uploaded on

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

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about '第七章 指针' - kaitlyn-sweemey


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
slide1

第七章 指针

教 材: C程序设计导论

主 讲: 谭 成 予

nadinetan@163.com

武汉大学计算机学院

slide2

指针定义和引用

地址运算

多级指针

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

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

本讲重点

slide3

指针的优点

C程序设计中使用指针可以:

  • 使程序简洁、紧凑、高效
  • 有效地表示复杂的数据结构
  • 动态分配内存
  • 得到多于一个的函数返回值
slide4

内存

0

…...

2000

2001

2002

2003

2005

…...

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

指针与地址

  • 变量与地址

程序中: int i;

float k;

i

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

k

变量是对程序中数据

存储空间的抽象

slide5

指针与指针变量

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

…...

整型变量i

2000

10

2001

2002

2003

变量i_pointer

2004

2005

2006

…...

指针与指针变量

指针

变量的内容

变量的地址

2000

指针变量

slide7

变量地址(指针)

指针变量

地址存入

指针变量

指向

变量值

变量

指针与指针变量

slide8

…...

整型变量i

2000

10

2001

2002

2003

变量i_pointer

2004

2000

指针变量

2005

2006

…...

直接访问与间接访问

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

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

3

20

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

slide9

i

*i_pointer

i_pointer

变量i

&i

i_pointer

2000

3

i=3;

*i_pointer=3

*i_pointer

指针变量的定义和引用

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

表示定义指针变量

不是‘*’运算符

合法标识符

指针变量本身的存储类型

指针的目标变量的数据类型

slide10

例 int *p1,*p2;

float *q ;

static char *name;

指针变量的定义和引用

注意:

1、int *p1, *p2; 与 int *p1, p2;

2、指针变量名是p1,p2 ,不是*p1,*p2

3、指针变量只能指向定义时所规定类型的变量

4、指针变量定义后,变量值不确定,应用前必须先赋值

slide11

例int *p=&i;

int i;

例 int i;

int *p=&i;

赋给指针变量,

不是赋给目标变量

变量必须已说明过

类型应一致

  • 指针变量的初始化

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

例 int i;

int *p=&i;

int *q=p;

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

例int main( )

{ int i;

static int *p=&i;

..............

} ()

不能用auto变量的地址

去初始化static型指针

slide12

#include<stdio.h>

int main()

{ int i=10;

int *p;

*p=i;

printf("%2d",*p);

return 0;

}

…...

整型变量i

2000

10

2001

2002

2003

指针变量p

2004

2005

随机

2006

…...

指针变量必须先赋值,再使用

危险!

#include<stdio.h>

int main( )

{ int i=10,k;

int *p;

p=&k;

*p=i;

printf("%2d",*p);

return 0;

}

slide13

零指针与空类型指针

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

p指向地址为0的单元,

系统保证该单元不作它用

表示指针变量值没有意义

#define NULL 0

int *p=NULL:

slide14

零指针与空类型指针

  • p=NULL与未对p赋值不同
  • 用途:
    • 避免指针变量的非法引用
    • 在程序中常作为状态比较

例 int *p;

......

while(p!=NULL)

{ ...…

}

slide15

零指针与空类型指针

表示不指定p是指向哪一种

类型数据的指针变量

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

例 char *p1;

void *p2;

p1=(char *)p2;

p2=(void *)p1;

slide16
空指针到底是什么?

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

空指针不同于未初始化的指针。

空指针可以确保不指向任何对象或函数;

未初始化指针则可能指向任何地方。

每种指针类型都有一个空指针, 而不同类型的空指针的内部表示可能不尽相同。尽管程序员不必知道内部值, 但编译器必须时刻明确需要那种空指针, 以便在需要的时候加以区分。

slide17

*i_pointer

i_pointer

10

2000

&i_pointer

i

…...

整型变量i

2000

10

2001

2002

2003

变量i_pointer

2004

2000

指针变量

2005

2006

…...

含义: 取指针所指向变量的内容

单目运算符

优先级: 2

结合性:自右向左

含义: 取变量的地址

单目运算符

优先级: 2

结合性:自右向左

  • 两者关系:互为逆运算
  • 理解

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

  • 含义

i_pointer &i &(*i_pointer)

i *i_pointer *(&i)

i_pointer = &i = &(*i_pointer)

i = *i_pointer = *(&i)

i_pointer-----指针变量,它的内容是地址量

*i_pointer----指针的目标变量,它的内容是数据

&i_pointer---指针变量占用内存的地址

slide18

…...

整型变量i

2000

10

2001

整型变量k

2002

2003

变量i_pointer

2004

2005

2000

指针变量

2006

…...

例k=i; --直接访问

k=*i_pointer; --间接访问

例k=i;

k=*i_pointer;

10

slide19

…...

整型变量a

f86

10

f87

指针变量pa

f88

f86

f89

f8a

f8b

f8c

…...

a:10

*pa: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;

}

slide20

…...

2000

指针变量p1

2002

2004

指针变量p

指针变量p2

整型变量a

整型变量b

2006

2008

…...

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

}

2008

2006

2006

2008

2006

5

9

5,9

max=9,min=5

slide21

动态存储分配

  • 实际编程中可能遇到所需内存空间无法预先确定的情况,需要根据实际的数据的多少来确定。
  • C语言提供动态分配内存函数 (stdlib.h)。
  • malloc()函数

void *malloc(unsigned int size);

功能:在动态存储区域中分配一个size字节的连续空间。

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

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

slide22

动态存储分配

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

slide23

calloc()函数

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

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

n 表示对象的个数

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

动态分配内存

slide24

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

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:20

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

slide25

动态分配内存

  • free()函数

void free(void *ptr) ;

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

slide26

动态分配内存

realloc函数realloc函数的原型为:void *realloc(void *ptr,size_t size);

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

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

}

动态存储分配函数的使用举例

slide28

动态存储分配函数的使用举例

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

}

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

malloc/free 的实现会在分配的时候记下每一块的

大小, 所以在释放的时候就不必再考虑了。

calloc() 和malloc() 有什么区别?利用calloc的零填充功能安全吗?free() 可以释放calloc()分配的内存吗, 还是需要一个cfree()?

calloc(m, n) 本质上等价于p = malloc(m * n);

memset(p, 0, m * n);

填充的零是全零, 因此不能确保生成有用的空指针值或浮点零值。

free() 可以安全地用来释放calloc() 分配的内存。

slide30

指针定义和引用

地址运算

多级指针

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

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

本讲重点

slide31

指针的运算

  • 指针变量的赋值运算
    • 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; ()

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

slide32

指针的算术运算

  • pi  p id (i为整型数,d为p指向的变量所占字节数)
  • p++, p--, p+i, p-i, p+=i, p-=i等
  • 若p1与p2指向同一数组,p1-p2=两指针间元素个数(p1-p2)/d
  • p1+p2 无意义
slide33

a数组

p

a[0]

p+1,a+1

a[1]

a[2]

a[3]

a[4]

p+i,a+i

a[5]

a[6]

a[7]

a[8]

p+9,a+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;

slide34

指针变量的关系运算

  • 若p1和p2指向同一数组,则:
    • p1<p2 表示p1指的元素在前;
    • p1>p2 表示p1指的元素在后;
    • p1==p2表示p1与p2指向同一元素。
  • 若p1与p2不指向同一数组,比较无意义;
  • p==NULL或p!=NULL
slide35

stack[0]

p1

p1

p1

stack[1]

stack[2]

……

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

push(10)

10

push(3)

3

pop()

pop()

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

}

slide37
/*将入栈操作定义成用户自定义函数*/

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

}

slide38

指针定义和引用

地址运算

多级指针

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

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

本讲重点

slide39
指针变量与数组

指向数组元素的指针变量

p

array[0]

array[1]

array[2]

array[3]

...

array[9]

整型指针p

&array[0]

例 int array[10];

int *p;

p=&array[0]; // p=array;

或 int *p=&array[0];

或 int *p=array;

数组名是表示数组首地址的地址常量,

就是数组的指针。

slide40

地址

p[0]

a[0]

a[0]

a[0]

*p

*a

元素

地址

元素

*(p+1)

*(a+1)

p[1]

a[1]

a[1]

a[1]

*(p+2)

*(a+2)

p[2]

a[2]

a[2]

a[2]

p

a

a[3]

a[3]

p+1

a+1

p+2

a+2

a[9]

a[9]

p[9]

*(p+9)

*(a+9)

a[9]

...

...

p+9

a+9

指针法

下标法

数组元素表示方法

[] 变址运算符

a[i] *(a+i)

a[i]  p[i]  *(p+i) *(a+i)

slide41

pa

a[0]

1

a[1]

2

a[2]

3

a[3]

4

a[4]

5

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

}

slide42

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

slide43

a

0

5

1

8

p

p

2

7

3

6

4

2

5

7

6

3

例7.7注意指针变量的运算

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

}

6

5 6

slide44

a

0

5

1

8

p

p

p

p

p

p

p

p

2

7

3

6

4

2

5

7

6

3

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

}

p=a;

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

slide45

一级指针变量与一维数组的关系

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字节的内存区
slide46
在C语言中“指针和数组等价”到底是什么意思?在C语言中“指针和数组等价”到底是什么意思?

不表示它们相同, 甚至也不能互换。

可以用指针方便的访问数组或者模拟数组。

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

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

这个定义的后果:编译器不严格区分数组下标操作符和指针。

a[i], 根据上边的规则, 数组蜕化为指针然后按照指针变量的方式如p[i] 那样寻址, 尽管最终的内存访问并不一样。如果你把数组地址赋给指针:p = a; 那么p[3] 和a[3] 将会访问同样的成员。

slide47

string

string[0]

I

string[1]

string[2]

l

string[3]

o

string[4]

v

string[5]

e

string[6]

string[7]

C

string[8]

h

string[9]

i

string[10]

n

string[11]

a

string[12]

!

string[13]

\0

指针与字符串

字符串

  • 字符数组

#include <stdio.h>

int main( )

{ char string[]=“I love China!”;

printf(“%s\n”,string);

printf(“%s\n”,string+7);

return 0;

}

slide48

string

I

l

o

string

v

e

C

h

i

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!=0

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

char *cp;与char str[20];

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

slide50

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

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

分析:

  • 程序中定义两个字符数组s和t存放输入的两个字符串
  • 定义两个字符指针ps和pt,它们的初值为分别指向s和t两个字符串首字符。
  • 逐个判断s和t的每一对字符是否相等,从左到右直到找到第一对不相等的字符或者所有字符判断完成为止。
slide52

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

else

printf("%s = %s\n",s,t);

return 0;

}

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

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

slide54

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

}

slide55

int array[10];

array

多维数组的指针表示

  • 二维数组的地址

对于一维数组:

(1)数组名array表示数组的首地址,即array[0]的地址;

(2)数组名array是地址常量

(3)array+i是元素array[i]的地址

(4)array[i]  *(array+i)

slide56

*(a[0]+1)

*(*(a+0)+1)

2000

a

2000

a[0]

a[0][0]

2002

a[0]+1

a[0][1]

*(a+0)+1

a[0][2]

a[0][3]

2008

a+1

2008

a[1]

a[1][0]

2010

a[1]+1

a[1][1]

*(a+1)+1

a[1][2]

a[1][3]

2016

a+2

2016

a[2]

a[2][0]

2018

a[2]+1

a[2][1]

*(a+2)+1

a[2][2]

a[2][3]

行指针与列指针

int a[3][4];

对于二维数组:

(1)a是数组名,

包含三个元素

a[0],a[1],a[2]

(2)每个元素a[i]

又是一个一维

数组,包含4个

元素

基类型

slide57

int a[3][4];

2000

2000

a

a[0][0]

a[0]

2002

a[0][1]

a[0][2]

a[0][3]

2008

2008

a+1

a[1][0]

a[1]

2010

a[1][1]

a[1][2]

a[1][3]

2016

a+2

2016

a[2]

a[2][0]

2018

a[2][1]

a[2][2]

a[2][3]

  • 对二维数组 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列元素地址,指向列
slide58

int a[3][4];

行指针

a[0][0]

a[0][1]

列指针

a[0][2]

a[0][3]

a[1][0]

a[1][1]

a[1][2]

a[1][3]

a[2][0]

a[2][1]

a[2][2]

a[2][3]

地址表示:

(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

二维数组元素表示形式:

(1)a[1][2]

(2)*(a[1]+2)

(3)*(*(a+1)+2)

(4)*(&a[0][0]+1*4+2)

slide59

表示形式

地址

含义

a

2000

二维数组名,数组首地址

2000

a[0],*(a+0),*a

第0行第0列元素地址

2008

a+1

第1行首地址

2008

第1行第0列元素地址

a[1],*(a+1)

2012

第1行第2列元素地址

a[1]+2,*(a+1)+2,&a[1][2]

第1行第2列元素值

13

*(a[1]+2),*(*(a+1)+2),a[1][2]

slide60

p

int a[3][4];

a[0][0]

a[0][1]

a[0][2]

a[0][3]

a[1][0]

a[1][1]

a[1][2]

a[1][3]

a[2][0]

p=*a;

p=&a[0][0];

p=(int *)a;

p=a;

a[2][1]

a[2][2]

a[2][3]

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

}

slide61

p

a

int a[3][4];

p[0]+1或 *p+1

a[0][0]

*(*p+1)或 (*p)[1]

a[0][1]

a[0][2]

p+1

a+1

a[0][3]

a[1][0]

p[1]+2或 *(p+1)+2

a[1][1]

*(*(p+1)+2)

a[1][2]

p+2

a+2

a[1][3]

a[2][0]

a[2][1]

a[2][2]

a[2][3]

  • 可让p指向二维数组某一行
  • 如 int a[3][4], (*p)[4]=a;

指向一维数组的指针变量

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

例 int (*p)[4];

p的值是一维数组的

首地址,p是行指针

( )不能少

int (*p)[4]与int *p[4]不同

一维数组指针变量维数和

二维数组列数必须相同

slide62

p

p

p

int a[3][4];

a[0][0]

a[0][1]

a[0][2]

a[0][3]

a[1][0]

a[1][1]

a[1][2]

a[1][3]

a[2][0]

p=a[0];

p=*a;

p=&a[0][0];

p=&a[0];

a[2][1]

a[2][2]

a[2][3]

例 一维数组指针变量举例

#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[0][j]

p=a[0];

p=*a;

p=&a[0][0];

p=&a[0];

slide63
字符串与数组关系
  • 字符串用一维字符数组存放;
  • 字符数组具有一维数组的所有特点:
    • 数组名是指向数组首地址的地址常量;
    • 数组元素的引用方法可用指针法和下标法;
  • 区别
    • 存储格式:字符串结束标志;
    • 赋值方式与初始化;
    • 输入输出方式:%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}; ()

slide64

指针定义和引用

地址运算

多级指针

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

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

本讲重点

slide65

q

q

q

p

p

p

3

4

2

1

3

4

5

6

5

6

7

8

例 二维数组与指针运算

#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

2,4,7,5,3

slide66

若int a[3][4];int (*p1)[4]=a;int *p2=a[0];

实参

形参

数组名a

数组名int x[][4]

数组名a

指针变量int (*q)[4]

指针变量p1

数组名int x[][4]

指针变量p1

指针变量int (*q)[4]

指针变量p2

指针变量int *q

二维数组的指针作函数参数

  • 用指向变量的指针变量;
  • 用指向一维数组的指针变量;
  • 用二维数组名。
slide67

二维数组与一维数组指针变量的关系

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

int *pb[2]

pb[0]

pb[1]

int b[2][3]

int b[2][3]

int *pb[2]

1

1

pb[0]

2

2

pb[1]

3

3

2

2

4

4

赋值:

main()

{ int b[2][3],*pb[2];

pb[0]=b[0];

pb[1]=b[1];

……..

}

6

6

初始化:

int main()

{ int b[2][3],*pb[ ]={b[0],b[1]};

……..

}

指针数组

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

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

例 int *p[4];

  • 指针数组赋值与初始化

指针本身的存储类型

指针所指向变量的数据类型

区分int *p[4]与int (*p)[4]

slide69

p[0]

或:

int main()

{ char *p[4];

p[0]= "Fortran";

p[1]= "Lisp";

p[2]= "Basic";

p[3]=NULL;

……..

}

赋值:

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;

……..

}

p[1]

F o r t r a n \0

p[2]

0

p[3]

L i s p \0

B a s i c \0

指针数组赋值与初始化

slide70

p[0]

p[1]

F o r t r a n \0

p[2]

0

p[3]

L i s p \0

B a s i c \0

指针数组赋值与初始化

初始化:

int main()

{ char *p[]={"Fortran", "Lisp", "Basic",NULL};

……..

}

slide71

g a i n \0

name[0]

m u c h \0

name[1]

name[2]

p o i n t \0

name[3]

b y e \0

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

s t r o n g e r \0

二维数组与指针数组区别

char name[5][9]={“gain”,“much”,“stronger”, “point”,“bye”};

char *name[5]={“gain”,“much”,“stronger”, “point”,“bye”};

指针数组元素的作用相当于二维数组的行名

但指针数组中元素是指针变量

二维数组的行名是地址常量

二维数组存储空间固定

字符指针数组相当于可变列长的二维数组

分配内存单元=数组维数*2+各字符串长度

slide72

int b[2][3]

int *pb[2]

b[0][0] *pb[0]

1

pb[0]

b[0][1] *(pb[0]+1)

2

pb[1]

b[0][2] *(pb[0]+2)

3

b[1][0] *pb[1]

2

b[1][1] *(pb[1]+1)

4

b[1][2] *(pb[1]+2)

6

例 用指针数组处理二维数组

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

}

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

slide74

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

}

例7.13 输入若干字符串,按照从小到大的顺序排序并输出排序的结果。

slide75

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;

}

slide76

i(整型变量)

P(指针变量)

&i

3

p1

i(整型变量)

P2(指针变量)

&p2

&i

3

多级指针:指向指针的指针

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

例 int *p;

int i=3;

p=&i;

*p=5;

一级指针

单级间接寻址

  • 二级指针:指针变量中存放一级指针变量的地址

例 int **p1;

int *p2;

int i=3;

p2=&i;

p1=&p2;

**p1=5;

二级指针

一级指针

目标变量

二级间接寻址

slide77

i

3

**p2, *p1

&i

p1

*p2

&p1

p2

  • 定义形式:[存储类型] 数据类型 **指针名;

如 char **p;

*p是p间接指向对象的地址

**p是p间接指向对象的值

最终目标变量的数据类型

指针本身的存储类型

例 int i=3;

int *p1;

int **p2;

p1=&i;

p2=&p1;

**p=5;

例 int i, **p;

p=&i; ()//p是二级指针,不能用变量地址为其赋值

  • 多级指针

例 三级指针 int ***p;

四级指针 char ****p;

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

}

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

}

slide80

cp

c

E N T E R \0

·

·

·

cpp

cpp

cpp

N E W \0

P O I N T \0

F I R S T \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;

}

程序运行结果:

POINTERSTEW

slide81

char *name[5]

hello

name[0]

good

name[1]

name[2]

world

name[3]

bye

name[4]

name

\0

p

p

例 用二级指针处理字符串

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

*(p++)

644 : good bye

slide82

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

int **p与 int *q[10]

  • 指针数组名是二级指针常量;
  • p=q; p+i 是q[i]的地址;
  • 指针数组作形参,int *q[ ]与int **q完全等价;但作为变量定义两者不同;
  • 系统只给p分配能保存一个指针值的内存区;而给q分配10块内存区,每块可保存一个指针值.
slide83

指针的数据类型

含义

定义

int i;

定义整型变量i

p为指向整型数据的指针变量

int *p;

int a[n];

定义含n个元素的整型数组a

n个指向整型数据的指针变量组成的指针数组p

int *p[n];

int (*p)[n];

p为指向含n个元素的一维整型数组的指针变量

int f();

f为返回整型数的函数

int *p();

p为返回指针的函数,该指针指向一个整型数据

int (*p)();

p为指向函数的指针变量,该函数返回整型数

p为指针变量,它指向一个指向整型数据的指针变量

int **p;

slide84

指针数组

指向一维数组的指针

返回指针的函数

指向函数的指针,函数返回int型变量

指向函数的指针,函数返回int 型指针

函数指针数组,函数返回int型变量

函数指针数组,函数返回int型指针

例 下列定义的含义

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

slide85

指针定义和引用

地址运算

多级指针

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

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

本讲重点

slide86

…...

(main)

2000

2002

变量a

2004

2006

变量b

2008

200A

…...

指针变量作为函数参数——指针传递参数地址

特点:共享内存,“双向”传递

例7.18 将数从大到小输出

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

slide87

…...

(main)

2000

2002

指针pointer_1

整型变量a

指针pointer_2

2004

2006

整型变量b

2008

200A

200C

200E

...

2010

例将数从大到小输出

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;

}

使用指针

传递参数地址

9

5

5

9

2000

2002

5,9

9,5

slide88
C有“按引用传递” 吗?

没有

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

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

slide89

例将数从大到小输出

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;

指针变量在使用前

必须赋值!

编译警告!

结果不对!

slide90

a

a

5

9

返回:

调前:

b

b

p1

p1

5

9

&a

&a

a

a

调swap:

交换:

9

5

p2

p2

&b

&b

b

b

5

9

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

}

slide91

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

}

0

12

43

i

i

i

i

i

i

1

23

23

int large(int x,int y)

{ int flag;

if(x>y) flag=1;

else if(x<y) flag=-1;

else flag=0;

return(flag);

}

2

56

21

0

3

98

10

1

76

66

4

2

88

54

5

3

4

n=0

m=0

k=1

n=0

m=1

k=1

n=1

m=1

k=1

n=1

m=1

k=2

n=2

m=1

k=2

n=3

m=1

k=2

5

例 两个数组大小比较

数组作为函数参数

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

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

n=0

m=0

k=0

slide92

实参

形参

数组名

数组名

数组名

指针变量

指针变量

数组名

指针变量

指针变量

数组名作函数参数

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

0

12

1

23

2

56

.

….

….

.

88

9

stu

score

一维数组名作函数参数

例 求学生的平均成绩

形参用数组定义, 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;

}

实参用数组名

slide94

a

a

a

1

1

1

2

2

2

调用前

调用

x

x

1

a[0]

a[0]

2

a[1]

a[1]

2

1

y

y

返回

交换

例7.21 数组元素与数组名作函数参数比较

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

}

值传递

slide95

a

a

a

a

1

1

2

2

2

2

1

1

调用前

x

x

返回

调用

交换

例7.21 数组元素与数组名作函数参数比较

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

}

地址传递

slide96

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;

}

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;

}

}

}

0

1

2

3

4

5

6

7

8

9

a

k

k

k

49

array

j

j

j

j

j

j

j

j

j

68

57

32

9

99

27

13

76

88

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

9

i=0

49

slide97

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

}

slide98

0

1

9

2

3

4

5

49

6

7

8

9

a

k

k

k

k

k

49

array

j

j

j

j

j

j

j

j

68

57

32

9

99

27

13

76

88

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

13

i=1

68

slide99

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

}

slide100

0

1

2

3

4

5

6

7

8

9

a

9

array

13

27

32

49

57

68

76

88

99

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

i=8

slide101

b

b

to

y

I

a

a

o

from

I

u

a

m

a

a

m

r

a

e

a

t

a

t

e

e

a

s

a

c

t

c

h

u

h

e

d

e

r

r

e

.

n

.

\0

t

t

\0

.

.

\0

\0

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)用字符指针变量作参数

slide102

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

/*版本3*/

int strlen(char *s)

{ int n=0;

while(*s++)

n++;

return n;

}

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

}

/*版本4*/

int strlen(char *s)

{ char *p=s;

while(*p++);

return p-s;

}

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

slide104

若int a[3][4];int (*p1)[4]=a;int *p2=a[0];

实参

形参

数组名a

数组名int x[][4]

数组名a

指针变量int (*q)[4]

指针变量p1

数组名int x[][4]

指针变量p1

指针变量int (*q)[4]

指针变量p2

指针变量int *q

二维数组的指针作函数参数

  • 用指向变量的指针变量
  • 用指向一维数组的指针变量
  • 用二维数组名
  • 二维数组形参实际上是一维数组指针变量, 即 int x[ ][10]  int (*x)[10]
slide105

j

j

j

j

1 3 5 7

1 3 5 7

1 3 5 7

1 3 5 7

1 3 5 7

1 3 5 7

i

i

i

i

i

i

2 4 6 8

2 4 6 8

2 4 6 8

2 4 6 8

2 4 6 8

2 4 6 8

15 17 34 12

15 17 34 12

15 17 34 12

15 17 34 12

15 17 34 12

15 17 34 12

max=5

max=3

max=1

j

j

max=7

max=7

max=34

二维数组名作函数参数

例7.26 求二维数组中最大元素值

slide106

多维形参数组第一维维数

可省略,第二维必须相同

 int array[][4]

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

}

slide107

result

a

sum_row

x

3

6

9

1

4

7

例 求二维数组中各行元素之和

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;

}

18

12

slide108

79

60

52

65

80

87

90

81

90

99

100

98

p

p

例7.27 3个学生各学4门课,计算总平均分,并输出第n个学生成绩

函数说明

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;

}

列指针

行指针

float p[][4]

 p[n][i]

slide109

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

}

}

}

p

79

60

52

65

80

87

90

81

90

99

100

98

int main()

{ void search(float (*p)[4], int n);

float score[3][4]={{...},{...},{...}};

search(score,3);return 0;

}

例7.28 3个学生各学4门课,计算总平均分,并查找一门以上课 不及格学生, 输出其各门课成绩

 p[j][i]

slide110

(main)

2000

1

2002

变量a

2

2004

2000

2006

指针变量q

变量b

指针变量p

二级指针r

二级指针s

指针变量t

2002

2008

COPY

(swap)

200A

2004

2006

例 二级指针做函数参数

#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

2000

slide111

(main)

2000

1

2002

变量a

2

2004

2000

2006

指针变量p

指针变量q

变量b

2002

2008

200A

例二级指针做函数参数

#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

slide112

p

p

a

b

q

q

b

a

p

p

r

a

r

a

s

b

s

b

q

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;

}

2,1

slide113

#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编写函数,从一个已经创建好链表中删除指定结点,该链表由整数构成

slide114

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

形参

函数返回值

slide115

max

指令1

指令2

…...

指向函数的指针变量

  • 函数指针:函数在编译时被分配的入口地址,用函数名表示
  • 指向函数的指针变量
    • 定义形式: 数据类型 (*指针变量名)();

如 int (*p)();

( )不能省

int (*p)() 与 int *p()不同

  • 函数指针变量赋值:如p=max;

专门存放函数入口地址

可指向返回值类型相同的不同函数

函数返回值的数据类型

  • 函数调用形式: c=max(a,b);  c=(*p)(a,b);  c=p (a,b);
  • 对函数指针变量pn, p++, p--无意义

函数指针变量指向的函数必须有函数说明

slide116

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

}

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

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

slide118

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

}

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

slide119

系统自动调用

main函数时传递

命令行实参

main(形参)

命令行参数

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

C:\TC> copy[.exe] source.c temp.c

带参数的main函数形式:

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

main(int argc, char *argv[])

{ ………

}

  • 命令行参数传递

元素指向命令行参数

中各字符串首地址

命令行中参数个数

形参名任意

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

slide120

char *argv[]

argv

test

argv[0]

argc=3

hello

argv[1]

argv[2]

world

例7.32 输出命令行参数

/*test.c*/

main(int argc, char *argv[])

{ while(argc>1)

{ ++argv;

printf("%s\n",*argv);

--argc;

}

}

1. 编译、链接test.c,生成可执行文件test.exe

2. 在DOS状态下运行(test.exe所在路径下)

例如: C:\TC> test[.exe] hello world!

hello

world!

slide121

char *argv[]

argv

test

argv[0]

argc=3

hello

argv[1]

argv[2]

world

例7.32 输出命令行参数

int main(int argc, char *argv[])

{ while(argc-->0)

printf("%s\n",*argv++);

}

test

hello

world!

slide122

例7.33,编程模拟实现UNIX中的grep命令的基本功能例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*/  

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

}

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

}

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

find -x –n pattern

或者为

find –xn pattern

X: 表示形式与pattern不匹配的行

N: 要求形式行号

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

}

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

}