当前位置:  开发笔记 > 编程语言 > 正文

未定义的行为值得吗?

如何解决《未定义的行为值得吗?》经验,为你挑选了4个好方法。

由于未定义的行为,许多不好的事情发生并且继续发生(或者不知道,谁知道,任何事情都可能发生).据我所知,这是为了让编译器进行优化留下一些摆动空间,也可能使C++更容易移植到不同的平台和架构.然而,由未定义的行为引起的问题似乎太大而无法通过这些论证来证明.未定义行为的其他参数是什么?如果没有,为什么还存在未定义的行为?

编辑为我的问题添加一些动机:由于使用较少C++的几个糟糕经历 - 狡猾的同事,我已经习惯了使我的代码尽可能安全.断言每一个论点,严谨的正确性以及类似的东西.我试图离开,因为小房间可能以错误的方式使用我的代码,因为经验表明,如果有漏洞,人们会使用它们,然后他们会打电话给我,说我的代码很糟糕.我认为让我的代码尽可能安全是一种好的做法.这就是为什么我不明白为什么存在未定义的行为.有人可以给我一个未定义行为的例子,这些行为在运行时或编译时无法检测到而没有相当大的开销吗?



1> Carson Myers..:

我对未定义行为的看法是这样的:

该标准定义了语言的使用方式,以及在以正确方式使用时应该如何做出反应.但是,要涵盖每个功能的每个可能用途都需要做很多工作,所以标准就是这样做.

但是,在编译器实现中,您不能只是"保留它",代码必须转换为机器指令,并且您不能只留下空白点.在许多情况下,编译器可能会抛出错误,但这并不总是可行的:在某些情况下需要额外的工作来检查程序员是否做错了(例如:调用析构函数两次 - 来检测这个,编译器必须计算已调用某些函数的次数,或添加额外的状态或其他内容).因此,如果标准没有定义它,并且编译器只是让它发生,那么有时可能会发生诙谐的事情,如果你不幸的话.


我不打算假装成为所有C++内部的专家,但C++程序比java更接近金属的事实可能是一个很大的原因.语言规范必须为在完全不同的硬件上实现提供空间.
我并不反对提供两者的想法,我认为大多数开发人员不需要速度而是使用习惯的方式来访问索引`[]`,这也是不安全的事实...因此我会我更喜欢安全的惯用方式,而对于那些真正需要速度的人则更不安全.

2> Matthieu M...:

我认为关注的核心来自于C/C++的速度哲学.

这些语言是在原始功耗稀疏的情况下创建的,您需要获得所有优化才能获得可用的功能.

指定如何处理UB意味着首先检测它,然后当然指定适当的处理.然而,检测它是违反语言的速度第一哲学!

今天,我们还需要快速的节目吗?是的,对于我们这些使用非常有限的资源(嵌入式系统)或非常严格的约束(响应时间或每秒事务数)工作的人来说,我们确实需要尽可能地挤出来.

我知道这个座右铭会在这个问题上投入更多硬件.我有一个应用程序,我工作:

预计回答的时间?不到100毫秒,中间有数据库调用(感谢memcached).

每秒交易次数?平均1200,峰值1500/1700.

它运行在大约40个怪物上:8个双核opteron(2800MHz)和32GB RAM.此时使用更多硬件变得"更快"变得困难,因此我们需要优化代码和允许它的语言(我们确实限制在那里抛出汇编代码).

我必须说,无论如何我对UB并不在意.如果你达到程序调用UB的程度那么它需要修复实际发生的任何行为.当然,如果立即报告它将更容易修复它们:这就是调试版本的用途.

所以也许我们应该学会使用这种语言,而不是专注于UB:

不要使用未经检查的电话

(对于专家)不要使用未经检查的电话

(对于大师)你确定你真的需要一个未经检查的电话吗?

一切都突然变得更好:)



3> 小智..:

问题不是由未定义的行为引起的,它们是由编写导致它的代码引起的.答案很简单 - 不要写那种代码 - 不这样做并不完全是火箭科学.

至于:

在运行时或编译时无法检测到的未定义行为的示例,没有相当大的开销

现实世界的问题:

int * p = new int;
// call loads of stuff which may create an alias to p called q
delete p;

// call more stuff, somewhere in which you do:
delete q;

在编译时检测到这一点是不可能的.在运行时它只是非常困难,并且需要内存分配系统进行更多的簿记(即更慢并占用更多内存),而不是简单地说第二次删除是未定义的.如果您不喜欢这样,也许C++不是您的语言 - 为什么不切换到java?


不,这不是政治,而是工程.并非一切都可以在合理的条件下进行检查 假设取消引用无效指针会从未定义的行为更改为已知错误.然后标准将要求**所有实现执行围绕每个指针解引用的检查以产生该错误.而且我不只是讨论取消引用null,而是**所有**指针.每当看到`*p`时,你必须验证`p`是指向有效内存块的指针,要求运行时跟踪该检查的所有已分配内存.
关于跟踪错误而丢失的金额:从标准角度来看,未定义的行为并不意味着必须在特定的实现中对其进行定义.许多实现在调试版本中都有特定的代码来诊断错误.不同的实施将提供更大的诊断支持,以尝试占领更大的市场.没有标准化意味着相同的实现可以在调试模式下对迭代器进行边界检查,同​​时具有快速未经检查的发布版本.
@ Space_C0wb0y它的行为尚未定义,因为它对委员会不方便,它会损害语言的可移植性或使编译器难以编写.我的意思是......这不是一个特征,缺乏某些不需要或不可信的功能.
然而,如果一个"特征"导致如此多的浪费工作时间,那么你就不得不想知道为什么它不会被删除.必须有一个非常令人印象深刻的好处来证明这一切.
您的答案来自程序员的角度,而问题则来自语言设计的角度.问:"为什么这个坏事存在?" 答:"避免它"

4> UncleZeiv..:

未定义行为的主要来源是指针,这就是为什么C和C++有很多未定义的行为.

考虑以下代码:

char * r = 0x012345ff;
std::cout << r;

这段代码看起来很糟糕,但它应该发出错误吗?如果该地址确实可读,即它是我以某种方式获得的值(可能是设备地址等),该怎么办?

在这种情况下,没有办法知道操作是否合法,如果不合法,它的行为确实是不可预测的.

除此之外:一般来说,C++在设计时考虑了"零开销规则"(参见C++的设计和演变),因此它不可能对实现检查角落情况等施加任何负担.您应该始终保持请记住,这种语言的设计确实不仅适用于桌面,也适用于资源有限的嵌入式系统.

推荐阅读
coco2冰冰
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有