首页计算机计算机软件C语言程序设计 08 第八章 指针.pptx

08 第八章 指针.pptx

页数:80页文件大小:1.22 MB来源: C语言程序设计ppt课件

本课件系统讲解C语言的指针,从变量与地址、指针与指针变量的概念入手,说明取地址与间接访问运算,随后展开指针变量的定义初始化与运算、指针与一维数组及二维数组的对应关系、指针与字符串,再介绍返回指针值的函数、指向函数的指针,最后讲解指针数组与多级指针的用法。

第八章指针导览页,列出指针概念指针与数组字符串及函数等主要知识点
指针作用页,说明指针在简洁高效数据结构字符串数组与动态内存上的优势
变量与地址页,说明内存按字节编号及编译或调用时分配内存单元
查看本课件的文字版(共 80 页,供检索与复制)

第1页:第八章指针导览

第八章指针章节封面,标示本章主线:指针的概念、指针变量、指针与数组、指针与字符串、返回指针值的函数、指针与函数、指针数组和多级指针。

第2页:指针的作用

指针是C语言的重要组成部分,精华所在。使用指针可以:

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

第3页:变量与地址

变量与地址:内存中每个字节有一个编号,即地址。定义 int i;float k; 后,编译或函数调用时为其分配内存单元(如 i 占 2000~2001,k 占 2002~2005)。

变量是对程序中数据存储空间的抽象,变量的地址由系统分配,程序只可使用不能修改。

第4页:指针与指针变量

指针与指针变量

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

整型变量 i 的值为 10、地址为 2000;指针变量 i_pointer 的内容为 2000,即指向 i 的地址。变量地址存入指针变量即构成指向关系。

第5页:取地址与取内容运算符

& 与 * 运算符

  • &:取变量的地址,单目运算符,优先级 2,自右向左结合
  • *:取指针变量的目标变量的内容,单目运算符,优先级 2,自右向左结合
  • 两者互为逆运算
i_pointer ----- 指针变量,它的内容是地址量 *i_pointer ---- 指针的目标变量,它的内容是数据 &i_pointer --- 指针变量占用内存的地址 i = *i_pointer = *(&i) i_pointer = &i = &(*i_pointer)

第6页:直接访问与间接访问

直接访问与间接访问

  • 直接访问:按变量地址存取变量值,如 i = 3;
  • 间接访问:通过存放变量地址的变量访问变量,如 *i_pointer = 20;

两种方式作用于同一内存单元,i 与 *i_pointer 等价。

第7页:两种访问方式示例

整型变量 i 的值为 10,指针变量 i_pointer 指向 i:

k = i; k = *i_pointer; 

第8页:指针变量的定义

一般形式:[存储类型] 数据类型 *指针名;

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

注意:

  • int *p1, *p2; 与 int *p1, p2; 不同,后者 p2 是整型变量
  • 指针变量名是 p1、p2,不是 *p1、*p2
  • 指针变量只能指向定义时所规定类型的变量
  • 指针变量定义后变量值不确定,应用前必须先赋值
  • 此处的 * 表示定义指针变量,不是 * 运算符

第9页:指针变量的初始化

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

int i; int *p = &i; int *q = p; 

赋给指针变量的是地址值,不是赋给目标变量。

第10页:指针变量必须先赋值再使用

void main() { int i = 10; int *p; *p = i; printf("%d", *p); } void main() { int i = 10, k; int *p; p = &k; *p = i; printf("%d", *p); }

指针变量未赋值时其值是随机数,向随机地址写入会破坏内存,必须先赋值再使用。

第11页:零指针与空指针

零指针(空指针):指针变量值为零。

int *p = 0; #define NULL 0 int *p = NULL; while (p != NULL) { ... } 

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

第12页:指针概念示例

void main() { int a; int *pa = &a; a = 10; printf(" a: %d\n", a); printf("*pa: %d\n", *pa); printf(" &a: %x\n", &a); printf(" pa: %x\n", pa); printf("&pa:%x\n", &pa); }

