大家帮我想个特征脸方法源码,希望有源码,或者是成本。再不济给我个思路。

编程好的思路。_百度知道
编程好的思路。
我想知道程序员有什么好的方法编写代码,能用较少的时间做较多的活,我在长时间编程当中也积累了一些,也提供给大家,但是我想分享不同人的思想。
我的方法有
(1)、将程序细化,将相似代码或相同代码用一段代码完成。这样可以使程序尽量的短小...
C++ 我只想是探讨,然后开阔思路,不要怕回答后被别人笑话,高手是在错误中成长的。下面是总结的经验。1、程序细化,将细化后的相似代码和重复代码合并,利于组建其他程序。2、程序模块化,利于整体调用。3、分类程序段,使程序思路清晰。4、合并相同和相似的代码,编写成一个函数,用到此功能调用此函数。5、编写通用程序,载入不同的脚本实现不同的功能。6、编写程序工具,不同功能的模块可以用程序工具来编辑,比用代码编辑方便。7、用通用的现成功能库来提高工作效率。8、持续优化重构和改进,提高代码质量。(lens23 提出的)9、建立通用数学模型,使一类事物用一个统一模型表示。10、预留一些接口,提高程序的可扩展性。(chm51666提出的)11、添加一些注释、流程图、说明文档使思路清晰。谢谢各位朋友的回答,但是最佳答案只能给一个人,也许每个人心中都认为自己的答案是最佳答案,甚至应该把编程放在哪个重要的角度上,但是我想说的是应该综合考虑。每个人的工作性质不一样,侧重点也就不一样,所要的答案当然也就不一样。因此分只能给一个人,我的问题后续还会提出。请有思想的人继续回答。
我有更好的答案
你说的都是关于编程思想的,参考所谓的23种设计模式吧,编程的基本思想多数都在里面了,不用自己总结。另外,就是持续优化重构和改进,这是最重要的。较少的时间做较多的事情,需要考虑,这里的时间包不包括调试时间,测试时间,改bug时间,返工时间。。。如果包括,那么可做的事情还有更详细的调试信息,更多的自动化测试脚本,项目过程回顾等等。
“持续优化重构和改进”,想法比较好。这个我需要。如果在21天内没有人提出比你优越的想法,我给你加分。
提高效率的方式还有:1.比如基于AOP来实现功能代码和辅助代码的解耦,提高可读性、可重用性和可维护性2.选择合适的编译器,优化编译,例如:分布式编译,提高编译速度3.构建模拟器,实现快速业务仿真,帮助程序员分析、理解、调试。。。4.针对不同项目内容,选择合适的编程语言
采纳率:44%
我认为编程,重要的不是如何华丽的代码,而是能够将用户需求转化为机器语言的能力你的很多思想,是刚开始做程序员的普遍想法,开始思考通过模块化设计能够更省力,更快捷的完成工作,程序运行效率还要高。如果你在大软件公司工作过,就不会有这种困惑了。因为对于具有一定规模的软件公司,已经在相当的时间内积累起很丰富的模块和库资源,程序员们只需要根据项目的不同象选择自助餐一样给拼接到一起,就有了基本框架。最重要的还是做好用户需求到需求说明,再到系统框架设计这个工作,会少走很多弯路。细化到编写程序,我觉得很重要的一点就是要求公司里面的程序员要有绝对规范的编程习惯,不然在团队协作的时候会出很多问题,做出来的基础库也经不起时间的考验。还有就是你说的模块化的东西不是万能的,和你经常从事的项目领域密切相关,你用着很顺手,别人可能用不了。比如你是做信息系统的,那么一个好的查询分析模块很多地方都能用,用户信息管理就要根据复杂程度做几套,比如能够定期更换密码的,比如权限是要细化到列的,比如只是一个简单的用户密码。根据项目的不同选用。有些人鼓吹自动化编程,利用商业化的系统模板进行配置。我认为对于企业应用还是可以的,但是对于软件开发就不可取了。因为提供模板的单位水平如何你并不知道,里面是否存在大量bug你也不清楚,只是演示做的漂亮。一旦你用了这个东西,在你的项目中出现问题,你debug是查不到具体原因的,苦果只能自己吃,这是个建议,有点离题,但是怕你思路到了一定程度就推崇这种方法。最后一点,大部分的项目都是需要数据库作为后台支持的,一定要注意处理好数据库设计的问题,不然很容易因为库设计的不合理造成程序复杂,或者是在使用一段时间后效率严重降低,造成程序重新返工,就说这么多,希望你能有所收获
谢谢你的回答,你的建议很有价值,确实编程规范比节省时间重要的多,模块化的东西像一个黑箱子一样,往往是纯在很多bug但是我们应该是取其精髓、弃其糟粕。模块的东西有他的好一面也有他的坏的一面,可能你在编程时不知不觉的已经用到了模块。我们看待问题应该综合每一个角度,而不是考虑顶层不考虑底层,考虑集体合作,不考虑效率。
可能我说的不够清楚。好像是从2004年开始,就有一股风气,叫什么代码生成器或者是开发系统之类的名词,记不清楚了。很多软件公司制作类似的产品推销给其他软件公司,作用就是用这个自动化生成项目框架或者是进行C/S程序到B/S程序的转换。这种东西流行了一段时间,很是害了一大批人。就是你说的黑盒,不知道里面是怎么弄得,出了问题大家互相推诿。不知道你是否接触过软件成熟度模型CMM,这里面就将软件过程的黑盒尽量打破,规范开发过程,我觉得理解CMM模型比研究哪个函数行数更少要有意义。模块一定要尽量保留和充实,如果没有模块化编程,一方面效率不能提升,一方面不能进行团队合作,一方面总是重复错误,增加测试难度,还有一方面就是出了问题更麻烦。但是我只建议本公司沉淀好的模块,并且有很好的传承和开发文档,像自助餐一样告诉你这是什么,怎么吃。绝不建议出去购买类似的产品。我始终认为,软件开发不是单打独斗的事情,也许一个人够出色,能够独立完成所有的代码编写,先不论时间上比团队要长很多,只说程序的BUG,好的程序员很难成为好的测试工程师,尤其是测试自己编写的程序,因为他已经知道程序流程,就很难编写测试用例了。又说多了,选你需要的看吧,软件工程说不完
写程序前先形成流程图或逻辑图以及文字文档,然后团队讨论,之后文档文字可作为注释,再在其基础上逐渐增加代码
我觉得编程要养成总结和汇总的习惯,积累经验汇总成接口和自定义控件:
(1)、硬件接口类:包括串口通信、SOCKET通信和网卡通信等,不同的通信方式封装成不同的接口类,同时考虑不同程序语言的兼容性(不同编程语言是否都可以调用)
(2)、数据接口类:不同数据库访问的接口类,常用的oracle、sqlserver数据库
(3)、应用接口类:包括EXECL、WORD之间的数据交互
(4)、数据显示控件:可以显示不同的数据源,包括数组、表和XML文档等
(5)、通用查询控件:可以动态加载查询条件和动态加载查询列信息
(6)、文件操作控件:文件的创建、读、写、上传、下载等业务
(7)、视频控件:语音、录音、在线视频等功能
....................
我认为编程是一个长期积累总结的过程,可以活到老学到老,我们先可以考虑功能性汇总,后面可以结合业务汇总成业务性质的功能控件.
工作好像是搞接口或是软硬件结合的工程人员。
编程好的思路。何学好VB 序:不要老是想着谁“最有钱途”,这些都不是我们应当考虑的,我们要考虑的是怎样才能真正的精通VB。如果成为了VB的专家,应当很容易赚到钱的。VB程序员为什么工资低,不是VB不行,是因为大部份VB程序员不行,他们只懂得用控件,而没有什么编程的思想。而VB的初学都只会用控件和简单的API就说精通VB了,显然影响的整个VB在程序界的地位,这些人显然不能称为程序员的,只能相当于会使用VB了,就像会Word一样。如果VB程序员都懂的数据结构,UML等,就完全是另外一回事了。有认为VB功能弱,请注意:仙剑95版就是用VB4做的,还有DirectX SDK7中有个一很好玩的3D游戏,声音和动画都非常好,也是用VB写的。所以VB能做很多东西,只要你想去做。 (1)VB不只是数据库和报表。不要以为VB就是数据库和报表,这只是VB强大功能的一小部份。如果这此都搞不清,还是好好的看一下相关的书籍,比在这里问问题有用多了。我以前没有学过数据库,只是有些编程的思路,后来学数据库就非常容易了。我做报表一般用RichTextBox或是From来做,一般都能做的很好,也不用去考虑什么水晶报表了。 (2)不要迷信于API。API能做很多东西,但有些东西在VB里面的函数中就有,API只是VB对WindowsSDK的封装而已。如果想学好API,建议学习SDK。如果VB本身就能很好的支持这个功能,何必还要用API呢。 (3)不要什么都想着控件。如果做程序都想着第三方控件的话,代价是很高的,不仅程序大,安全性也低了很多。其实VB本身带的控件已经完全够用了。 (4)认认真真的把MSDN中关于VB的内容好好看一看,特别是函数和语句,很有用的。 (5)遇到问题不要直接来这里问,自己好好想一想,试着解决这个问题,这虽然在时间上慢了,可是在自身的提高上却有很大的帮助。 (6)没事的时候,多研究些语言上的东西,如数据结构和算法,不要老是想着什么花哨的东西。这对以后的提高非常有帮助。 如何学好VB程序设计 1,学习程序设计的基本要求 2,Vb的功能及特点 3,学习VB程序设计的方法 对学习者知识的要求: 首先,学习程序设计要具备一定的数学基础.计算机与数学有很大的联系,综观计算机历史,计算机的数学模型(图灵机,由Alan Turing提出)和体系结构(由John Von Neuman提出)等都是由数学家提出的,最早的计算机也是为数值计算而设计的.因此,要学习好计算机就要有一定的数学基础.不过对于初学者来说,在数学基础方面的要求并不是很高,从我个人的角度来看,有高中数学水平就差不多了. 如何学好VB程序设计 学习程序设计基本要求 对学习者知识的要求: 其次,学习程序设计要有一定的逻辑思维能力.逻辑思维需要长时间的锻炼,如果你觉得自己在逻辑思维能力上有不足,也没有关系,因为编写程序本身也是对逻辑思维的锻炼.初学程序设计应具备的逻辑基础可以从高中数学中学到. 如何学好VB程序设计 学习程序设计基本要求 熟悉Visual Basic操作环境与设计工具,能设计应用程序界面. 掌握,理解面向对象程序设计基本概念 掌握VB语言的基础知识,程序设计的方法, 能阅读一般难的程序; 能应用编写一些简单程序; 具有用Visual Basic开发Windows环境下应用程序的能力和阅读分析一般难度的VB程序的能力. 如何学好VB程序设计 VB程序设计的基本要求 Visual Basic 简介 Visual Basic 是Microsoft 公司开发......
粘贴别人的,不对路
看过你写的,相信你也有一定量编程的经验了,你说的这个好字,比较难解释我来分享一下我的经验,权当交流从浅入深1 首先是在熟悉一门开发语言的基础上,做量的积累,有了几万行编码经验,再去看各种编程思想之类,会更有感触 ,才能辨出优劣2 试着在某个开源框架的基础上,重写其重要部分,比如log跟异常处理,应该有多少种异常,多少种log ,怎样封装使其适合客户需要 3 熟悉了各种借口各种框架后,试着做设计,利用UML,从上到下, 让不懂业务不懂开发的人看了后 明白各个模块的功能,出问题应该去哪里找 ,利用v型开发的思想,开发不同阶段与测试不同阶段相互对应4 了解各种语言各种框架的优劣点,比如spring跟struts相比较,容器数量与性能瓶颈的关系,何时采用何种数据库 大概预算5 思考如何控制成本,让刚毕业的学生也能编出可靠性可读性强的代码,这时候要利用经验写属于你的队伍的编程基准, 怎样定义变量,怎样定义函数怎样写注释等。做到除了核心逻辑代码及sql文,其他的都能自动生成的程度,可以参考各种程序生成工具,好好利用vba知识,可以节省大量时间,提高生产效率6 做好项目总结,这是你最宝贵的无形资产,包括实现某功能用了多少时间,出现了什么风险,如何解决,代价多少 哪里是机械的编码,可不可以自动生成等等开发思想有许多种,我的原则是用简单的代替复杂,够用则好,不必超出。代码不必华丽,只求易懂。
用方法和思想减少劳动量,提高工作效率。将复杂问题简单化,使思路清晰。和我的观点是完全一样的。你有很丰富的编程经验,至少要有5年工作经验。谢谢你“除了核心逻辑代码及sql文,其他的都能自动生成的程度”这个话意味着很多人能想到只有高水平的人才能做到,你能做到。其实我把软件工作者(程序员、架构师)分成了5类。1、服务、调试层面2、技术层面3、思想层面4、架构层面5、眼光、战略层面
其实自动生成代码没有那么复杂,包括画面的代码,都是可以生成的。最重要的好处是,通过设计来生成代码,可以保持设计跟代码的一致性这样无论换了多少开发者,都能段时间在源代码上 完美无缝的修改 增删功能但对于一个企业,这是点滴积累的,最好在新人培训阶段,让他们接触vba,试着用excle写设计,然后用vba生成代码刚开始工作时候,大量画面跳转的定义,全是手写在各种xml中,难读易错不易维护,自动化以后,完全从痛苦中解脱 ,给我感触很深
其他5条回答
为您推荐:
其他类似问题
您可能关注的内容
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。4.2k 人阅读
标签:至少1个,最多5个
每一盒香烟的包装上都会写『吸烟有害健康』。白酒瓶上也写了『过度饮酒,有害健康』。本文的外包装上写的则是『阅读有害健康』,特别是『甩掉强迫症』那一节,它适合我自己阅读,但不一定适合你。
黑暗的内存
很多人对 C 语言深恶痛绝,仅仅是因为 C 语言迫使他们在编程中必须手动分配与释放内存,然后通过指针去访问,稍有不慎可能就会导致程序运行运行时出现内存泄漏或内存越界访问。
C 程序的内存泄漏只会发生在程序所用的堆空间内,因为程序只能在堆空间内动态分配内存。NULL 指针、未初始化的指针以及引用的内存空间被释放了的指针,如果这些指针访问内存,很容易就让程序挂掉。
除了堆空间,程序还有个一般而言比较小的栈空间。这个空间是所有的函数共享的,每个函数在运行时会独占这个空间。栈空间的大小是固定的,它是留给函数的参数与局部变量用的。栈空间有点像宾馆,你下榻后,即使将房间搞的一团糟,也不需要你去收拾它,除非你把房间很严重的损坏了——用 C 的黑话来说,即缓冲区溢出。
虽然导致这些问题出现的原因很简单,但是却成为缺乏编程素养的人难以克服的障碍,被 C 语言吓哭很多次之后,他们叛逃到了 Java、C# 以及各种动态类型语言的阵营,因为这些语言将指针隐藏了起来,并提供内存垃圾回收(GC)功能。他们赢了,他们懒洋洋的躺在沙发上,拿着遥控器指挥内存,信号偶尔中断,内存偶尔紊乱。
C 内存的动态分配与回收
C 语言标准库(stdlib)中为堆空间中的内存分配与回收提供了 malloc 与 free 函数。例如,在下面的代码中,我们从堆空间中分配了 7 个字节大小的空间,然后又释放了:
#include &stdlib.h&
void *p = malloc(7);
一点都不难!跟你去学校图书馆借了 7 本书,然后又还回去没什么两样。有借有还,再借不难,过期不还,就要罚款。有谁因为去图书馆借几本书就被吓哭了的?
我们也可以向堆空间借点地方存储某种类型的数据:
int *n = malloc(4);
如果你不知道 int 类型的数据需要多大的空间才能装下,那就用 sizeof,让 C 编译器去帮助你计算,即:
int *n = malloc(sizeof(int));
策略与机制分离
在 C 语言中有关内存管理的机制已经简单到了几乎无法再简单的程度了,那么为何那么多人都在嘲笑讥讽挖苦痛骂诅咒 C 的内存管理呢?
如果你略微懂得一些来自 Unix 的哲学,可能听说过这么一句话:策略与机制分离。如果没听说过这句话,建议阅读 Eric Raymond 写的《Unix 编程艺术》第一章中的 Unix 哲学部分。
malloc 与 free 是 C 提供的内存管理机制,至于你怎么去使用这个机制,那与 C 没有直接关系。例如,你可以手动使用 malloc 与 free 来管理内存——最简单的策略,你也可以实现一种略微复杂一点的基于引用计数的内存管理策略,还可以基于 Lisp 之父 John McCarthy 独创的 Mark&Sweep 算法实现一种保守的内存自动回收策略,还可以将引用计数与 Mark&Sweep 这两种策略结合起来实现内存自动回收。总之,这些策略都可以在 C 的内存管理机制上实现。
借助 Boehm GC 库,就可以在 C 程序中实现垃圾内存的自动回收:
#include &assert.h&
#include &stdio.h&
#include &gc.h&
int main(void)
GC_INIT();
for (int i = 0; i & ; ++i)
int **p = GC_MALLOC(sizeof(int *));
int *q = GC_MALLOC_ATOMIC(sizeof(int));
assert(*p == 0);
*p = GC_REALLOC(q, 2 * sizeof(int));
if (i % 100000 == 0)
printf("Heap size = %zu\n", GC_get_heap_size());
在 C 程序中使用 Boehm GC 库,只需用 GC_MALLOC 或 C_MALLOC_ATOMIC 替换 malloc,然后去掉所有的 free 语句。C_MALLOC_ATOMIC 用于分配不会用于存储指针数据的堆空间。
如果你的系统(Linux)中安装了 boehm-gc 库(很微型,刚 100 多 Kb),可以用 gcc 编译这个程序然后运行一次体验一下,编译命令如下:
$ gcc -lgc test-gc.c
GNU 的 Scheme 解释器 Guile 2.0 就是用的 boehm-gc 来实现内存回收的。有很多项目在用 boehm-gc,只不过很少有人听说过它们,见:
如果 C 语言直接提供了某种内存管理策略,无论是提供引用计数还是 Mark&Sweep 抑或这二者的结合体,那么都是在剥夺其他策略生存的机会。例如,在 Java、C# 以及动态类型语言中,你很难再实现一种新的内存管理策略了——例如手动分配与释放这种策略。
Eric Raymond 说,将策略与机制揉在一起会导致有两个问题,(1) 策略会变得死板,难以适应用户需求的改变;(2) 任何策略的改变都极有可能动摇机制。相反,如果将二者剥离,就可以在探索新策略的时候不会破坏机制,并且还检验了机制的稳定性与有效性。
Unix 的哲学与 C 有何相干?不仅是有何相干,而且是息息相关!因为 C 与 Unix 是鸡生蛋 & 蛋生鸡的关系——Unix 是用 C 语言开发的,而 C 语言在 Unix 的开发过程中逐渐成熟。C 语言只提供机制,不提供策略,也正因为如此才招致了那些贪心的人的鄙薄。
这么多年来,像 C 语言提供的这种 malloc + free 的内存管理机制一直都没有什么变化,而计算机科学家们提出的内存管理策略在数量上可能会非常惊人。像 C++ 11 的智能指针与 Java 的 GC 技术,如果从研究的角度来看,可能它们已经属于陈旧的内存回收策略了。因为它们的缺点早就暴露了出来,相应的改进方案肯定不止一种被提了出来,而且其中肯定会有一些策略是基于概率算法的……那些孜孜不倦到处寻找问题的计算机科学家们,怎能错过这种可以打怪升级赚经费的好机会?
总之,C 已经提供了健全的内存管理机制,它并没有限制你使用它实现一种新的内存管理策略。
手动管理内存的常见陷阱
在编写 C 程序时,手动管理内存只有一个基本原则是:谁需要,谁分配;谁最后使用,谁负责释放。这里的『谁』,指的是函数。也就是说,我们有义务全程跟踪某块被分配的堆空间的生命周期,稍有疏忽可能就会导致内存泄漏或内存被重复释放等问题。
那些在函数内部作为局部变量使用的堆空间比较容易管理,只要在函数结尾部分稍微留心将其释放即可。一个函数写完后,首先检查一下所分配的堆空间是否被正确释放,这个习惯很好养成。这种简单的事其实根本不用劳烦那些复杂的内存回收策略。
C 程序内存管理的复杂之处在于在某个函数中分配的堆空间可能会一路辗转穿过七八个函数,最后又忘记将其释放,或者本来是希望在第 7 个函数中访问这块堆空间的,结果却在第 3 个函数中将其释放了。尽管这样的场景一般不会出现(根据快递公司丢包的概率,这种堆空间传递失误的概率大概有 0.001),但是一旦出现,就够你抓狂一回的了。没什么好方法,惟有提高自身修养,例如对于在函数中走的太远的堆空间,一定要警惕,并且思考是不是设计思路有问题,寻找缩短堆空间传播路径的有效方法。
堆空间数据在多个函数中传递,这种情况往往出现于面向对象编程范式。例如在 C++ 程序中,对象会作为一种穿着隐行衣的数据——this 指针的方式穿过对象的所有方法(类的成员函数),像穿糖葫芦一样。不过,由于 C++ 类专门为对象生命终结专门设立了析构函数,只要这个析构函数没被触发,那么这个对象在穿过它的方法时,一般不会出问题。因为 this 指针是隐藏的,也没人会神经错乱在对象的某个方法中去 delete this。真正的陷阱往往出现在类的继承上。任何一个训练有素的 C++ 编程者都懂得什么时候动用虚析构函数,否则就会陷入用 delete 去释放引用了派生类对象的基类指针所导致的内存泄漏陷阱之中。
在面向对象编程范式中,还会出现对象之间彼此引用的现象。例如,如果对象 A 引用了对象 B,而对象 B 又引用了对象 A。如果这两个对象的析构函数都试图将各自所引用对象销毁,那么程序就会直接崩溃了。如果只是两个相邻的对象的相互引用,这也不难解决,但是如果 A 引用了 B,B 引用了 C, C 引用了 D, D 引用了 B 和 E,E 引用了 A……然后你可能就凌乱了。如果是基于引用计数来实现内存自动回收,遇到这种对象之间相互引用的情况,虽然那程序不会崩溃,但是会出现内存泄漏,除非借助弱引用来打破这种这种引用循环,本质上这只是变相的谁最后使用,谁负责释放。
函数式编程范式中,内存泄漏问题依然很容易出现,特别是在递归函数中,通常需要借助一种很别扭的思维将递归函数弄成尾递归形式才能解决这种问题。另外,惰性计算也可能会导致内存泄漏。
似乎并没有任何一种编程语言能够真正完美的解决内存泄漏问题——有人说 Rust 能解决,我不是很相信,但是显而易见,程序在设计上越低劣,就越容易导致内存错误。似乎只有通过大量实践,亡羊补牢,塞翁失马,卧薪尝胆,破釜沉舟,久而久之,等你三观正常了,不焦不躁了,明心见性了,内存错误这种癌症就会自动从你的 C 代码中消失了——好的设计品味,自然就是内存友好的。当我们达到这种境界时,可能就不会再介意在 C 中手动管理内存。
让 Valgrind 帮你养成 C 内存管理的好习惯
Linux 环境中有一个专门用于 C 程序内存错误检测工具——valgrind,其他操作系统上应该也有类似的工具。valgrind 能够发现程序中大部分内存错误——程序中使用了未初始化的内存,使用了已释放的内存,内存越界访问、内存覆盖以及内存泄漏等错误。
看下面这个来自『』的小例子:
#include &stdlib.h&
void f(void)
int* x = malloc(10 * sizeof(int));
x[10] = 0;
int main(void)
不难发现,在 f 函数中即存在这内存泄漏,又存在着内存越界访问。假设这份代码保存在 valgrind-demo.c 文件中,然后使用 gcc 编译它:
$ gcc -g -O0 valgrind-demo.c -o valgrind-demo
为了让 valgrind 能够更准确的给出程序内存错误信息,建议打开编译器的调试选项 -g,并且禁止代码优化,即 -O0。
然后用 valgrind 检查 valgrind-demo 程序:
$ valgrind --leak-check=yes ./valgrind-demo
结果 valgrind 输出以下信息:
==10000== Memcheck, a memory error detector
==10000== Copyright (C) , and GNU GPL'd, by Julian Seward et al.
==10000== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==10000== Command: ./valgrind-demo
==10000== Invalid write of size 4
at 0x400574: f (valgrind-demo.c:6)
by 0x400585: main (valgrind-demo.c:11)
Address 0x51d3068 is 0 bytes after a block of size 40 alloc'd
at 0x4C29FE0: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x400567: f (valgrind-demo.c:5)
by 0x400585: main (valgrind-demo.c:11)
==10000== HEAP SUMMARY:
in use at exit: 40 bytes in 1 blocks
total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==10000== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29FE0: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x400567: f (valgrind-demo.c:5)
by 0x400585: main (valgrind-demo.c:11)
==10000== LEAK SUMMARY:
definitely lost: 40 bytes in 1 blocks
indirectly lost: 0 bytes in 0 blocks
possibly lost: 0 bytes in 0 blocks
still reachable: 0 bytes in 0 blocks
suppressed: 0 bytes in 0 blocks
==10000== For counts of detected and suppressed errors, rerun with: -v
==10000== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
valgrind 首先检测出在 valgrind-demo 程序中存在一处内存越界访问错误,即:
==10000== Invalid write of size 4
at 0x400574: f (valgrind-demo.c:6)
by 0x400585: main (valgrind-demo.c:11)
Address 0x51d3068 is 0 bytes after a block of size 40 alloc'd
at 0x4C29FE0: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x400567: f (valgrind-demo.c:5)
by 0x400585: main (valgrind-demo.c:11)
然后 valgrind 又发现在 valgrind-demo 程序中存在 40 字节的内存泄漏,即:
10000== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29FE0: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x400567: f (valgrind-demo.c:5)
by 0x400585: main (valgrind-demo.c:11)
由于我们在编译时开启了调试选项,所以 valgrind 也能告诉我们内存错误发生在具体哪一行源代码中。
除了可用于程序内存错误的检测之外,valgrind 也具有函数调用关系跟踪、程序缓冲区检查、多线程竞争检测等功能,但是无论 valgrind 有多么强大,你要做的是逐渐的摆脱它,永远也不要将自己的代码建立在『反正 valgrind 能帮我检查错误』这样的基础上。
甩掉强迫症
选择用 C 语言来写程序,这已经让我们牺牲了很多东西——项目进度、漂亮的桌面程序、炙手可热的网站前端……如果你再为 C 语言的一些『脆弱』之处患上强迫症,这样的人生太过于悲催。用 C 语言,就要对自己好一点。
负责分配内存的 malloc 函数可能会遇到内存分配失败的情况,这时它会返回 NULL。于是,问题就来了,是否需要在程序中检测 malloc 的返回值是否为 NULL?我觉得没必要检测,只需记住 malloc 可能会返回 NULL 这一事实即可。如果一个程序连内存空间都无法分配了,那么它还有什么再继续运行的必要?有时,可能会因为系统中进程存在内存泄漏,导致你的程序无法分配内存,这时你使用 malloc 返回的 NULL 指针来访问内存,会出现地址越界错误,这种错误很容易定位,并且由于你知道
malloc 可能会返回 NULL 这一事实,也很容易确定错误的原因,实在不济,还有 valgrind。
如果确实有对 malloc 返回值进行检查的必要,例如本文评论中 @依云 所说的那些情况,可以考虑这样做:
#include &stdio.h&
#include &stdlib.h&
#define SAFE_MALLOC(n) safe_malloc(n)
void * safe_malloc(size_t n) {
void *p = malloc(n);
printf("你的内存不够用,我先撤了!\n");
int main(void) {
int *p = SAFE_MALLOC(sizeof(int));
... ... ...
如果你被我说服了,决定不去检查 malloc 的返回值是否为 NULL,那么又一个问题随之而来。我们是否需要在程序中检测一个指针是否为 NULL?NULL 指针实在是太恐怖了,直接决定了程序的生死。为了安全起见,在用一个指针时检测一下它的值是否为 NULL 似乎非常有必要。特别是向一个函数传递指针,很多编程专家都建议在函数内部首先要检测指针参数是否为 NULL,并将这种行为取名为『契约式编程』。之所以是契约式的,是因为这种检测已经假设了函数的调用者可能会传入 NULL……事实上这种契约非常容易被破坏,例如:
void foo(int *p)
printf("You passed a NULL pointer!\n");
... ... ...
int main(void)
当我将一个未初始化的指针传给 foo 函数时,foo 函数对参数的检测不会起到任何作用。
可能你会辩解,说调用 foo 函数的人,应该先将 p 指针初始化为 NULL,但这有些自欺欺人。契约应当是双方彼此达成一致意见之后才能签署,而不是你单方面起草一个契约,然后要求他人必须遵守这个契约。foo 不应该为用户传入无效的指针而买单,何况它也根本无法买这个单——你能检测的了 NULL,但是无法检测未初始化的指针或者被释放了的指针。也许你认为只要坚持将指针初始化为 NULL,并坚持消除野指针,那么 foo 中的 NULL 检测就是有效的。但是很可惜,野指针不是那么容易消除,下面就会讨论此事。凡是不能彻底消除的问题,就不应该再浪费心机,否则只是将一个问题演变了另一个问题而已。那些被重重遮掩的问题,一旦被触发,你会更看难看清真相。
指针不应该受到不公正待遇。如果你处处纠结程序中用到的整型数或浮点数是否会溢出,或者你走在人家楼下也不是时时仰望上方有没有高空坠物,那么也就不应该对指针是否为 NULL 那么重视,甚至不惜代价为其修建万里长城。在 C 语言中,不需要指针的 NULL 契约,只需要遵守指针法律:你要传给我指针,就必须保证你的指针是有效的,否则我就用程序崩溃来惩罚你。
第三个问题依然与 NULL 有关,那就是一个指针所引用的内存空间被释放后,是否要将这个指针赋值为 NULL?对于这个问题,大家一致认为应该为之赋以 NULL,否则这个指针就成为『野指针』——野指针是有害的。一开始我也这么认为,但是久而久之就觉得消除野指针,是一种很无聊的行为。程序中之所以会出现野指针引发的内存错误,往往意味着你的代码出现了拙劣的设计!如果消除野指针,再配合指针是否为 NULL 的检测,这样做固然可以很快的定位出错点,但是换来的经常是一个很脏的补丁式修正,而坏的设计可能会继续得到纵容。
如果你真的害怕野指针,可以像下面这样做:
#include &stdio.h&
#include &stdlib.h&
#define SAFE_FREE(p) safe_free((void **)(&(p)))
void safe_free(void **p) {
*p = NULL;
printf("哎呀,我怕死野指针了!\n");
int main(void) {
int *p = malloc(sizeof(int));
for(int i = 0; i & 10; i++) {
SAFE_FREE(p);
对于所引用的内存被释放了的指针,即使赋之以 NULL,也只能解决那些你原本一眼就能看出来的问题。更糟糕的是,当你对消除野指针非常上心时,每当消除一个野指针,可能会让你觉得你的程序更加健壮了,这种错觉反而消除了你的警惕之心。如果一块内存空间被多个指针引用,当你通过其中一个指针释放这块内存空间之后,并赋该指针以 NULL,那么其他几个指针该怎么处理?也许你会说,那应该用引用计数技术来解决这样的问题。引用计数的确可以解决一些问题,但是它又带来一个新的问题,对于指针所引用的空间,在引用计数为 0 时,它被释放了,这时另外一个地方依然有代码在试图 unref,这时该怎么处理?
绝对的不去检测指针是否为 NULL 肯定也不科学。因为有时 NULL 是作为状态来用的。例如在树结构中,可以根据任一结点中的子结点指针是否为 NULL 来判断这个结点是否为叶结点。有些函数通过返回 NULL 告诉调用者:『我可耻的失败了』。我觉得这才是 NULL 真正的用武之地。
王垠在『』一文中告诫大家,尽量不要让函数返回 NULL,他认为如果你的函数要返回『没有』或『出错了』之类的结果,尽量使用 Java 的异常机制。这种观点也许是对的,但是鉴于 C 没有异常机制(在 C 中可以用 setjmp/longjmp 勉强模拟异常),只有 NULL 可用。有些人形而上学强加附会的将这种观点解读为让函数返回 NULL 是有害的,甚至将这种行为视为『低级错误』,甚至认为 C 指针的存在本身就是错误,认为这样做是整个软件行业 40 多年的耻辱,这是小题大作,或者说他只有能力将罪责推给 NULL,而没有能力限制 NULL 的副作用。如果我们只将 NULL 用于表示『没有』或『出错了』的状态,这非但无害,而且会让代码更加简洁清晰。
如果你期望一个函数能够返回一个有效的指针,那么你就有义务检查它是不是真的返回了有效的指针,否则就没必要检查。这种检查其实与这个函数是否有可能返回 NULL 无关。类似的 NULL 检查,在生活中很常见。即使银行的 ATM 机已经在安全性上做了重重防御,但是你取钱时,也经常会检查一下 ATM 吐出来钱在数目上对不对。你过马路时,虽然有红绿灯,而且你也都是通过驾照考试的,但你依然会下意识的环顾左右,看有没有正在过往的车辆。如果你坚持 NULL 的意义不明确而导致歧义,然后得出推论『返回 NULL 的函数是有害的』,这只不过是在说「这个人又像好人,又像坏蛋,所以他是有害的」。
当你打算检测一个指针的值是否为 NULL 时,问题又来了……我们是应该
if(p == NULL) {
... ... ...
... ... ...
很多人害怕出错,他们往往会选择第一种判断方式,他们的理由是:在某些 C 的实现(编译器与标准库)中,NULL 的值可能不是 0。这个理由,也许对于 C99 之前的 C 是成立的,但是至少从 C99 就不再是这样了。C99 标准的 6.3.2.3 节,明确将空指针定义为常量 0。在现代一些的 C 编译器上,完全可以放心使用更为简洁且直观的第二种判断方式。
用 C 语言,就不要想太多。想的太多,你可能就不会或者不敢编程了。用 C 语言,你又必须想太多,因为不安全的因素到处都有,但是也只有不安全的东西才真正是有威力的工具,刀枪剑戟,车铣刨磨,布鲁弗莱学院传授的挖掘机技术,哪样不能要人命!不要想太多,指的是不要在一些细枝末节之处去考虑安全性,甚至对于野指针这种东西都诚惶诚恐。必须想太多,指的是多从程序的逻辑层面来考虑安全性。出错不可怕,可怕的是你努力用一些小技俩来规避错误,这种行为只会导致错误向后延迟,延迟到基于引用计数的内存回收,延迟到 Java 式的 GC,延迟到你认为可以高枕无忧然而错误却像癌症般的出现的时候。
8 收藏&&|&&43
你可能感兴趣的文章
,对于指针,不管空指针的值是多少, !p 都等同于 p == NULL,编译器会做这种判断。而 NULL 总是被定义为 (void*) 0,而不管平台上的空指针值实际上多少。
看来评论的编辑次数还有限制?
[comp.lang.c 的 FAQ 上说](http://c-faq.com/null/ptrtest.html),对于指针,不管空指针的值是多少, !p 都等同于 p == NULL,编译器会做这种判断。而 NULL 总是被定义为 (void*) 0,而不管平台上的空指针值实际上多少。
看来评论的编辑次数还有限制?
可以的,只要支持手动分配与释放内存的语言,都能写出自动释放内存的『扩展』。
可以的,只要支持手动分配与释放内存的语言,都能写出自动释放内存的『扩展』。
在一些情况下,检查 malloc 是否成功是有用的。比如使用 Vim 的时候,一不注意尝试打开一个巨大的日志文件,结果内存不够用。这时候你当然不希望 Vim 崩溃掉。使用 Python 进行交互式的数据处理的时候也一样,你不希望因为不小心加载了过大的数据集,导致你之前的所有操作都被迫丢弃。
在一些情况下,检查 malloc 是否成功是有用的。比如使用 Vim 的时候,一不注意尝试打开一个巨大的日志文件,结果内存不够用。这时候你当然不希望 Vim 崩溃掉。使用 Python 进行交互式的数据处理的时候也一样,你不希望因为不小心加载了过大的数据集,导致你之前的所有操作都被迫丢弃。
能不能写一个自动释放内存的扩展?
能不能写一个自动释放内存的扩展?
写一个类似 Boehm GC 库的库?
写一个类似 Boehm GC 库的库?
我也不是很清楚,我是写PHP的,只是有这么一个想法而已。。。
我也不是很清楚,我是写PHP的,只是有这么一个想法而已。。。
嚓,用了这个站的测试版,竟然没法回复评论。
既然有这种需求,那么我改一下文章。不过,我早就习惯了一个 emacs 打开一个文件 :)
嚓,用了这个站的测试版,竟然没法回复评论。
既然有这种需求,那么我改一下文章。不过,我早就习惯了一个 emacs 打开一个文件 :)
学会编程至少要十年,然而大部分人只是想找份工作。。。
学会编程至少要十年,然而大部分人只是想找份工作。。。
那他们挺幸福的,因为要在 10 年里一直不知道自己不会编程 :)
那他们挺幸福的,因为要在 10 年里一直不知道自己不会编程 :)
嗯,不知道是次数限制,还是时间限制。
嗯,不知道是次数限制,还是时间限制。
都有,而且我都遇到了 :-( :-( :-(
都有,而且我都遇到了 :-( :-( :-(
好文,先赞!
有些没太理解的部分,请指教:“如果你期望一个函数能够返回一个有效的指针,那么你就有义务检查它是不是真的返回了有效的指针,否则就没必要检查。”
那么对于malloc的调用,我是期望它能够返回有效指针的,为何您建议不去检查它是否真的返回了有效指针呢?
好文,先赞!
有些没太理解的部分,请指教:“如果你期望一个函数能够返回一个有效的指针,那么你就有义务检查它是不是真的返回了有效的指针,否则就没必要检查。”
那么对于malloc的调用,我是期望它能够返回有效指针的,为何您建议不去检查它是否真的返回了有效指针呢?
如果 malloc 返回 NULL,说明系统已无内存可分配……除非你是希望遇到 malloc 返回 NULL 的情况,就释放一部分内存,否则就没必要检查它是否返回 NULL。事实上,大部分人检查 malloc,目的并不是这样。
如果 malloc 返回 NULL,说明系统已无内存可分配……除非你是希望遇到 malloc 返回 NULL 的情况,就释放一部分内存,否则就没必要检查它是否返回 NULL。事实上,大部分人检查 malloc,目的并不是这样。
我向你的 gmail 邮箱发了一封邮件。
我向你的 gmail 邮箱发了一封邮件。
赞!另希望能推荐一些关于设计品味的文章,或干脆您写一篇专题。
赞!另希望能推荐一些关于设计品味的文章,或干脆您写一篇专题。
这样的文章,我写不出来……除非我也写了个震古烁今的程序 :)
可以搜一下 Donald Knuth,Ken Thompson 这些大神的一些访谈,特别是 Ken 爷爷的,他对积木式的自下向上的开发方法的理解,世界一流。像 Brian Kernighan 所写的《程序设计实践》这样的书,里面总结的经验几乎处处都值得借鉴……
不过,对于在职员工而言,好品味,可能也不会太有用——总是要受上级以及同事的制约的。因地制宜,总是以理解问题然后寻求最短路径去解决问题为要,问题解决了之后,再去琢磨如何将代码弄得更漂亮一些……这样做肯定没错。
这样的文章,我写不出来……除非我也写了个震古烁今的程序 :)
可以搜一下 Donald Knuth,Ken Thompson 这些大神的一些访谈,特别是 Ken 爷爷的,他对积木式的自下向上的开发方法的理解,世界一流。像 Brian Kernighan 所写的《程序设计实践》这样的书,里面总结的经验几乎处处都值得借鉴……
不过,对于在职员工而言,好品味,可能也不会太有用——总是要受上级以及同事的制约的。因地制宜,总是以理解问题然后寻求最短路径去解决问题为要,问题解决了之后,再去琢磨如何将代码弄得更漂亮一些……这样做肯定没错。
我很喜欢指针啊,当初第一次学Java的时候,没有了指针,瞬间不知所措C和C++其实都一样,他们提供最原始但又是最强大的东西给你,用得好不好,全看程序员素养那些喷C/C++的程序员,先看看自己的能力再说。当然,我也不排斥没有指针的语言。但是我觉得全天下的程序员,都应当把指针弄熟了,再去学习别的语言,否则遇到问题的时候都不知道怎么解决
我很喜欢指针啊,当初第一次学Java的时候,没有了指针,瞬间不知所措
C和C++其实都一样,他们提供最原始但又是最强大的东西给你,用得好不好,全看程序员素养
那些喷C/C++的程序员,先看看自己的能力再说。
当然,我也不排斥没有指针的语言。但是我觉得全天下的程序员,都应当把指针弄熟了,再去学习别的语言,否则遇到问题的时候都不知道怎么解决
你可能感兴趣的文章
分享到微博?
我要该,理由是:
在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。

我要回帖

更多关于 eclipse查看方法源码 的文章

 

随机推荐