尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

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

《C程序设计(第五版)》第8章「善于利用指针」知识要点、案例分析与课后习题解析 1. 本章导读指针是 C 语言最核心、也最容易让初学者感到困惑的知识点之一。谭浩强老师在《C程序设计第五版》中用了整整一章来讲解指针足见其重要性。本章从「指针是什么」讲起逐步深入到指针与数组、指针与字符串、函数指针、动态内存分配等内容最终目标是让读者能够灵活运用指针处理一批数据、优化函数传参、构建动态数据结构。本文将按照教材第 8 章的章节脉络梳理知识要点配合典型案例分析并对课后习题给出解析与参考代码帮助读者系统掌握指针。2. 知识要点梳理2.1 指针与指针变量地址内存中每个字节都有一个编号这个编号就是地址。变量在内存中占用若干字节其「首地址」就是该变量的地址。指针在 C 语言中地址被形象地称为「指针」即通过地址可以「指向」某个存储单元。指针变量专门用来存放地址指针的变量。定义指针变量时必须指定它指向的数据类型例如int *p;表示p是一个指向整型数据的指针变量。指针变量的定义与初始化inta10;int*pa;/* p 保存 a 的地址 */两个关键运算符运算符含义示例结果取地址a得到变量 a 的地址*间接访问解引用*p得到 p 所指向的变量2.2 指针变量作函数参数C 语言函数调用采用「值传递」——形参只是实参的一份拷贝。如果想在函数内部修改主调函数中的变量就必须把变量的地址传进去。这是指针最典型的应用之一。voidswap(int*x,int*y){inttmp*x;*x*y;*ytmp;}intmain(void){inta3,b5;swap(a,b);/* 调用后 a5, b3 */return0;}思考如果swap的参数写成int x, int y调用后a、b会交换吗答案是不会因为交换的只是形参副本。2.3 通过指针引用数组数组名代表数组首元素的地址因此可以用指针遍历数组提高程序执行效率。inta[5]{1,2,3,4,5};int*pa;/* 等价于 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(inti0;in;i)printf(%d ,*(pi));}2.4 通过指针引用字符串C 语言没有专门的字符串类型字符串本质上是以\0结尾的字符数组。可以用字符指针指向字符串常量char*strHello, C!;printf(%s\n,str);两种方式的区别charstr1[]hello;/* 字符数组内容可以修改 */char*str2hello;/* 指针指向字符串常量内容通常不可修改 */用字符指针处理字符串更灵活例如字符串复制、比较、连接等操作voidmy_strcpy(char*dest,constchar*src){while((*dest*src)!\0);}2.5 指向函数的指针函数在内存中也占存储空间函数的入口地址同样可以用指针保存。函数指针常用于回调函数和菜单驱动程序中。intadd(inta,intb){returnab;}intsub(inta,intb){returna-b;}intmain(void){int(*func)(int,int);/* 定义函数指针 */funcadd;printf(%d\n,func(10,20));/* 输出 30 */funcsub;printf(%d\n,func(10,20));/* 输出 -10 */return0;}2.6 返回指针值的函数函数可以返回地址指针典型场景是返回数组中某个元素或某段内存的地址。注意绝不能返回局部变量的地址因为函数结束后局部变量已被释放。int*max_elem(int*arr,intn){int*maxarr;for(inti1;in;i)if(arr[i]*max)maxarr[i];returnmax;}2.7 指针数组与多重指针指针数组数组的每个元素都是指针。常用于保存多个字符串char*names[]{Alice,Bob,Carol};printf(%s\n,names[1]);/* 输出 Bob */指向指针的指针多重指针指针变量本身也有地址可以用另一个指针指向它。char*namehello;char**ppname;printf(%s\n,*pp);/* 输出 hello */2.8 动态内存分配动态分配允许程序在运行期间按需申请内存是构建链表等动态数据结构的基础。#includestdlib.hint*p(int*)malloc(sizeof(int));/* 分配一个 int 大小 */int*arr(int*)malloc(n*sizeof(int));/* 分配 n 个 int 的连续空间 */free(p);/* 使用完毕必须释放 */free(arr);常用的相关函数malloc、calloc、free、realloc。使用动态内存时必须注意「配对释放」防止内存泄漏。3. 典型案例分析案例一用指针实现数组逆序题目输入 10 个整数用指针方法将它们按逆序重新存放并输出。思路设首指针p指向第一个元素尾指针q指向最后一个元素二者相向移动并交换所指内容直到p q。#includestdio.hvoidreverse(int*a,intn){int*pa;int*qan-1;while(pq){inttmp*p;*p*q;*qtmp;p;q--;}}intmain(void){inta[10];inti;printf(请输入 10 个整数\n);for(i0;i10;i)scanf(%d,a[i]);reverse(a,10);printf(逆序后\n);for(i0;i10;i)printf(%d ,a[i]);putchar(\n);return0;}分析本题体现了「指针 数组」的经典用法。函数reverse通过指针直接操作原数组不需要返回新数组避免了内存浪费。案例二函数指针实现简易计算器题目编写程序根据用户输入的四则运算符号调用相应函数完成计算。思路把加、减、乘、除四个函数存入函数指针数组用运算符作为下标索引。#includestdio.hintadd(inta,intb){returnab;}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(请输入表达式如 82);scanf(%d%c%d,x,op,y);for(inti0;i4;i){if(opsymbols[i]){printf(%d %c %d %d\n,x,op,y,ops[i](x,y));break;}}return0;}分析本题展示了函数指针数组的威力。如果不使用函数指针往往需要写if-else或switch逐个分支调用代码冗长且不易扩展。案例三字符串排序题目用指针数组对若干字符串按字典序从小到大排序。思路使用指针数组保存每个字符串的首地址排序时只交换指针不移动字符串本身从而提升效率。#includestdio.h#includestring.hvoidsort_strings(char*arr[],intn){for(inti0;in-1;i){for(intj0;jn-1-i;j){if(strcmp(arr[j],arr[j1])0){char*tmparr[j];arr[j]arr[j1];arr[j1]tmp;}}}}intmain(void){char*names[]{pear,apple,orange,banana};intn4;sort_strings(names,n);for(inti0;in;i)printf(%s\n,names[i]);return0;}分析排序过程中交换的是指针变量地址而不是字符串内容这就是「通过指针引用字符串」带来的典型优势。4. 课后习题解析节选习题 4输入三个整数按由小到大的顺序输出要求用指针方法处理。#includestdio.hvoidswap(int*p,int*q){inttmp*p;*p*q;*qtmp;}intmain(void){inta,b,c;int*p1a,*p2b,*p3c;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输入三个字符串按由小到大的顺序输出#includestdio.h#includestring.hintmain(void){chars1[80],s2[80],s3[80];char*p1s1,*p2s2,*p3s3,*tmp;printf(请输入三个字符串\n);scanf(%s%s%s,s1,s2,s3);if(strcmp(p1,p2)0){tmpp1;p1p2;p2tmp;}if(strcmp(p1,p3)0){tmpp1;p1p3;p3tmp;}if(strcmp(p2,p3)0){tmpp2;p2p3;p3tmp;}printf(从小到大%s %s %s\n,p1,p2,p3);return0;}解析这里借助指针变量交换「指向关系」字符串内容本身无需移动。习题用函数指针求两个整数的最大值、最小值、和#includestdio.hintmax(inta,intb){returnab?a:b;}intmin(inta,intb){returnab?a:b;}intsum(inta,intb){returnab;}voidcompute(intx,inty,int(*func)(int,int)){printf(结果为%d\n,func(x,y));}intmain(void){inta10,b20;compute(a,b,max);compute(a,b,min);compute(a,b,sum);return0;}解析compute函数的第三个参数是函数指针通过传入不同函数实现了同一框架下的多种运算体现「回调」思想。5. 易错点与避坑指南指针未初始化就解引用int *p; *p 10;是危险的因为p中存的是随机地址。务必先让它指向合法空间。混淆*在不同位置的含义定义时int *p表示声明指针变量使用时*p表示间接访问。两者作用完全不同。忘记区分数组名与指针变量数组名是常量a非法指针变量可以自增。返回局部变量地址函数返回指向局部变量的指针是未定义行为应避免。动态内存不释放malloc分配的每一块内存都应在适当时机free否则会造成内存泄漏。字符数组与字符指针的差异通过字符指针指向的字符串常量通常不可修改直接写*str H可能引发运行时错误。6. 本章小结指针是 C 语言的灵魂。学好指针才能真正理解 C 程序在内存中的运行机制也才能熟练运用动态数据结构和高效算法。学习本章建议遵循「由浅入深」的路径理解地址与指针概念掌握指针变量与基本运算用指针处理数组用指针处理字符串函数指针与返回指针的函数指针数组、多重指针与动态内存综合运用排序、链表等实战建议读者在学习中多画内存示意图明确「谁存地址、谁存数据」每个指针变量指向哪里、解引用后得到什么。配合上机调试逐步体会指针带来的灵活与高效本章的难点便能迎刃而解。
返回列表