输出:a: 10 / *pa: 10 / &a: f86 / pa: f86 / &pa: f88。指针变量自身也占内存单元。

第13页:用指针实现两数从大到小输出

void main() { int *pa, *pb, *p, a, b; scanf("%d %d", &a, &b); pa = &a; pb = &b; if (a < b) { p = pa; pa = pb; pb = p; } printf("a=%d,b=%d\n", a, b); printf("max=%d,min=%d\n", *pa, *pb); }

运行结果:a=5,b=9 / max=9,min=5。只交换指针的指向,变量 a、b 本身的值不变。

第14页:指针作函数参数之值传递

void swap(int x, int y) { int temp; temp = x; x = y; y = temp; } void main() { int a, b; scanf("%d %d", &a, &b); if (a < b) swap(a, b); printf("\n%d,%d\n", a, b); }

形参 x、y 只是实参 a、b 的副本(COPY),交换发生在副本上。

第15页:值传递的运行结果

输入 5 9,调用 swap(a, b) 后 main 中变量 a=5、b=9 保持不变,输出 5, 9,说明值传递无法实现两数交换。

第16页:用指针实参再次值传递

void swap(int x, int y) { int temp; temp = x; x = y; y = temp; } void main() { int a, b; int *pa, *pb; scanf("%d%d", &a, &b); pa = &a; pb = &b; if (a < b) swap(*pa, *pb); printf("\n%d,%d\n", a, b); }

实参写 *pa、*pb 时传的是变量值,仍为值传递。

第17页:指针实参值传递的结果

swap 中 x、y 得到 a、b 的副本,交换后 main 中 a=5、b=9 不变,运行结果仍为 5, 9。要真正交换须传地址。

第18页:指针作参数的地址传递

void swap(int *p1, int *p2) { int temp; temp = *p1; *p1 = *p2; *p2 = temp; } void main() { int a, b; int *pa, *pb; scanf("%d%d", &a, &b); pa = &a; pb = &b; if (a < b) swap(pa, pb); printf("\n%d,%d\n", a, b); }

形参 p1、p2 得到的是 pa、pb 的地址值(COPY),但它们指向 main 中的 a、b,通过 *p1、*p2 间接访问即可交换。

第19页:地址传递的运行结果

调用 swap(pa, pb) 后,main 中 a=9、b=5,输出 9,5。地址传递实现共享内存与双向传递。

第20页:指针未赋初值的错误用法

void swap(int *p1, int *p2) { int *p; *p = *p1; *p1 = *p2; *p2 = *p; }

正确写法应为 int temp; int *p = &temp;。指针变量在使用前必须赋值,否则向随机地址写入可能破坏系统。

第21页:交换指针形参的无效写法

void swap(int *p1, int *p2) { int *p; p = p1; p1 = p2; p2 = p; }

形参 p1、p2 互换后随函数调用结束释放,实参 pa、pb 的指向不变。

第22页:交换指针形参的运行结果

运行结果:5,9。本质上 C 语言参数传递只有一种方式——值传递,地址传递传的是地址值,通过间接访问才能改变实参所指内容。

第23页:指向数组元素的指针变量

int array[10]; int *p; p = &array[0]; int array[10]; int *p = &array[0]; int *p = array;

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

第24页:指针的赋值运算

指针变量的赋值运算

  • p = &a; 将变量 a 的地址赋给 p
  • p = array; 将数组首地址赋给 p
  • p = &array[i]; 将数组元素地址赋给 p
  • p = NULL; 空指针
  • p1 = p2; 指针间赋值
  • 不能把一个整数赋给 p,也不能把 p 的值赋给整型变量:p = 1000;(非法)、i = p;(非法)
  • 不能取表达式地址 p = &(i+5);,不能取 register 变量地址 p = &k;,不能改变变量地址 &i = p;

第25页:指针的算术运算

