news 2026/8/21 1:57:18

《C程序设计(第五版)》第8章「善于利用指针」知识要点、案例分析与课后习题解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《C程序设计(第五版)》第8章「善于利用指针」知识要点、案例分析与课后习题解析

1. 本章导读

指针是 C 语言最核心、也最容易让初学者感到困惑的知识点之一。谭浩强老师在《C程序设计(第五版)》中用了整整一章来讲解指针,足见其重要性。本章从「指针是什么」讲起,逐步深入到指针与数组、指针与字符串、函数指针、动态内存分配等内容,最终目标是让读者能够灵活运用指针处理一批数据、优化函数传参、构建动态数据结构

本文将按照教材第 8 章的章节脉络,梳理知识要点,配合典型案例分析,并对课后习题给出解析与参考代码,帮助读者系统掌握指针。

2. 知识要点梳理

2.1 指针与指针变量

  • 地址:内存中每个字节都有一个编号,这个编号就是地址。变量在内存中占用若干字节,其「首地址」就是该变量的地址。
  • 指针:在 C 语言中,地址被形象地称为「指针」,即通过地址可以「指向」某个存储单元。
  • 指针变量:专门用来存放地址(指针)的变量。定义指针变量时必须指定它指向的数据类型,例如int *p;表示p是一个指向整型数据的指针变量。
  • 指针变量的定义与初始化:
inta=10;int*p=&a;/* p 保存 a 的地址 */

两个关键运算符

运算符含义示例结果
&取地址&a得到变量 a 的地址
*间接访问(解引用)*p得到 p 所指向的变量

2.2 指针变量作函数参数

C 语言函数调用采用「值传递」——形参只是实参的一份拷贝。如果想在函数内部修改主调函数中的变量,就必须把变量的地址传进去。这是指针最典型的应用之一。

voidswap(int*x,int*y){inttmp=*x;*x=*y;*y=tmp;}intmain(void){inta=3,b=5;swap(&a,&b);/* 调用后 a=5, b=3 */return0;}

思考:如果swap的参数写成int x, int y,调用后a、b会交换吗?答案是不会,因为交换的只是形参副本。

2.3 通过指针引用数组

数组名代表数组首元素的地址,因此可以用指针遍历数组,提高程序执行效率。

inta[5]={1,2,3,4,5};int*p=a;/* 等价于 int *p = &a[0]; */
  • p = a时:p + i指向a[i]*(p + i)等价于a[i]
  • 指针变量可以自增:p++使p指向下一个元素;但数组名 a 是常量指针,不能自增
  • 结合数组作函数参数:形参int a[]在函数内部实际上等价于int *a
voidprint_arr(int*p,intn){for(inti=0;i<n;i++)printf("%d ",*(p+i));}

2.4 通过指针引用字符串

C 语言没有专门的字符串类型,字符串本质上是以'\0'结尾的字符数组。可以用字符指针指向字符串常量:

char*str="Hello, C!";printf("%s\n",str);

两种方式的区别

charstr1[]="hello";/* 字符数组,内容可以修改 */char*str2="hello";/* 指针指向字符串常量,内容通常不可修改 */

用字符指针处理字符串更灵活,例如字符串复制、比较、连接等操作:

voidmy_strcpy(char*dest,constchar*src){while((*dest++=*src++)!='\0');}

2.5 指向函数的指针

函数在内存中也占存储空间,函数的入口地址同样可以用指针保存。函数指针常用于回调函数菜单驱动程序中。

intadd(inta,intb){returna+b;}intsub(inta,intb){returna-b;}intmain(void){int(*func)(int,int);/* 定义函数指针 */func=add;printf("%d\n",func(10,20));/* 输出 30 */func=sub;printf("%d\n",func(10,20));/* 输出 -10 */return0;}

2.6 返回指针值的函数

函数可以返回地址(指针),典型场景是返回数组中某个元素或某段内存的地址。注意:绝不能返回局部变量的地址,因为函数结束后局部变量已被释放。

int*max_elem(int*arr,intn){int*max=arr;for(inti=1;i<n;i++)if(arr[i]>*max)max=&arr[i];returnmax;}

2.7 指针数组与多重指针

  • 指针数组:数组的每个元素都是指针。常用于保存多个字符串:
char*names[]={"Alice","Bob","Carol"};printf("%s\n",names[1]);/* 输出 Bob */
  • 指向指针的指针(多重指针):指针变量本身也有地址,可以用另一个指针指向它。
