C语言编程要点---指针和内存分配上-Read.doc
《C语言编程要点---指针和内存分配上-Read.doc》由会员分享,可在线阅读,更多相关《C语言编程要点---指针和内存分配上-Read.doc(12页珍藏版)》请在咨信网上搜索。
1、C语言编程要点-指针和内存分配上-ReadC语言编程要点-第7章 指针和内存分配(上)指针和内存分配指针为C语言编程提供了强大的支持如果你能正确而灵活地利用指针,你就可以直接切入问题的核心,或者将程序分割成一个个片断。一个很好地利用了指针的程序会非常高效、简洁和精致。 利用指针你可以将数据写入内存中的任意位置,但是,一旦你的程序中有一个野指针(wild”pointer),即指向一个错误位置的指针,你的数据就危险了存放在堆中的数据可能会被破坏,用来管理堆的数据结构也可能会被破坏,甚至操作系统的数据也可能会被修改,有时,上述三种破坏情况会同时发生。 此后可能发生的事情取决于这样两点:第一,内存中的
2、数据被破坏的程度有多大;第二,内存中的被破坏的部分还要被使用多少次。在有些情况下,一些函数(可能是内存分配函数、自定义函数或标准库函数)将立即(也可能稍晚一点)无法正常工作。在另外一些情况下,程序可能会终止运行并报告一条出错消息;或者程序可能会挂起;或者程序可能会陷入死循环;或者程序可能会产生错误的结果;或者程序看上去仍在正常运行,因为程序没有遭到本质的破坏。 值得注意的是,即使程序中已经发生了根本性的错误,程序有可能还会运行很长一段时间,然后才有明显的失常表现;或者,在调试时,程序的运行完全正常,只有在用户使用时,它才会失常。 在C语言程序中,任何野指针或越界的数组下标(out-of-bou
3、nds array subscript)都可能使系统崩溃。两次释放内存的操作也会导致这种结果。你可能见过一些C程序员编写的程序中有严重的错误,现在你能知道其中的部分原因了。 有些内存分配工具能帮助你发现内存分配中存在的问题,例如漏洞(leak,见721),两次释放一个指针,野指针,越界下标,等等。但这些工具都是不通用的,它们只能在特定的操作系统中使用,甚至只能在特定版本的编译程序中使用。如果你找到了这样一种工具,最好试试看能不能用,因为它能为你节省许多时间,并能提高你的软件的质量。 指针的算术运算是C语言(以及它的衍生体,例如C+)独有的功能。汇编语言允许你对地址进行运算,但这种运算不涉及数据
4、类型。大多数高级语言根本就不允许你对指针进行任何操作,你只能看一看指针指向哪里。 C指针的算术运算类似于街道地址的运算。假设你生活在一个城市中,那里的每一个街区的所有街道都有地址。街道的一侧用连续的偶数作为地址,另一侧用连续的奇数作为地址。如果你想知道River Rd街道158号北边第5家的地址,你不会把158和5相加,去找163号;你会先将5(你要往前数5家)乘以2(每家之间的地址间距),再和158相加,去找River Rd街道的168号。同样,如果一个指针指向地址158(十进制数)中的一个两字节短整型值,将该指针加3=5,结 果将是一个指向地址168(十进制数)中的短整型值的指针(见77和
5、78中对指针加减运算的详细描述)。 街道地址的运算只能在一个特定的街区中进行,同样,指针的算术运算也只能在一个特定的数组中进行。实际上,这并不是一种限制,因为指针的算术运算只有在一个特定的数组中进行才有意义。对指针的算术运算来说,一个数组并不必须是一个数组变量,例如函数malloc()或calloc()的返回值是一个指针,它指向一个在堆中申请到的数组。 指针的说明看起来有些使人感到费解,请看下例: char *p; 上例中的说明表示,p是一个字符。符号“*”是指针运算符,也称间接引用运算符。当程序间接引用一个指针时,实际上是引用指针所指向的数据。 在大多数计算机中,指针只有一种,但在有些计算机
6、中,指向数据和指向函数的指针可以是不同的,或者指向字节(如char。指针和void *指针)和指向字的指针可以是不同的。这一点对sizeof运算符没有什么影响。但是,有些C程序或程序员认为任何指针都会被存为一个int型的值,或者至少会被存为一个long型的值,这就无法保证了,尤其是在IBM PC兼容机上。 注意:以下讨论与Macintosh或UNIX程序员无关; 最初的IBM PC兼容机使用的处理器无法有效地处理超过16位的指针(人们对这种结论仍有争议。16位指针是偏移量,见93中对基地址和偏移量的讨论)。尽管最初的IBM PC机最终也能使用20位指针,但颇费周折。因此,从一开始,基于IBM兼
7、容机的各种各样的软件就试图冲破这种限制。 为了使20位指针能指向数据,你需要指示编译程序使用正确的存储模式,例如紧缩存储模式。在中存储模式下,你可以用20位指针指向函数。在大和巨存储模式下,用20位指针既可以指向数据,也可以指向函数。在任何一种存储模式下,你都可能需要用到far指针(见718和719)。 基于286的系统可以冲破20位指针的限制,但实现起来有些困难。从386开始,IBM兼容机就可以使用真正的32位地址了,例如象MS-Windows和OS2这样一些操作系统就实现了这一点,但MSDOS仍未实现。 如果你的MSDOS程序用完了基本内存,你可能需要从扩充内存或扩展内存中分配更多的内存。
8、许多版本的编译程序和函数库都提供了这种技术,但彼此之间有所差别。这些技术基本上是不通用的,有些能在绝大多数MS-DOS和MS-WindowsC编译程序中使用,有些只能在少数特定的编译程序中使用,还有一些只能在特定的附加函数库的支持下使用。如果你手头有能提供这种技术的软件,你最好看一下它的文档,以了解更详细的信息.7.1. 什么是间接引用(indirection)? 对已说明的变量来说,变量名就是对变量值的直接引用。对指向变量或内存中的任何对象的指针来说,指针就是对对象值的间接引用。如果p是一个指针,p的值就是其对象的地址;*p表示“使间接引用运算符作用于p”,*p的值就是p所指向的对象的值。
9、*p是一个左值,和变量一样,只要在*p的右边加上赋值运算符,就可改变*p的值。如果p是一个指向常量的指针,*p就是一个不能修改的左值,即它不能被放到赋值运算符的左边,请看下例:例 7.1 一个间接引用的例子#include intmain() int i; int * p ; i = 5; p = & i; / * now * p = = i * / / * %Pis described in FAQ VII. 28 * / printf(i=%d, p=%P, * p= %dn , i, P, *p); * p = 6; / * same as i = 6 * / printf(i=%d,
10、p=%P, * p= %dn , i, P, *P); return 0; / * see FAQ XVI. 4 * / 上例说明,如果p是一个指向变量i的指针,那么在i能出现的任何一个地方,你都可以用*p代替i。在上例中,使p指向i(p&i)后,打印i或*p的结果是相同的;你甚至可以给*p赋值,其结果就象你给i赋值一样。 请参见: 74 什么是指针常量?7.2. 最多可以使用几层指针? 对这个问题的回答与“指针的层数”所指的意思有关。如果你是指“在说明一个指针时最多可以包含几层间接引用”,答案是“至少可以有12层”。请看下例:int i = 0;int * ip0l = &d;int * i
11、p02 = &ip01;int *ip03 = &ip02;int * ip04 = &dp03;int * ip05 = &ip04;int * ip06 = &ip05;int * ip07 = &ip06;int * ip08 = &ip07;int * ip09 = &ip08;int *ip10 = &ip09;int *ipll = &ip10;int * ip12 = &ipll;* ip12 = 1; / * i = 1 * / 注意:ANSIC标准要求所有的编译程序都必须能处理至少12层间接引用,而你所使用的编译程序可能支持更多的层数。 如果你是指“最多可以使用多少层指针而不
12、会使程序变得难读”,答案是这与你的习惯有关,但显然层数不会太多。一个包含两层间接引用的指针(即指向指针的指针)是很常见的,但超过两层后程序读起来就不那么容易了,因此,除非需要,不要使用两层以上的指针。 如果你是指“程序运行时最多可以有几层指针”,答案是无限层。这一点对循环链表来说是非常重要的,因为循环链表的每一个结点都指向下一个结点,而程序能一直跟住这些指针。请看下例: 例72一个有无限层间接引用的循环链表/ * Would run forever if you didnt limit it to MAX * /# include struct circ_list char value 3 ;
13、 /* e.g.,st (incl 0) */ struct circ_list * next; struct circ_list suffixes = th , &.suffixes 1 , / * Oth * / st , &.suffixes 2 , / * 1st * / nd , & suffixes 3 , / * 2nd * / rd , & suffixes 4 , / * 3rd * / th, &.suffixes 5 , / * 4th * / th , &.suffixes 6 , / * 5th * / th , & suffixes 7 , / * 6th * /
14、th , & suffixes 8 , / * 7th * / th, & suffixes 9 , / * 8th * / th , & suffixes 0 , / * 9th * / ;# define MAX 20main() int i = 0; struct circ_list *p = suffixes; while (i value); + +i; p = p-next; 在上例中,结构体数组suffixes的每一个元素都包含一个表示词尾的字符串(两个字符加上末尾的NULL字符)和一个指向下一个元素的指针,因此它有点象一个循环链表;next是一个指针,它指向另一个circ_li
15、st结构体,而这个结构体中的next成员又指向另一个circ_list结构体,如此可以一直进行下去。 上例实际上相当呆板,因为结构体数组suffixes中的元素个数是固定的,你完全可以用类似的数组去代替它,并在while循环语句中指定打印数组中的第(i10)个元素。循环链表中的元素一般是可以随意增减的,在这一点上,它比上例中的结构体数组suffixes要有趣一些。请参见: 71 什么是间接引用(indirection)?7.3. 什么是空指针? 有时,在程序中需要使用这样一种指针,它并不指向任何对象,这种指针被称为空指针。空指针的值是NULL,NULL是在中定义的一个宏,它的值和任何有效指针的
16、值都不同。NULL是一个纯粹的零,它可能会被强制转换成void*或char*类型。即NULL可能是0,0L或(void*)0等。有些程序员,尤其是C+程序员,更喜欢用0来代替NULL。 指针的值不能是整型值,但空指针是个例外,即空指针的值可以是一个纯粹的零(空指针的值并不必须是一个纯粹的零,但这个值是唯一有用的值。在编译时产生的任意一个表达式,只要它是零,就可以作为空指针的值。在程序运行时,最好不要出现一个为零的整型变量)。 注意:空指针并不一定会被存为零,见710。 警告:绝对不能间接引用一个空指针,否则,你的程序可能会得到毫无意义的结果,或者得到一个全部是零的值,或者会突然停止运行。 请参
17、见: 74 什么时候使用空指针? 710 NULL总是等于0吗? 724 为什么不能给空指针赋值? 什么是总线错误、内存错误和内存信息转储7.4. 什么时候使用空指针? 空指针有以下三种用法: (1)用空指针终止对递归数据结构的间接引用。 递归是指一个事物由这个事物本身来定义。请看下例: /*Dumb implementation;should use a loop */ unsigned factorial(unsinged i) if(i0 | i1) return 1; else return i * factorial(i-1); 在上例中,阶乘函数factoriai()调用了它本身,
18、因此,它是递归的。 一个递归数据结构同样由它本身来定义。最简单和最常见的递归数据结构是(单向)链表,链表中的每一个元素都包含一个值和一个指向链表中下一个元素的指针。请看下例: struct string_list char *str; /* string(inthiscase)*/ struct string_list *next; ; 此外还有双向链表(每个元素还包含一个指向链表中前一个元素的指针)、键树和哈希表等许多整洁的数据结构,一本较好的介绍数据结构的书中都会介绍这些内容。 你可以通过指向链表中第一个元素的指针开始引用一个链表,并通过每一个元素中指向下一个元素的指针不断地引用下一个元素
19、;在链表的最后一个元素中,指向下一个元素的指针被赋值为NULL,当你遇到该空指针时,就可以终止对链表的引用了。请看下例: while(p!=NULL) /*dO something with p-str*/ pp-next; 请注意,即使p一开始就是一个空指针,上例仍然能正常工作。 (2)用空指针作函数调用失败时的返回值。 许多C库函数的返回值是一个指针,在函数调用成功时,函数返回一个指向某一对象的指针;反之,则返回一个空指针。请看下例: if(setlocale(cat,loc_p)=NULL) /* setlocale()failed;do something*/ /* .*/ 返回值为一
20、指针的函数在调用成功时几乎总是返回一个有效指针(其值不等于零),在调用失败时则总是返回一个空指针(其值等于零);而返回值为一整型值的函数在调用成功时几乎总是返回一个零值,在调用失败时则总是返回一个非零值。请看下例: if(raise(sig)!0) /* raise()failed;do something*/ /* */ 对上述两类函数来说,调用成功或失败时的返回值含义都是不同的。另外一些函数在调用成功时可能会返回一个正值,在调用失败时可能会返回一个零值或负值。因此,当你使用一个函数之前,应该先看一下它的返回值是哪种类型,这样你才能判断函数返回值的含义。 (3)用空指针作警戒值 警戒值是标志
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 编程 要点 指针 内存 分配 Read
1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前自行私信或留言给上传者【人****来】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时私信或留言给本站上传会员【人****来】,需本站解决可联系【 微信客服】、【 QQ客服】,若有其他问题请点击或扫码反馈【 服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【 版权申诉】”(推荐),意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:4008-655-100;投诉/维权电话:4009-655-100。