指针的算术运算

  • p ± i 等价于 p ± i×d(i 为整型数,d 为 p 指向变量所占字节数)
  • p++、p--、p += i、p -= i 等
  • 若 p1、p2 指向同一数组,p1 - p2 为两指针间元素个数,即 (p1-p2)/d
  • p1 + p2 无意义
int a[10]; int *p = &a[2]; p++; *p = 1; int *p1 = &a[2], *p2 = &a[5]; p2 - p1 = 3; 

p 指向 float 数时 p+1 相当于地址加 4(放大因子)。

第26页:指针的关系运算

指针变量的关系运算

  • 若 p1、p2 指向同一数组:p1 < p2 表示 p1 指的元素在前;p1 > p2 表示在后;p1 == p2 表示指向同一元素
  • 若 p1 与 p2 不指向同一数组,比较无意义
  • p == NULL 或 p != NULL 判断指针是否为空

第27页:数组元素的表示方法

等价关系

数组元素:a[i]  p[i]  *(p+i)  *(a+i) 元素地址:&a[i]  &p[i]  p+i  a+i
数组元素与地址表示方法对照
表示法元素地址元素
下标法&a[0] &a[1] &a[2] &a[9]a[0] a[1] a[2] a[9]
指针法p p+1 p+2 p+9*p *(p+1) *(p+2) *(p+9)
数组名法a a+1 a+2 a+9*a *(a+1) *(a+2) *(a+9)

应用场合:顺序访问用指针法速度快,随机访问用下标法直观。[ ] 是变址运算符,a[i] 即 *(a+i),优先级 1,自左向右结合。

第28页:数组元素地址选择题

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] int a[] = {8,1,2,5,0,4,7,6,3,9}; a[*(a+a[3])] 的值等于: (A)8 (B)0 (C)3 (D)9

数组名是地址常量,a++、a-- 非法,p++、a+1、*(a+2) 合法。

注:a[*(a+a[3])] 中 a[3]=5,*(a+5)=a[5]=4,故结果为 a[4]=0,对应选项 B。

第29页:数组元素的引用方法

void main() { int a[5], *pa, i; pa = a; for (i = 0; i < 5; i++) scanf("%d", &a[i]); for (i = 0; i < 5; i++) printf("%d: %d\n", i, a[i]); }

也可用 *(a+i)、pa[i]、*(pa+i)、&pa[i] 等形式访问同一元素。

第30页:指针引用数组元素的注意事项

void main() { int a[5], *pa = a, i; for (i = 0; i < 5; i++) scanf("%d", pa++); pa = a; for (i = 0; i < 5; i++, pa++) printf("%d: %d\n", i, *pa); }

指针变量可以指到数组之后的内存单元,使用前应确保其指向有效元素。

第31页:指针运算综合示例

void main() { int a[] = {1, 2, 3, 4, 5}; int *p; p = a; printf("%d\n", (*p)++); printf("%d\n", *++p); printf("%d\n", ++*p); printf("%d\n", *p--); p += 3; printf("%d\n%d\n", *p, *(a+3)); }

数组 a 经运算后为 2、3、3、4、5,输出依次为 1、2、3、3 与 4、4。

第32页:一维数组名作函数参数

数组名作函数参数是地址传递,实参与形参的对应关系:

实参形参与对应的形参形式
实参形参
数组名数组 int x[ ]、指针变量 int *x
指针变量数组 int x[ ]、指针变量 int *x

第33页:求一维数组元素之和

int sum(int x[], int n) { int i, sum = 0; for (i = 0; i < n; i++) sum += x[i]; return(sum); } void main() { int a[10] = {3,7,9,11,0,6,7,5,4,2}, *p; p = a; printf("The sum is:%d\n", sum(a, 10)); }

形参 int x[ ] 等价于 int *x;调用可写 sum(a,10)、sum(p,10)、sum(&a[0],10);循环体可写 sum += *x++;、sum += *(x+i); 或 {sum += x[0]; x++;}。

第34页:一级指针与一维数组的关系

