如果你是用C或C ++的人谁的方案,没有内存管理,类型检查或缓冲区溢出保护,使用指针运算的管理语言的好处,你如何确保你的程序是安全的? 您是否使用了大量的单元测试,还是你只是一个谨慎的编码器? 你有没有其他的方法?
Answer 1:
上述所有的。 我用:
- 谨慎了许多
- 智能指针尽可能
- 已测试的数据结构,有很多标准库
- 单元测试所有的时间
- 内存验证工具,如MemValidator和AppVerifier的
- 祈祷,每天晚上它不会崩溃客户的网站。
其实,我只是夸大。 它不是太糟糕了和它其实不是太硬,以保持对资源的控制,如果你正确地组织你的代码。
有趣的说明。 我有一个使用DCOM和已托管和非托管模块的大型应用程序。 非托管模块通常是很难在开发过程中调试,但在客户现场表现非常好,因为很多测试在其上运行。 该管理模块有时糟糕的代码受到影响,因为垃圾收集器非常灵活,程序员们在检查资源使用懒惰。
Answer 2:
我用很多很多的断言,并建立两个“调试”版本和“释放”的版本。 我的调试版本运行速度比我的发行版本多慢得多,与所有它做检查。
我经常下运行Valgrind的 ,和我的代码具有零内存泄漏。 零。 这是一个更容易保持程序无泄漏比它采取了错误的程序和修复所有漏洞。
另外,我的代码编译没有警告,尽管我有额外的警告编译器集的事实。 有时警告是愚蠢的,但有时他们在错误点吧,我解决它,而不需要找到它的调试器。
我写纯C(我不能在此项目中使用C ++),但我在一个非常一致的方式做℃。 我有面向对象的类,与构造函数和析构函数; 我用手给他们打电话,但一致性帮助。 如果我忘了调用析构函数,Valgrind的打我的头,直到我解决它。
除了构造函数和析构函数,我写了一个自检功能,看起来在对象,并确定它是否是健全与否; 例如,如果一个文件句柄为空,但相关文件数据没有清零,表示某种错误(或者手柄一败涂地,或文件没有被打开,但在对象的字段,他们有垃圾)。 另外,我的大部分对象都必须设置为特定值(具体到每个不同的对象)一个“签名”栏。 使用对象的函数通常断言,对象是理智的。
任何时候,我malloc()
的一些记忆,我的函数填充与内存0xDC
值。 未完全初始化的结构变得很明显:计数是太大了,指针是无效的( 0xDCDCDCDC
),当我看结构在调试器很明显,它的初始化。 这比零填充内存调用时更好malloc()
(当然0xDC
填充仅在调试版本,没有必要发布版本浪费那个时候。)
任何时候我释放内存,我抹去指针。 这样一来,如果我有当代码尝试使用指针它的内存已经释放后一个愚蠢的错误,我立即得到一个空指针异常,这点我在正确的bug。 我的析构函数不采取一个指向对象,他们采取了指针的指针,并破坏对象之后揍的指针。 此外,析构函数释放他们之前擦拭自己的对象,因此,如果一些代码块有一个指针的副本,并试图使用一个对象,完整性检查断言大火瞬间。
如果任何代码编写过一个缓冲区末尾的valgrind会告诉我。 如果我没有说,我将不得不把“金丝雀”的价值观缓冲区的结束后,并有全面的检查测试。 这些金丝雀值,比如签名值,将调试集结而已,所以发行版本不会有内存膨胀。
我的单元测试的集合,当我对代码做任何大的变化,这是非常令人欣慰的运行单元测试,并有一定的信心,我没有可怕的破事。 当然,我运行调试版本的单元测试,以及发行版本,所以我所有的断言有自己的机会来发现问题。
将所有这些结构到位有点额外的努力,但它每天都不负有心人。 而且我觉得挺高兴的时候断言火灾,并指出我就在一个错误,而不必运行的bug在调试器下。 从长远来看,这只是工作量少,保持干净的东西所有的时间。
最后,我不得不说,其实我喜欢匈牙利命名法。 我在微软工作了几年前,像乔尔我学会应用匈牙利和不破的变体。 它确实做出错误的代码看起来错误 。
Answer 3:
正如有关- 你如何确保你的文件和套接字被关闭,你的锁发布,内容十分重要。 内存是不是唯一的资源,并与GC,你本身失去可靠/及时销毁。
无论是GC还是非GC自动优越。 每个人都有优点,每个人都有它的价格,一个好的程序员应该能够应付两个。
我说了这么多在回答这个问题 。
Answer 4:
我一直在使用C ++ 10年。 我已经使用C,Perl中,Lisp语言,德尔福,Visual Basic 6中,C#,Java和其他各种语言,我不记得了我的头顶。
在回答你的问题很简单: 你必须知道你在做什么 ,不是C#/ Java的多。 在超过正是滋生这种夸大其词的杰夫·阿特伍德有关的“Java学校” 。
大部分的问题,在一定意义上,是没有意义的。 该“问题”你带了只是如何硬件真正起作用的事实。 我想挑战你写的VHDL / Verilog的一个CPU和RAM和看到的东西真的有效,即使真的简化时。 你会开始意识到,C#/ Java的方式是一个抽象掩盖了硬件。
更简单的挑战将是程序从初始上电的嵌入式系统的基本操作系统; 它会告诉你你需要知道还有什么。
(我也写C#和Java)
Answer 5:
我们用C编写的嵌入式系统。 除了使用一些常用的任何编程语言或环境的技术,我们还聘请:
- 静态分析工具(如PC -皮棉 )。
- 符合MISRA-C (由静态分析工具执行)。
- 没有动态内存分配的。
Answer 6:
安德鲁的回答是一个很好的,但我也想补充纪律到列表中。 我发现足够的练习用C ++,你得到了什么是安全的,哪些是一个相当不错的手感之后乞求伶盗龙属来吃掉你。 你倾向于开发遵循安全守则时感到舒适,让你感觉神经过敏,你应该尝试,比方说,投了智能指针回到原始指针,并把它传递给别的一种编码风格。
我喜欢把它像在商店的电动工具。 你一定要始终遵循所有的安全规则,这是足够安全的,一旦你学会正确使用,并且只要使用它。 这是当你认为你可以放弃,你受伤的护目镜。
Answer 7:
我已经做了C ++和C#和我没有看到所有关于托管代码的炒作。
哦,对了,还有就是内存垃圾收集,这是有帮助的......除非你在过程中的C ++使用普通的旧指针避免,如果你只使用smart_pointers,那么你就没有那么多的问题。
但后来我想知道......你做垃圾回收器可以保护你免受:
- 保持数据库连接打开?
- 保持上的文件锁吗?
- ...
没有比内存管理更多资源的管理。 好事是C ++是你学习什么快速资源管理和RAII手段,使之成为一种条件反射:
- 如果我想要一个指针,我想一个auto_ptr,一个shared_ptr或weak_ptr的
- 如果我想要一个数据库连接,我希望对象的“连接”
- 如果我打开一个文件,我想一个对象“文件”
- ...
至于缓冲区溢出,那么,它不是像我们所使用的char *类型和size_t无处不在。 我们确实有一些东西称之为“串”,“iostream的”,当然还有已经提到矢量::在其释放我们从这些限制的方法。
经测试库(STL,升压)都不错,使用起来并获得更多的功能性问题。
Answer 8:
除了很多在这里给出的很好的提示,我最重要的工具就是干 - 不要重复自己。 我不扩散容易出错代码(例如,用于处理内存分配使用malloc()和free())都在我的代码库。 我在我的代码只有一个单一的位置,其中malloc和free被调用。 这是在包装功能MemoryAlloc和MemoryFree。
还有所有的参数检查,并且通常是作为调用函数malloc各地重复样板代码最初的错误处理。 此外,它使任何与需要修改只有一个位置,用简单的调试检查,就像数到malloc和free的成功呼叫开始,在程序终止,这两个数字都是平等的,达到各类安全扩展checkings的验证。
有时候,当我读到一个问题在这里像“我总是要确保函数strncpy终止字符串,是有其他选择吗?”
strncpy(dst, src, n);
dst[n-1] = '\0';
随后的讨论天,我一直在想,如果提取重复功能整合到功能的艺术是较高的编程是不再编程讲座教授的一项失传的艺术。
char *my_strncpy (dst, src, n)
{
assert((dst != NULL) && (src != NULL) && (n > 0));
strncpy(dst, src, n);
dst[n-1] = '\0';
return dst;
}
重复代码的主要问题就解决了 - 现在让我们觉得如果真的strncpy()函数是这份工作的合适工具。 性能? 过早的优化! 和一个单一的位置在一开始,它被证明后成为瓶颈。
Answer 9:
C ++拥有所有你所提到的功能。
有内存管理。 您可以使用智能指针非常精确的控制。 还是有几个垃圾收集器可用,虽然它们不是标准的一部分(但大多数情况下,智能指针是绰绰有余)。
C ++是一种强类型语言。 就像C#。
我们正在使用的缓冲区。 您可以选择使用边界检查接口的版本。 但是,如果你知道,有没有问题,那么你可以自由地使用该接口的选中版本。
比较在()(检查)方法来操作符[](未选中)。
是的,我们使用单元测试。 就像你应该使用C#。
是的,我们是谨慎的程序员。 就像你应该在C#。 唯一的区别是缺陷是两种语言的不同。