知识大全 C 语言最大难点揭秘
Posted 知
篇首语:互补的人容易一见钟情,相似的人适合一起变老。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 C 语言最大难点揭秘相关的知识,希望对你有一定的参考价值。
内存调试技巧:C 语言最大难点揭秘 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
本文将带您了解一些良好的和内存相关的编码实践 以将内存错误保持在控制范围内 内存错误是 C 和 C++ 编程的祸根 它们很普遍 认识其严重性已有二十多年 但始终没有彻底解决 它们可能严重影响应用程序 并且很少有开发团队对其制定明确的管理计划 但好消息是 它们并不怎么神秘 引言
C 和 C++ 程序中的内存错误非常有害 它们很常见 并且可能导致严重的后果 来自计算机应急响应小组(请参见参考资料)和供应商的许多最严重的安全公告都是由简单的内存错误造成的 自从 年代末期以来 C 程序员就一直讨论此类错误 但其影响在 年仍然很大 更糟的是 如果按我的思路考虑 当今的许多 C 和 C++ 程序员可能都会认为内存错误是不可控制而又神秘的顽症 它们只能纠正 无法预防
但事实并非如此 本文将让您在短时间内理解与良好内存相关的编码的所有本质
正确的内存管理的重要性
存在内存错误的 C 和 C++ 程序会导致各种问题 如果它们泄漏内存 则运行速度会逐渐变慢 并最终停止运行;如果覆蓋内存 则会变得非常脆弱 很容易受到恶意用户的攻击 从 年著名的莫里斯蠕虫攻击到有关 Flash Player 和其他关键的零售级程序的最新安全警报都与缓冲区溢出有关 大多数计算机安全漏洞都是缓冲区溢出 Rodney Bates 在 年写道
在可以使用 C 或 C++ 的地方 也广泛支持使用其他许多通用语言(如 Java? Ruby Haskell C# Perl Smalltalk 等) 每种语言都有众多的爱好者和各自的优点 但是 从计算角度来看 每种编程语言优于 C 或 C++ 的主要优点都与便于内存管理密切相关 与内存相关的编程是如此重要 而在实践中正确应用又是如此困难 以致于它支配着面向对象编程语言 功能性编程语言 高级编程语言 声明性编程语言和另外一些编程语言的所有其他变量或理论
与少数其他类型的常见错误一样 内存错误还是一种隐性危害 它们很难再现 症状通常不能在相应的源代码中找到 例如 无论何时何地发生内存泄漏 都可能表现为应用程序完全无法接受 同时内存泄漏不是显而易见
因此 出于所有这些原因 需要特别关注 C 和 C++ 编程的内存问题 让我们看一看如何解决这些问题 先不谈是哪种语言
内存错误的类别
首先 不要失去信心 有很多办法可以对付内存问题 我们先列出所有可能存在的实际问题
内存泄漏
错误分配 包括大量增加 free()释放的内存和未初始化的引用
悬空指针
数组边界违规
这是所有类型 即使迁移到 C++ 面向对象的语言 这些类型也不会有明显变化;无论数据是简单类型还是 C 语言的 struct或 C++ 的类 C 和 C++ 中内存管理和引用的模型在原理上都是相同的 以下内容绝大部分是 纯 C 语言 对于扩展到 C++ 主要留作练习使用
内存泄漏
在分配资源时会发生内存泄漏 但是它从不回收 下面是一个可能出错的模型(请参见清单 )
清单 简单的潜在堆内存丢失和缓冲区覆蓋
void f (char *explanation)char p ;p = malloc( );(void) sprintf(p The f error occurred because of %s explanation);local_log(p ); 您看到问题了吗?除非 local_log()对 free()释放的内存具有不寻常的响应能力 否则每次对 f 的调用都会泄漏 字节 在记忆棒增量分发数兆字节内存时 一次泄漏是微不足道的 但是连续操作数小时后 即使如此小的泄漏也会削弱应用程序
在实际的 C 和 C++ 编程中 这不足以影响您对 malloc()或 new的使用 本部分开头的句子提到了 资源 不是仅指 内存 因为还有类似以下内容的示例(请参见清单 ) FILE句柄可能与内存块不同 但是必须对它们给予同等关注
清单 来自资源错误管理的潜在堆内存丢失
int getkey(char *filename)FILE *fp;int key;fp = fopen(filename r );fscanf(fp %d &key);return key; fopen的语义需要补充性的 fclose 在没有 fclose()的情况下 C 标准不能指定发生的情况时 很可能是内存泄漏 其他资源(如信号量 网络句柄 数据库连接等)同样值得考虑
内存错误分配
错误分配的管理不是很困难 下面是一个示例(请参见清单 )
清单 未初始化的指针
void f (int datum)int *p ;/* Uh oh! No one has initialized p */*p = datum; 关于此类错误的好消息是 它们一般具有显著结果 在 AIX 下 对未初始化指针的分配通常会立即导致 segmentation fault错误 它的好处是任何此类错误都会被快速地检测到;与花费数月时间才能确定且难以再现的错误相比 检测此类错误的代价要小得多
在此错误类型中存在多个变种 free()释放的内存比 malloc()更频繁(请参见清单 ) /* Allocate once free ice */void f ()char *p;p = malloc( ); free(p); free(p);/* Allocate zero times free once */void f ()char *p;/* Note that p remains uninitialized here */free(p); 这些错误通常也不太严重 尽管 C 标准在这些情形中没有定义具体行为 但典型的实现将忽略错误 或者快速而明确地对它们进行标记;总之 这些都是安全情形
悬空指针
悬空指针比较棘手 当程序员在内存资源释放后使用资源时会发生悬空指针(请参见清单 )
清单 悬空指针
void f ()struct x *xp;xp = (struct x *) malloc(sizeof (struct x));xp q = ; free(xp); /* Problem! There s no guarantee thatthe memory block to which xp pointshasn t been overwritten */return xp q; 传统的 调试 难以隔离悬空指针 由于下面两个明显原因 它们很难再现
即使影响提前释放内存范围的代码已本地化 内存的使用仍然可能取决于应用程序甚至(在极端情况下)不同进程中的其他执行位置
悬空指针可能发生在以微妙方式使用内存的代码中 结果是 即使内存在释放后立即被覆蓋 并且新指向的值不同于预期值 也很难识别出新值是错误值 悬空指针不断威胁著 C 或 C++ 程序的运行状态
数组边界违规
数组边界违规十分危险 它是内存错误管理的最后一个主要类别 回头看一下清单 ;如果 explanation的长度超过 则会发生什么情况?回答 难以预料 但是它可能与良好情形相差甚远 特别是 C 复制一个字符串 该字符串不适于为它分配的 个字符 在任何常规实现中 超过的 字符会覆蓋内存中的其他数据 内存中数据分配的布局非常复杂并且难以再现 所以任何症状都不可能追溯到源代码级别的具体错误 这些错误通常会导致数百万美元的损失
内存编程的策略
勤奋和自律可以让这些错误造成的影响降至最低限度 下面我们介绍一下您可以采用的几个特定步骤;我在各种组织中处理它们的经验是 至少可以按一定的数量级持续减少内存错误
编码风格
编码风格是最重要的 我还从没有看到过其他任何作者对此加以强调 影响资源(特别是内存)的函数和方法需要显式地解释本身 下面是有关标头 注释或名称的一些示例(请参见清单 )
清单 识别资源的源代码示例
/********* ** Note that any function invoking protected_file_read()* assumes responsibility eventually to fclose() its* return value UNLESS that value is NULL *********/FILE *protected_file_read(char *filename)FILE *fp;fp = fopen(filename r );if (fp) else return fp;/******* * ** Note that the return value of get_message points to a * fixed memory location Do NOT free() it; remember to * make a copy if it must be retained * ********/char *get_message()static char this_buffer[ ]; (void) sprintf(this_buffer );return this_buffer;/********* * While this function uses heap memory and so* temporarily might expand the over all memory* footprint it properly cleans up after itself *********/int f (char *item )my_class c ;int result; c = new my_class(item ); result = c x;delete c ;return result;/********* * Note that f () is documented to return a value* which needs to be returned to heap; as f thinly* wraps f any code which invokes f () must be* careful to free() the return value *********/int *f ()int *p;p = f ( ); return p;
使这些格式元素成为您日常工作的一部分 可以使用各种方法解决内存问题
专用库
语言
软件工具
硬件检查器在这整个领域中 我始终认为最有用并且投资回报率最大的是考虑改进源代码的风格 它不需要昂贵的代价或严格的形式;可以始终取消与内存无关的段的注释 但影响内存的定义当然需要显式注释 添加几个简单的单词可使内存结果更清楚 并且内存编程会得到改进
我没有做受控实验来验证此风格的效果 如果您的经历与我一样 您将发现没有说明资源影响的策略简直无法忍受 这样做很简单 但带来的好处太多了
检测
检测是编码标准的补充 二者各有裨益 但结合使用效果特别好 机灵的 C 或 C++ 专业人员甚至可以浏览不熟悉的源代码 并以极低的成本检测内存问题 通过少量的实践和适当的文本搜索 您能够快速验证平衡的 *alloc()和 free()或者 new和 delete的源主体 人工查看此类内容通常会出现像清单 中一样的问题
清单 棘手的内存泄漏
static char *important_pointer = NULL;void f ()if (!important_pointer)important_pointer = malloc(IMPORTANT_SIZE); if (condition)/* Ooops! We just lost the referenceimportant_pointer already held */important_pointer = malloc(DIFFERENT_SIZE); 如果 condition为真 简单使用自动运行时工具不能检测发生的内存泄漏 仔细进行源分析可以从此类条件推理出证实正确的结论 我重复一下我写的关于风格的内容 尽管大量发布的内存问题描述都强调工具和语言 对于我来说 最大的收获来自 软的 以开发人员为中心的流程变更 您在风格和检测上所做的任何改进都可以帮助您理解由自动化工具产生的诊断
静态的自动语法分析
当然 并不是只有人类才能读取源代码 您还应使静态语法分析成为开发流程的一部分 静态语法分析是 lint 严格编译和几种商业产品执行的内容 扫描编译器接受的源文本和目标项 但这可能是错误的症状
希望让您的代码无 lint 尽管 lint已过时 并有一定的局限性 但是 没有使用它(或其较高级的后代)的许多程序员犯了很大的错误 通常情况下 您能够编写忽略 lint的优秀的专业质量代码 但努力这样做的结果通常会发生重大错误 其中一些错误影响内存的正确性 与让客户首先发现内存错误的代价相比 即使对这种类别的产品支付最昂贵的许可费也失去了意义 清除源代码 现在 即使 lint标记的编码可能向您提供所需的功能 但很可能存在更简单的方法 该方法可满足 lint 并且比较强键又可移植
内存库
补救方法的最后两个类别与前三个明显不同 前者是轻量级的;一个人可以容易地理解并实现它们 另一方面 内存库和工具通常具有较高的许可费用 对部分开发人员来说 它们需要进一步完善和调整 有效地使用库和工具的程序员是理解轻量级的静态方法的人员 可用的库和工具给人的印象很深 其作为组的质量很高 但是 即使最优秀的编程人员也可能会被忽略内存管理基本原则的非常任性的编程人员搅乱 据我观察 普通的编程人员在尝试利用内存库和工具进行隔离工作时也只能感到灰心
由于这些原因 我们催促 C 和 C++ 程序员为解决内存问题先了解一下自己的源 在这完成之后 才去考虑库
使用几个库能够编写常规的 C 或 C++ 代码 并保证改进内存管理 Jonathan Bartlett 在 developerWorks 的 评论专栏中介绍了主要的候选项 可以在下面的参考资料部分获得 库可以解决多种不同的内存问题 以致于直接对它们进行比较是非常困难的;这方面的常见主题包括垃圾收集 智能指针和智能容器 大体上说 库可以自动进行较多的内存管理 这样程序员可以犯更少的错误
我对内存库有各种感受 他们在努力工作 但我看到他们在项目中获得的成功比预期要小 尤其在 C 方面 我尚未对这些令人失望的结果进行仔细分析 例如 业绩应该与相应的手动内存管理一样好 但是这是一个灰色区域——尤其在垃圾收集库处理速度缓慢的情况下 通过这方面的实践得出的最明确的结论是 与 C 关注的代码组相比 C++ 似乎可以较好地接受智能指针
内存工具
开发真正基于 C 的应用程序的开发团队需要运行时内存工具作为其开发策略的一部分 已介绍的技术很有价值 而且不可或缺 在您亲自尝试使用内存工具之前 其质量和功能您可能还不了解
本文主要讨论了基于软件的内存工具 还有硬件内存调试器;在非常特殊的情况下(主要是在使用不支持其他工具的专用主机时)才考虑它们
市场上的软件内存工具包括专有工具(如 IBM Rational Purify 和 Electric Fence)和其他开放源代码工具 其中有许多可以很好地与 AIX 和其他操作系统一起使用
所有内存工具的功能基本相同 构建可执行文件的特定版本(很像在编译时通过使用 g标记生成的调试版本) 练习相关应用程序和研究由工具自动生成的报告 请考虑如清单 所示的程序
清单 示例错误
int main()char p[ ];strcpy(p Hello world );puts(p); 此程序可以在许多环境中 运行 它编译 执行并将 Hello world \\n 打印到屏幕 使用内存工具运行相同应用程序会在第四行产生一个数组边界违规的报告 在了解软件错误(将十四个字符复制到了只能容纳五个字符的空间中)方面 这种方法比在客户处查找错误症状的花费小得多 这是内存工具的功劳
结束语
cha138/Article/program/net/201311/12852相关参考
我想大家在平时生活中都会经常摄入维生素C,其中,维生素C是一种水溶性的常见的维生素,最近的研究显示,维生素C对治疗牛皮癣也具有较好的治疗效果。对于这一点牛皮癣患者朋友都比较好奇,我想,产生这种结果与其
我想大家在平时生活中都会经常摄入维生素C,其中,维生素C是一种水溶性的常见的维生素,最近的研究显示,维生素C对治疗牛皮癣也具有较好的治疗效果。对于这一点牛皮癣患者朋友都比较好奇,我想,产生这种结果与其
世界上最大的软体公司——微软公司,已形成其独特的(也有人认为是古怪的)招聘方式。该公司每个月会收到1.2万份履历。无须人工介入,计算机会通过关键词的方式仔细搜寻,然后将其录入数据库。有前景的履历会给应
第二课本课主题抽象数据类型的表示与实现教学目的了解抽象数据类型的定义表示和实现方法教学重点抽象数据类型表示法类C语言语法教学难点抽象数据类型表示法授课内容一抽象数据类型定义(ADT)作用抽象数据类型可
知识大全 我很想学一门除了英语之外的外语,但不知学哪种语言好
我很想学一门除了英语之外的外语,但不知学哪种语言好?一、建议第二外语学习德语。二、德语:1)语音和英语十分接近,舌颤音基本不需要,简单易掌握;2)语法上名词是难点,3个性,4个格,复数形式变化没有规律
学c语言能找什么工作如果你只学c语言,可能不好找工作,现在都是多学几种语言。只学习C语言能找什么工作汗!大学毕业,软件工程学完了都没工作。C语言我们大一学的最基础的东西学习C语言,以后能找什么工作有点
知识大全 c语言编程怎么样才能学好、高数和c语言学习有密切关系么
c语言编程怎么样才能学好、高数和c语言学习有密切关系么不是,导论就是介绍一下学科基础知识,了解一下更好,不懂对于学习编程语言影响也不大高数更木有关系学C语言要多上机练习,多写点代码就行了怎样才能学好C
大量()都在乡镇,乡镇企业数量众多,是安全监管的难点。A.民营企业B.国有企业C.合资企业D.小微企业【参考答案】D[拓展知识]()以上各级人民政府及有关部门对报告重大事故隐患或举报安全生产违法行为有
大量()都在乡镇,乡镇企业数量众多,是安全监管的难点。A.民营企业B.国有企业C.合资企业D.小微企业【参考答案】D[拓展知识]()以上各级人民政府及有关部门对报告重大事故隐患或举报安全生产违法行为有
自学c语言要多久可以工作C语言的确是基础C++是C的拓展,Java是用C语言开发出来的学语言一般都先学C掌握了编程思想,才学那些高级语言。现在很多人都懂C,不过都不精通,C精通的话工作工资都不缺。不过