对 int *p 与 int a[10]:

  • 数组名是指针(地址)常量,p=a 后 p+i 即 a[i] 的地址
  • 若 p=a,则 p[i]  a[i]  *(p+i)  *(a+i)
  • 形参数组实质上是指针变量,int a[ ]  int *a
  • 定义指针变量(非形参)时不能把 int *p 写成 int p[ ]
  • 系统只给 p 分配能保存一个指针值的内存区(一般 2/4 字节),而给 a 分配 sizeof(int)*10 字节

第35页:二维数组的地址

对于一维数组 int array[10];:

  • 数组名 array 表示数组首地址,即 array[0] 的地址
  • 数组名 array 是地址常量
  • array+i 是元素 array[i] 的地址
  • array[i]  *(array+i)

第36页:二维数组的行指针与元素指针

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))[j]

a+i = a[i] = *(a+i) = &a[i][0],值相等但含义不同:a+i 指向行,a[i] 指向列。数组 a 由 a[0]、a[1]、a[2] 三个一维数组构成。

第37页:二维数组元素地址等价式

对 int a[3][4]:a+i 表示第 i 行首地址,指向行;a[i] 即 *(a+i),等于 &a[i][0],表示第 i 行第 0 列元素地址,指向列。二者数值相同,类型不同。

第38页:指向二维数组元素的指针变量

void main() { 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); } }

p = a[0];、p = *a;、p = &a[0][0];、p = *(a+0); 均正确,p = a; 类型不符。

第39页:指向一维数组的指针变量

定义形式:数据类型 (*指针名)[一维数组维数];,如 int (*p)[4];,括号不能少。

  • int (*p)[4] 与 int *p[4] 不同,前者是行指针,后者是指针数组
  • p 的值是一维数组的首地址,可指向二维数组某一行
  • 一维数组指针变量的维数与二维数组列数必须相同
  • p ± i 偏移 i × sizeof(数据类型) × 数组列维数

对 int a[3][4] 与 p:p[i][j]  *(p[i]+j)  *(*(p+i)+j)  (*(p+i))[j]。

第40页:一维数组指针变量举例

void main() { 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("%4d ", *(*p+j)); printf("\n"); } }

*(*p+j) 等价于 p[0][j];此处 p = a[0];、p = *a;、p = &a[0][0]; 均不正确,应写 p = a;。

第41页:二维数组与指针运算

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

输出:2,4,7,5,3。i=0 时 a[0][0] 被改为 2,随后 p 与 q 逐次后移。

第42页:二维数组的指针作函数参数

对 int a[3][4]; int (*p1)[4] = a; int *p2 = a[0];,实参形参对应关系:

二维数组指针作函数参数的对应关系
实参形参
数组名 a数组名 int x[][4]、指针变量 int (*q)[4]
指针变量 p1数组名 int x[][4]、指针变量 int (*q)[4]
指针变量 p2指针变量 int *q

第43页:求二维数组各行元素之和

void get_sum_row(int x[2][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]; } } void 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]); }

形参 int x[2][3] 可写为 int x[][3] 或 int (*x)[3],元素用 *(*(x+i)+j) 访问;结果 18、12。

第44页:二维数组与行指针的关系

对 int a[5][10] 与 int (*p)[10]:

  • 二维数组名 a 是指向有 10 个元素的一维数组的指针常量
  • p = a+i 使 p 指向第 i 行,*(*(p+i)+j)  a[i][j]
  • 二维数组形参实际上是一维数组指针变量:int x[][10]  int (*x)[10]
  • 变量定义(非形参)时两者不等价;系统只给 p 分配一个指针值的内存区,给 a 分配 sizeof(int)*5*10 字节

第45页:用字符数组表示字符串

void main() { char string[] = "I love China!"; printf("%s\n", string); printf("%s\n", string+7); }

输出:I love China! / China!。数组名 string 是地址常量,string += 7; 与 string = "I Love China!"; 均非法。

第46页:用字符指针表示字符串

void main() { char *string = "I love China!"; printf("%s\n", string); string += 7; while (*string) { putchar(string[0]); string++; } }