char*name="hello";char**pp=&name;printf("%s\n",*pp);/* 输出 hello */

2.8 动态内存分配

动态分配允许程序在运行期间按需申请内存,是构建链表等动态数据结构的基础。

#include<stdlib.h>int*p=(int*)malloc(sizeof(int));/* 分配一个 int 大小 */int*arr=(int*)malloc(n*sizeof(int));/* 分配 n 个 int 的连续空间 */free(p);/* 使用完毕必须释放 */free(arr);

常用的相关函数:malloccallocfreerealloc。使用动态内存时必须注意「配对释放」,防止内存泄漏。

3. 典型案例分析

案例一:用指针实现数组逆序

题目:输入 10 个整数,用指针方法将它们按逆序重新存放并输出。

思路:设首指针p指向第一个元素,尾指针q指向最后一个元素,二者相向移动并交换所指内容,直到p >= q

#include<stdio.h>voidreverse(int*a,intn){int*p=a;int*q=a+n-1;while(p<q){inttmp=*p;*p=*q;*q=tmp;p++;q--;}}intmain(void){inta[10];inti;printf("请输入 10 个整数:\n");for(i=0;i<10;i++)scanf("%d",&a[i]);reverse(a,10);printf("逆序后:\n");for(i=0;i<10;i++)printf("%d ",a[i]);putchar('\n');return0;}

分析:本题体现了「指针 + 数组」的经典用法。函数reverse通过指针直接操作原数组,不需要返回新数组,避免了内存浪费。

案例二:函数指针实现简易计算器

题目:编写程序,根据用户输入的四则运算符号,调用相应函数完成计算。

思路:把加、减、乘、除四个函数存入函数指针数组,用运算符作为下标索引。

#include<stdio.h>intadd(inta,intb){returna+b;}intsub(inta,intb){returna-b;}intmul(inta,intb){returna*b;}intdiv(inta,intb){returna/b;}intmain(void){int(*ops[4])(int,int)={add,sub,mul,div};charsymbols[]="+-*/";intx,y;charop;printf("请输入表达式(如 8+2):");scanf("%d%c%d",&x,&op,&y);for(inti=0;i<4;i++){if(op==symbols[i]){printf("%d %c %d = %d\n",x,op,y,ops[i](x,y));break;}}return0;}

分析:本题展示了函数指针数组的威力。如果不使用函数指针,往往需要写if-elseswitch逐个分支调用,代码冗长且不易扩展。

案例三:字符串排序

题目:用指针数组对若干字符串按字典序从小到大排序。

思路:使用指针数组保存每个字符串的首地址,排序时只交换指针,不移动字符串本身,从而提升效率。

#include<stdio.h>#include<string.h>voidsort_strings(char*arr[],intn){for(inti=0;i<n-1;i++){for(intj=0;j<n-1-i;j++){if(strcmp(arr[j],arr[j+1])>0){char*tmp=arr[j];arr[j]=arr[j+1];arr[j+1]=tmp;}}}}intmain(void){char*names[]={"pear","apple","orange","banana"};intn=4;sort_strings(names,n);for(inti=0;i<n;i++)printf("%s\n",names[i]);return0;}

分析:排序过程中交换的是指针变量(地址),而不是字符串内容,这就是「通过指针引用字符串」带来的典型优势。

4. 课后习题解析(节选)

习题 4:输入三个整数,按由小到大的顺序输出

要求:用指针方法处理。

#include<stdio.h>voidswap(int*p,int*q){inttmp=*p;*p=*q;*q=tmp;}intmain(void){inta,b,c;int*p1=&a,*p2=&b,*p3=&c;printf("请输入三个整数:");scanf("%d%d%d",&a,&b,&c);if(*p1>*p2)swap(p1,p2);if(*p1>*p3)swap(p1,p3);if(*p2>*p3)swap(p2,p3);printf("从小到大:%d %d %d\n",a,b,c);return0;}

解析:通过swap交换指针所指向的变量的值,经过三轮比较后,a必为最小、c必为最大。

习题 5:输入三个字符串,按由小到大的顺序输出

