C语言难点分析整理

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

\目录1.C语言中的指针和内存泄漏52.C语言难点分析整理103.C语言难点184.C/C++实现冒泡排序算法325.C++中指针和引用的区别356.constchar*,charconst*,char*const的区别367.C中可变参数函数实现388.C程序内存中组成部分419.C编程拾粹4210.C语言中实现数组的动态增长4411.C语言中的位运算4612.浮点数的存储格式:5013.位域5814.C语言函数二维数组传递方法6415.C语言复杂表达式的执行步骤6616.C语言字符串函数大全6817.C语言宏定义技巧8918.C语言实现动态数组10019.C语言笔试-运算符和表达式10420.C语言编程准则之稳定篇10721.C语言编程常见问题分析10822.C语言编程易犯毛病集合11223.C语言缺陷与陷阱(笔记)11924.C语言防止缓冲区溢出方法12625.C语言高效编程秘籍12826.C运算符优先级口诀13327.do/while(0)的妙用13428.exit()和return()的区别14029.exit子程序终止函数与return的差别14130.extern与static存储空间矛盾14531.PC-Lint与C\C++代码质量14732.spirntf函数使用大全15833.二叉树的数据结构16734.位运算应用口诀和实例17035.内存对齐与ANSIC中struct内存布局17336.冒泡和选择排序实现18037.函数指针数组与返回数组指针的函数18638.右左法则-复杂指针解析18939.回车和换行的区别19240.堆和堆栈的区别19441.堆和堆栈的区别19842.如何写出专业的C头文件20243.打造最快的Hash表20744.指针与数组学习笔记22245.数组不是指针22446.标准C中字符串分割的方法22847.汉诺塔源码23148.洗牌算法23449.深入理解C语言指针的奥秘23650.游戏外挂的编写原理25451.程序实例分析-为什么会陷入死循环25852.空指针究竟指向了内存的哪个地方26053.算术表达式的计算26554.结构体对齐的具体含义26955.连连看AI算法27456.连连看寻路算法的思路28357.重新认识:指向函数的指针28858.链表的源码29159.高质量的子程序29560.高级C语言程序员测试必过的十六道最佳题目+答案详解29761.C语言常见错误32062.超强的指针学习笔记32563.程序员之路──关于代码风格34364.指针、结构体、联合体的安全规范34665.C指针讲解35266.关于指向指针的指针36867.C/C++误区一:voidmain()37368.C/C++误区二:fflush(stdin)37669.C/C++误区三:强制转换malloc()的返回值38070.C/C++误区四:charc=getchar();38171.C/C++误区五:检查new的返回值38372.C是C++的子集吗?38473.C和C++的区别是什么?38774.无条件循环38875.产生随机数的方法38976.顺序表及其操作39077.单链表的实现及其操作39178.双向链表39579.程序员数据结构笔记39980.Hashtable和HashMap的区别40881.hash表学习笔记41082.C程序设计常用算法源代码41283.C语言有头结点链表的经典实现41984.C语言惠通面试题42885.C语言常用宏定义4501.C语言中的指针和内存泄漏在使用C语言时,您是否对花时间调试指针和内存泄漏问题感到厌倦?如果是这样,那么本文就适合您。您将了解可能导致内存破坏的指针操作类型,您还将研究一些场景,了解要在使用动态内存分配时考虑什么问题。引言对于任何使用C语言的人,如果问他们C语言的最大烦恼是什么,其中许多人可能会回答说是指针和内存泄漏。这些的确是消耗了开发人员大多数调试时间的事项。指针和内存泄漏对某些开发人员来说似乎令人畏惧,但是一旦您了解了指针及其关联内存操作的基础,它们就是您在C语言中拥有的最强大工具。本文将与您分享开发人员在开始使用指针来编程前应该知道的秘密。本文内容包括:1.导致内存破坏的指针操作类型2.在使用动态内存分配时必须考虑的检查点3.导致内存泄漏的场景如果您预先知道什么地方可能出错,那么您就能够小心避免陷阱,并消除大多数与指针和内存相关的问题。?什么地方可能出错?有几种问题场景可能会出现,从而可能在完成生成后导致问题。在处理指针时,您可以使用本文中的信息来避免许多问题。1.未初始化的内存在本例中,p已被分配了10个字节。这10个字节可能包含垃圾数据,如图1所示。char*p=malloc(10);图1.垃圾数据如果在对这个p赋值前,某个代码段尝试访问它,则可能会获得垃圾值,您的程序可能具有不可预测的行为。p可能具有您的程序从未曾预料到的值。良好的实践是始终结合使用memset和malloc,或者使用calloc。char*p=malloc(10);memset(p,’\0’,10);现在,即使同一个代码段尝试在对p赋值前访问它,该代码段也能正确处理Null值(在理想情况下应具有的值),然后将具有正确的行为。2.内存覆盖由于p已被分配了10个字节,如果某个代码片段尝试向p写入一个11字节的值,则该操作将在不告诉您的情况下自动从其他某个位置“吃掉”一个字节。让我们假设指针q表示该内存。图2.原始q内容图3.覆盖后的q内容结果,指针q将具有从未预料到的内容。即使您的模块编码得足够好,也可能由于某个共存模块执行某些内存操作而具有不正确的行为。下面的示例代码片段也可以说明这种场景。Char*name=(char*)malloc(11);//Assignsomevaluetonamememcpy(p,name,11);//Problembeginshere在本例中,memcpy操作尝试将11个字节写到p,而后者仅被分配了10个字节。作为良好的实践,每当向指针写入值时,都要确保对可用字节数和所写入的字节数进行交叉核对。一般情况下,memcpy函数将是用于此目的的检查点。内存读取越界内存读取越界(overread)是指所读取的字节数多于它们应有的字节数。这个问题并不太严重,在此就不再详述了。下面的代码提供了一个示例。char*ptr=(char*)malloc(10);charname[20];memcpy(name,ptr,20);//Problembeginshere在本例中,memcpy操作尝试从ptr读取20个字节,但是后者仅被分配了10个字节。这还会导致不希望的输出。内存泄漏内存泄漏可能真正令人讨厌。下面的列表描述了一些导致内存泄漏的场景。重新赋值我将使用一个示例来说明重新赋值问题。char*memoryArea=malloc(10);char*newArea=malloc(10);这向如下面的图4所示的内存位置赋值。图4.内存位置memoryArea和newArea分别被分配了10个字节,它们各自的内容如图4所示。如果某人执行如下所示的语句(指针重新赋值)……memoryArea=newArea;则它肯定会在该模块开发的后续阶段给您带来麻烦。在上面的代码语句中,开发人员将memoryArea指针赋值给newArea指针。结果,memoryArea以前所指向的内存位置变成了孤立的,如下面的图5所示。它无法释放,因为没有指向该位置的引用。这会导致10个字节的内存泄漏。图5.内存泄漏在对指针赋值前,请确保内存位置不会变为孤立的。首先释放父块假设有一个指针memoryArea,它指向一个10字节的内存位置。该内存位置的第三个字节又指向某个动态分配的10字节的内存位置,如图6所示。图6.动态分配的内存free(memoryArea)如果通过调用free来释放了memoryArea,则newArea指针也会因此而变得无效。newArea以前所指向的内存位置无法释放,因为已经没有指向该位置的指针。换句话说,newArea所指向的内存位置变为了孤立的,从而导致了内存泄漏。每当释放结构化的元素,而该元素又包含指向动态分配的内存位置的指针时,应首先遍历子内存位置(在此例中为newArea),并从那里开始释放,然后再遍历回父节点。这里的正确实现应该为:free(memoryArea-newArea);free(memoryArea);返回值的不正确处理有时,某些函数会返回对动态分配的内存的引用。跟踪该内存位置并正确地处理它就成为了calling函数的职责。char*func(){returnmalloc(20);//makesuretomemsetthislocationto‘\0’…}voidcallingFunc(){func();//Problemlieshere}在上面的示例中,callingFunc()函数中对func()函数的调用未处理该内存位置的返回地址。结果,func()函数所分配的20个字节的块就丢失了,并导致了内存泄漏。归还您所获得的在开发组件时,可能存在大量的动态内存分配。您可能会忘了跟踪所有指针(指向这些内存位置),并且某些内存段没有释放,还保持分配给该程序。始终要跟踪所有内存分配,并在任何适当的时候释放它们。事实上,可以开发某种机制来跟踪这些分配,比如在链表节点本身中保留一个计数器(但您还必须考虑该机制的额外开销)。访问空指针访问空指针是非常危险的,因为它可能使您的程序崩溃。始终要确保您不是在访问空指针。总结本文讨论了几种在使用动态内存分配时可以避免的陷阱。要避免内存相关的问题,良好的实践是:始终结合使用memset和malloc,或始终使用calloc。每当向指针写入值时,都要确保对可用字节数和所写入的字节数进行交叉核对。在对指针赋值前,要确保没有内存位置会变为孤立的。每当释放结构化的元素(而该元素又包含指向动态分配的内存位置的指针)时,都应首先遍历子内存位置并从那里开始释放,然后再遍历回父节点。始终正确处理返回动态分配的内存引用的函数返回值。每个malloc都要有一个对应的free。确保您不是在访问空指针。2.C语言难点分析整理这篇文章主要是介绍一些在复习C语言的过程中笔者个人认为比较重点的地方,较好的掌握这些重点会使对C的运用更加得心应手。此外会包括一些细节、易错的地方。涉及的主要内容包括:变量的作用域和存储类别、函数、数组、字符串、指针、文件、链表等。一些最基本的概念在此就不多作解释了,仅希望能有只言片语给同是C语言初学者的学习和上机过程提供一点点的帮助。变量作用域和存储类别:了解了基本的变量类型后,我们要进一步了解它的存储类别和变量作用域问题。变量类别子类别局部变量静态变量(离开函数,变量值仍保留)自动变量寄存器变量全局变量静态变量(只能在本文件中用)非静态变量(允许其他文件使用)换一个角度变量类别子类别静态存储变量静态局部变量(函数)静态全局变量(本文件)非静态全局/外部变量(其他文件引用)动态存储变量自动变量寄存器变量形式参数extern型的存储变量在处理多文件问题时常能用到,在一个文件中定义extern型的变量即说明这个变量用的是其他文件的。顺便说一下,笔者在做课设时遇到outofmemory的错误,于是改成做多文件,再把它include进来(注意自己写的*.h要用“”不用),能起到一定的效用。static型的在读程序写结果的试题中是个考点。多数时候整个程序会出现多个定义的变量在不同的函数中,考查在不同位置同一变量的值是多少。主要是遵循一个原则,只要本函数内没有定义的变量就用全局变量(而不是main里的),全局变量和局部变量重名时局部变量起作用,当然还要注意静态与自动变量的区别。函数:对于函数最基本的理解是从那个叫main的单词开始的,一开始总会觉得把语句一并写在main里不是挺好的么,为什么偏择出去。其实这是因为对函数还不够熟练,否则函数的运用会给我们编程带来极大的便利。我们要知道函数的返回值类型,参数的类型,

1 / 318
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功