字符指针初始化即把字符串首地址赋给 string,等价于 char *string; string = "I love China!";;*string != 0 判断字符串是否结束。

第47页:字符串指针作函数参数

void copy_string(char from[], char *to) { while ((*to = *from) != '\0') { from++; to++; } } void main() { char *a = "I am a teacher.", b[20]; copy_string(a, b); printf("string_a=%s\nstring_b=%s\n", a, b); }

循环体可简写为 *to++ = *from++;,遇字符串结束标志 \0 停止复制。

第48页:字符指针变量与字符数组

char *cp; 与 char str[20];

  • str 由若干元素组成,每个元素放一个字符;cp 中存放字符串首地址
  • str = "I love China!";(非法);cp = "I love China!";(合法)
  • str 是地址常量,cp 是地址变量
  • cp 接受键入字符串前必须先开辟存储空间:char *cp; scanf("%s", cp);(非法),应改为 char *cp, str[10]; cp = str; scanf("%s", cp);

第49页:字符串与数组的关系

字符串用一维字符数组存放,字符数组具有一维数组的所有特点:数组名是指向首地址的地址常量、可用指针法与下标法引用、数组名作函数参数是地址传递。

区别:存储格式有字符串结束标志;赋值与初始化方式不同;输入输出用 %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}; str = "Hello"; cp = "Hello!"; scanf("%s", str); printf("%s", str); gets(str); puts(str);

第50页:返回指针值的函数定义

函数定义形式:

数据类型 *函数名(形参表) { 函数体; return(指针); }

例:int *f(int x, int y)。使用场合:被调函数需要返回多于一个的值。

第51页:返回指针值的函数示例

#include <stdio.h> char *strin(char c); void main() { char *ch, c; c = getchar(); ch = strin(c); printf("%s\n", ch); } char *strin(char c) { char str[5]; int i; for (i = 0; i < 4; i++) str[i] = c + i; str[i] = '\0'; return(str); }

输入 A 期望输出 ABCD,但 str 是存放在动态存储区(栈)的局部动态变量,函数返回后空间释放,返回的指针无效;应将 str 声明为 static char str[5]; 存放在静态存储区。不能返回形参或局部动态变量的地址作函数返回值。

第52页:指向函数的指针变量

函数指针:函数在编译时被分配的入口地址,用函数名表示。

定义形式:数据类型 (*指针变量名)( );,如 int (*p)();,括号不能省略,int (*p)() 与 int *p() 不同。

  • 赋值:p = max;(max 须有函数原型声明)
  • 调用:c = max(a,b); 等价于 c = (*p)(a,b);
  • 对函数指针变量 p±n、p++、p-- 无意义
  • 可指向返回值类型相同的不同函数

第53页:指针相关定义的含义辨析

下列定义的含义

  • int *p; —— 整型指针变量
  • int (*p)[3]; —— 指向一维数组的指针
  • int *p(int); —— 返回指针的函数
  • int (*p)(int); —— 指向函数的指针,函数返回 int 型变量
  • int *(*p)(int); —— 指向函数的指针,函数返回 int 型指针

第54页:指针数组的定义

指针数组:数组中的元素为指针变量,用于处理二维数组或多个字符串。

定义形式:[存储类型] 数据类型 *数组名[数组长度说明];,如 int *p[4];,注意与 int (*p)[4] 区分。

main() { int b[2][3], *pb[2]; pb[0] = b[0]; pb[1] = b[1]; pb[0] = &b[0][0]; pb[1] = &b[1][0]; pb[0] = *b; pb[1] = *(b+1); }

第55页:指针数组的初始化

void main() { int b[2][3], *pb[] = {b[0], b[1]}; ... }

初始化时把各行首地址依次赋给 pb[0]、pb[1],之后可用 pb[i][j] 访问元素。

第56页:字符指针数组的赋值与初始化