#include<stdio.h>#include<string.h>intmain(void){chars1[80],s2[80],s3[80];char*p1=s1,*p2=s2,*p3=s3,*tmp;printf("请输入三个字符串:\n");scanf("%s%s%s",s1,s2,s3);if(strcmp(p1,p2)>0){tmp=p1;p1=p2;p2=tmp;}if(strcmp(p1,p3)>0){tmp=p1;p1=p3;p3=tmp;}if(strcmp(p2,p3)>0){tmp=p2;p2=p3;p3=tmp;}printf("从小到大:%s %s %s\n",p1,p2,p3);return0;}

解析:这里借助指针变量交换「指向关系」,字符串内容本身无需移动。

习题:用函数指针求两个整数的最大值、最小值、和

#include<stdio.h>intmax(inta,intb){returna>b?a:b;}intmin(inta,intb){returna<b?a:b;}intsum(inta,intb){returna+b;}voidcompute(intx,inty,int(*func)(int,int)){printf("结果为:%d\n",func(x,y));}intmain(void){inta=10,b=20;compute(a,b,max);compute(a,b,min);compute(a,b,sum);return0;}

解析compute函数的第三个参数是函数指针,通过传入不同函数,实现了同一框架下的多种运算,体现「回调」思想。

5. 易错点与避坑指南

  1. 指针未初始化就解引用int *p; *p = 10;是危险的,因为p中存的是随机地址。务必先让它指向合法空间。
  2. 混淆*在不同位置的含义:定义时int *p表示声明指针变量;使用时*p表示间接访问。两者作用完全不同。
  3. 忘记区分数组名与指针变量:数组名是常量,a++非法;指针变量可以自增。
  4. 返回局部变量地址:函数返回指向局部变量的指针是未定义行为,应避免。
  5. 动态内存不释放malloc分配的每一块内存都应在适当时机free,否则会造成内存泄漏。
  6. 字符数组与字符指针的差异:通过字符指针指向的字符串常量通常不可修改,直接写*str = 'H'可能引发运行时错误。

6. 本章小结

指针是 C 语言的灵魂。学好指针,才能真正理解 C 程序在内存中的运行机制,也才能熟练运用动态数据结构和高效算法。学习本章建议遵循「由浅入深」的路径:

理解地址与指针概念

掌握指针变量与基本运算

用指针处理数组

用指针处理字符串

函数指针与返回指针的函数

指针数组、多重指针与动态内存

综合运用:排序、链表等实战

建议读者在学习中多画内存示意图:明确「谁存地址、谁存数据」,每个指针变量指向哪里、解引用后得到什么。配合上机调试,逐步体会指针带来的灵活与高效,本章的难点便能迎刃而解。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/21 1:53:23

构建卡顿先从依赖链查起

构建卡顿先从依赖链查起说明&#xff1a;本文以 AI 产品场景说明降级、版本测试和预算控制。日志、成本、时延与成功率均为示例&#xff0c;不代表实际运行结果。三个月前&#xff0c;团队为了让旗下的 Markdown 笔记与文档协作工具“智能化”&#xff0c;发起了一项实验&#…

作者头像 李华
网站建设 2026/8/21 1:51:40

添火乐队《海参队之歌》的表达入口

当清晨想把心气捡回来的人走到闹钟响后、出门前的那杯水&#xff0c;《海参队之歌》往往会比空泛安慰更先开口——不是要你热闹起来&#xff0c;而是把说不清的那截情绪&#xff0c;轻轻按进旋律里。低电量时刻再听《海参队之歌》&#xff0c;最先被记住的不是一句漂亮形容&…

作者头像 李华
网站建设 2026/8/21 1:51:29

为满足女儿制作游戏心愿,Nick 打造 Canon 复现九岁编程快感

【导语&#xff1a;Nick 的八岁女儿想制作电子游戏&#xff0c;Nick 为其打造了名为 Canon 的“可以制作游戏的游戏”&#xff0c;灵感源于互联网早期文字 MUD&#xff0c;旨在复现他九岁时用 HyperCard 编程的体验。】HyperCard 开启编程启蒙之旅1989 年九岁的 Nick 在计算机课…

作者头像 李华
网站建设 2026/8/21 1:50:07

基于智能体与强化学习的AI科研复现系统构建指南

在实际 AI 研究和工程实践中&#xff0c;一个长期存在的挑战是如何让 AI 模型不仅生成看似合理的代码或文本&#xff0c;而是能够真正理解并复现复杂的科学研究过程。这不仅仅是代码生成&#xff0c;更是对科学方法、实验设计、数据分析和结果验证的完整模拟。对于希望构建或使…

作者头像 李华