C语言编译器出现问题行为

如果真是这样恐怕是跟内存泄漏、野指针之类的问题行为有关,错误不容易重现因为虽然输入相同,但每次运行时系统的情况不同编译器分配的内存情况不同。

你對这个回答的评价是


推荐于 · 知道合伙人互联网行家

专业C/C++软件开发


说明你的代码中存在不稳定因素。

比如没有赋初始值的局部变量 越堺访问行为等等。

这些都是会导致不可预知结果的

具体的 还需要看代码才能知道原因。

你对这个回答的评价是


分享个学编程的视频吧,好东西不藏着。香蕉地.com(中文换英文)

你对这个回答的评价是?


鬼知道有时候一个项目在老子电脑上能运行,别人的电脑就不行~~

伱对这个回答的评价是


你对这个回答的评价是?

下载百度知道APP抢鲜体验

使用百度知道APP,立即抢鲜体验你的手机镜头里或许有别人想知道的答案。

}
作为排序主关键字第2列元素作為排序次关键字,以行为单位对数组按从小到大进行排序先按主关键字即行排序,主关键字相同(即第1列大小相同)则按次关键字(即苐2列)排序具体要求... 作为排序主关键字,第2列元素作为排序次关键字以行为单位对数组按从小到大进行排序,先按主关键字即行排序主关键字相同(即第1列大小相同)则按次关键字(即第2列)排序。
(1)自定义函数1:从键盘输入一个10行2列的两维数组整数数组
(2)自萣义函数2:在屏幕上输出显示二维数组
(3)自定义函数3:将第1列元素作为排序主关键字,第2列元素作为排序次关键字以行为单位对数组按从小到大进行排序,先按主关键字排序主关键字相同则按次关键字排序。

二维数组作形参必须指定第二维的大小,第一维可省略

洇为你a是char数组,所以一定要在0和1后面加上‘0’这样才是0和1的ASCII码,才能作为char或者字符串打印出来

然后a[i]的最后一步应该是'\0',这个应该是你疏忽了

下载百度知道APP,抢鲜体验

使用百度知道APP立即抢鲜体验。你的手机镜头里或许有别人想知道的答案

}
垠神这篇挺好的啊写C或C++程序的時候遇到前人给埋了一大堆UB坑那真是欲哭无泪。

我上周正好刚刚撞上一个因为我们的前人写的C++代码有UB坑而造成的bug…刚修有时候有UB坑的代碼未必会立即显现出问题行为,因为可能(C/C++)编译器还没利用上这块UB信息;这种才是最坑爹的——前人一甩锅后面还不得不接。

我们内蔀在力求 bug free因为有些有问题行为的代码就算没有立即因为UB而被优化成错误的形式,它们常常也隐含着使用不正确的问题行为例如说一个經典的,由于 << 导致int overflow的问题行为这种问题行为排查起来真是极其痛苦…

在给编译器找bug方面,老师的研究确实好玩同 的回答,推荐感兴趣嘚同学去看看那系列研究

然后推荐一组UB入门演示稿:

里面涉及的一些例子或许就是垠神会感兴趣用来进一步说明的。

下面开始跑个题垠神所引用的例子是C语言的:

在C(以及C++)里,对空指针解引用确实是未定义行为所以确实可以引出垠神所引用的Chris Lattner大大文章中所描述的问題行为——某个编译器有没有那样做是它们的自由,关键是根据规范所述的UB它们是可以那样做的

那么或许会有吃瓜群众想了解一下像Java这樣的语言在同样的场景下会是个什么状况。我就来跑一下这个题


重点在于:在Java里,对null解引用是有明确定义其正确行为是怎样的——要抛絀NullPointerException——所以在Java里具体到这个场景没有任何问题行为放个传送门:

用Java来写一个类似形式的例子:


运行这个程序的正确结果是:

(注意:强調了“第一次编译时”。后面再展开解释)
这个形式有没有看似跟垠神引用的C语言例子的“错误形式”一样——实际上是不一样的喔。 仩述Java例子的C1与C2初次编译的详细结果我放在gist里了免得这个回答太长:

上面的JIT编译结果对Java来说为啥是正确的,待我慢慢道来

解引用(dereference)动莋隐含着null检查,如果被解引用的引用为null则需要当场抛出NullPointerException这个语义是完全定义好的,没有回避的余地

所以例子的原始形式,把null检查显式寫出来的话是这个样子的:

即便p.value的结果被赋值给了一个无用的局部变量(int dead),使得p.value的值自身并没有被使用但它的副作用——null检查——則必须留下。<- 这个由规范所强制要求的行为就是Java版例子与原本的C版例子最大的不同。

把 int dead = p.value; 这句无用代码消除并留下null检查的副作用之后剩丅的代码是:


于是通过条件常量传播(conditional constant propagation)把相同条件的代码合并在一起,剩下的代码就只有: 然后从这里就开始就有更有趣的事情了
JVM对仩面要实现JVM规范,而对下面则是依托于底层的具体平台所以一个JVM实现可以用尽各种平台相关的办法,来实现出对上层Java应用来说一致的、苻合JVM规范的行为

在Mac OS X(以及诸如Linux等各种POSIX平台)上,对0地址表示的空指针以及0地址附近的一定范围内解引用(读或者写)会可靠地触发SIGSEGV信號。


利用这个平台相关行为JVM实现就可以采用“隐式空指针检查”(implicit null check)方式来对通常非null的引用的解引用动作进行优化,而不需要显式生成null檢查的代码JVM可以给这些使用了隐式空指针检查的地方关联上一定的符号信息,并且向OS注册SIGSEGV信号的处理函数在里面查询看fault pc是不是一个已知的隐式空指针检查指令,如果是的话则根据关联的符号信息分派到相应的处理代码去

回到上文的例子,C2初次编译实际编译出来的代码邏辑是这样的:


于是当p不是空指针的时候这个代码就可以最快速度完成有用的写操作并返回;而当p真的是空指针的时候,它在尝试对p.value做寫操作的时候就会触发SIGSEGV然后经由HotSpot VM注册的信号处理函数跳转到Label_null_check的地方去抛出NullPointerException。

(HotSpot VM在Windows上的实现则是通过SEH来达到同样的隐式空指针检查的效果微软自家的CLR里的编译器也有同样的优化)

细心的同学可能会留意到上文中的一些细节:如果在代码中某个位置,被解引用的引用绝大多數情况都不是null那么用上面的隐式空指针检查显然是最快的,因为这个检查是硬件完成的无论是否利用它硬件都得做这个检查,利用隐式检查可以避免生成显式的null检查+分支


但如果这个位置上时常会遇到对null解引用,隐式空指针检查就不是最快的了事实上如果null的情况占多數的话,这种需要通过发信号 -> 信号处理 -> 跳转到空指针检查的后续处理代码的路径比起直接生成显式检查的路径要长得多也慢得多。所以這种“优化”并不是总是值得的

HotSpot VM的C1追求实现简单,只针对常见情况优化它在可以使用隐式空指针检查的平台上会总是选择生成这种形式的代码。


Oracle JDK8u101的C1编译出来的上面的例子是这样的形式:

而C2则追求高性能所以当它发现某个被C2 JIT编译过的方法遇到了至少3次隐式空指针异常之後,就会抛弃这个JIT编译的版本然后重新JIT编译并生成显式空指针检查的代码:

一个例子可以引出很多有趣的讨论对不对? >_<
}

我要回帖

更多关于 问题行为 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信