void main() { char a[] = "Fortran", b[] = "Lisp", c[] = "Basic"; char *p[4]; p[0] = a; p[1] = b; p[2] = c; p[3] = NULL; } p[0] = "Fortran"; p[1] = "Lisp"; p[2] = "Basic"; p[3] = NULL; char *p[] = {"Fortran", "Lisp", "Basic", NULL};

第57页:二维数组与指针数组的区别

char name[5][9] = {"gain", "much", "stronger", "point", "bye"}; char *name[5] = {"gain", "much", "stronger", "point", "bye"};
  • 二维数组存储空间固定;字符指针数组相当于可变列长的二维数组
  • 指针数组分配内存 = 数组维数 × 2(或 4)+ 各字符串长度
  • 指针数组元素的作用相当于二维数组的行名,但元素是指针变量,而二维数组的行名是地址常量

第58页:用指针数组处理二维数组

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

也可写为 printf("b[%d][%d]:%2d\n", i, j, *(pb[i]+j));,更直观且不会移动指针。

第59页:字符串排序示意一

例:对字符串排序(简单选择排序)

char *name[] = {"Follow me", "BASIC", "Great wall", "FORTRAN", "Computer"};

i=0 趟:从 name[0] 起依次与后面各串比较,记录最小串下标 k,本趟选出 BASIC 为最小。

第60页:字符串排序示意二

i=1 趟:在剩余字符串中继续比较,k 随比较结果更新,选出次小串并与 name[1] 交换指针。排序过程中只交换指针数组中记录的指向,不移动字符串本身,效率高于逐个字符复制。

第61页:字符串排序示意三

i=2 趟:在剩余字符串中继续比较,k 随比较结果更新,选出第三小串与 name[2] 交换。选择排序每趟确定一个位置。

第62页:字符串排序示意四

i=3 趟:比较剩余两个串,确定 name[3],排序结束。五个字符串共需 n-1 趟比较。

第63页:二维数组法字符串排序程序

#include <stdio.h> #include <string.h> void sort(char (*name)[20], int n); void print(char (*name)[20], int n); void main() { char name[][20] = {"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer design"}; int n = 5; sort(name, n); print(name, n); } void sort(char (*name)[20], int n) { char temp[20]; int i, j, k; for (i = 0; i < n-1; i++) { k = i; for (j = i+1; j < n; j++) if (strcmp(name[k], name[j]) > 0) k = j; if (k != i) { strcpy(temp, name[i]); strcpy(name[i], name[k]); strcpy(name[k], temp); } } } void print(char (*name)[20], int n) { int i; for (i = 0; i < n; i++) printf("%s\n", name[i]); }

形参写成数组形式即 char name[][20],排序时逐串复制字符,移动数据量大。

第64页:交换地址法排序示意一

用指针数组 char *name[5] 指向 Follow me、BASIC、Great Wall、FORTRAN、Computer,i=0 趟中 k 指向当前最小串,趟末交换 name[0] 与 name[k] 的指向。

第65页:交换地址法排序示意二

i=1 趟:k 初始为 1,与后续各串比较后更新,趟末交换指针,使 name[1] 指向剩余串中最小者。

第66页:交换地址法排序示意三

i=2 趟:继续在剩余串中选出最小者并交换指针,字符串本身不发生移动。

第67页:交换地址法排序示意四

i=3 趟:比较最后两个串并完成指针交换,排序结束。整个排序只交换指针值,开销远小于整串复制。

第68页:交换地址法字符串排序程序

void main() { char *name[] = {"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer "}; int n = 5; sort(name, n); print(name, n); } void sort(char *name[], int n) { char *temp; int i, j, k; for (i = 0; i < n-1; i++) { k = i; for (j = i+1; j < n; j++) if (strcmp(name[k], name[j]) > 0) k = j; if (k != i) { temp = name[i]; name[i] = name[k]; name[k] = temp; } } } void print(char *name[], int n) { int i; for (i = 0; i < n; i++) printf("%s\n", name[i]); }

形参 char *name[ ] 为指针数组,交换的只是各元素中保存的地址。

第69页:指向指针的指针

一级指针:指针变量中存放目标变量的地址,为单级间接寻址。

int *p; int i = 3; p = &i; *p = 5;

二级指针:指针变量中存放一级指针变量的地址,为二级间接寻址。

int **p2; int *p1; int i = 3; p1 = &i; p2 = &p1; **p2 = 5;

第70页:二级指针的定义形式

定义形式:[存储类型] 数据类型 **指针名;,如 char **p;

  • *p 是 p 间接指向对象的地址,**p 是 p 间接指向对象的值
  • int i, **p; p = &i; 错误,二级指针不能用普通变量的地址赋值
  • 多级指针:三级 int ***p;,四级 char ****p;
int i = 3; int *p1; int **p2; p1 = &i; p2 = &p1; **p2 = 5;

第71页:一级指针作形参交换失败

#include <stdio.h> void swap(int *r, int *s) { int *t; t = r; r = s; s = t; } void main() { int a = 1, b = 2, *p, *q; p = &a; q = &b; swap(p, q); printf("%d,%d\n", *p, *q); }

形参 r、s 是指针 p、q 的副本,交换副本后 main 中 p 仍指向 a、q 仍指向 b。

第72页:一级指针形参的输出结果

运行结果:1,2。要在被调函数中改变 main 里指针变量的指向,必须传指针变量的地址,即使用二级指针形参。

第73页:二级指针作形参的写法

#include <stdio.h> void swap(int **r, int **s) { int *t; t = *r; *r = *s; *s = t; } void main() { int a = 1, b = 2, *p, *q; p = &a; q = &b; swap(&p, &q); printf("%d,%d\n", *p, *q); }

实参为 &p、&q,形参 r、s 得到指针变量的地址,通过 *r、*s 修改其指向。

第74页:二级指针形参的输出结果

运行结果:2,1。p 改为指向 b、q 改为指向 a,说明二级指针形参可改变实参指针的指向。

第75页:二级指针与指针数组

char *name[5] = {"hello", "good", "world", "bye", ""}; char **p = name; 

name[0]~name[4] 分别指向 hello、good、world、bye 与空串,p 指向数组首元素。

第76页:用二级指针处理字符串

#define NULL 0 void main() { char **p; char *name[] = {"hello", "good", "world", "bye", ""}; p = name; p++; printf("%o : %s ", *p, *p); p += 2; while (**p != '\0') printf("%s\n", *p++); }

输出:644 : good bye。用 *p 可输出地址(%d、%o、%x)或字符串(%s),*p++ 即 *(p++)。

第77页:二级指针与指针数组的关系

对 int **p 与 int *q[10]:

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

第78页:本章小结

本章小结

  • 指针的概念
  • 指针变量的定义与调用:先赋值,后使用
  • 指针传递参数的方法及特点
  • 指针变量的类型辨析:int *p;int (*p)[4];int (*p)();int *p();int *p[4];int **p;
  • 指针运算:赋值、算术、关系
  • 用指针处理数组和字符串的方法

第79页:动态内存申请函数malloc

申请内存函数 malloc

  • 格式:void *malloc(unsigned size)
  • 功能:申请长度为 size 字节的内存区
  • 返值:申请成功返回所分配内存区首字节的地址,失败返回 NULL
  • 使用时需强制类型转换,包含在头文件 stdlib.h 中
int *p; p = (int *)malloc(10 * sizeof(int)); 

第80页:动态内存释放函数free

释放内存函数 free

  • 格式:void free(void *p)
  • 功能:释放 p 所指的内存空间,无返回值
  • p 所指内存必须是用 malloc 申请的,否则调用可能破坏系统
#include <stdlib.h> void try(int a[], int m) { int k; for (k = 0; k < m; k++) a[k] = k*10; for (k = 0; k < m; k++) printf("%d,", a[k]); } void main() { int *p; p = (int *)malloc(10 * sizeof(int)); printf("\nResult:"); try(p, 10); free(p); }
顶部目录