【转】关于C的未定义行为
关于C的未定义行为
对于C的初学者来说,被要求做下面的这种题目真的是脑残的不能再脑残的行为。但是很多C初级教程——居然都有这样的题。
最典型的例子就是
a+=a++;
这种情况下,a最后到底等于多少了?
编译器应该如何理解a+=a++呢?首先是展开,a=a+a++;
然后分别计算a和a++的值,把它们相加,然后把结果赋给a。
但是这里有一个问题,就是执行完a++之后,a++的值等于a本身的值,但是a的值却变成了a+1。
所以关键是处理顺序。
比如说int a=3;
如果编译器先计算赋值号+=左边a的值为3,然后计算右边a++的值为3,同时a变为4。
然后计算3+3=6,赋给a,那么a现在的值就是6。
如果编译器先计算赋值号右边的a++,得到的结果为3,同时a变为4,然后计算左边a=4。
接着计算4+3=7,于是7这个数被赋值给了a。
也就是说,不同的理解方法,在这个例子里面居然会得到不同的答案?
我为什么要用居然?难道这个结果不是不可思议的么?一样的表达式,只不过编译器不一样,就得出了不同的结果,这真是个悲剧啊。
难道没有什么标准要求编译器采用相同的理解模式么?C语言的标准遵从ANSI C标准。但是很不幸,ANSI C标准里面,并没有关于遇到这种情况应如何处理的规定,反而是指出,编译器你看着办吧。
这就是C语言的“未定义行为”。
话说我只是有在用一个GCD函数的时候被某大神狠狠的吐槽了,这个GCD函数如下:
int GCD(int a,int b){
while (a %= b ^= a ^= b ^= a);
return b;
}
就像上面分析的那样,这段程序在编译过程中,会出现什么顺序,这也是标准里面没有规定的,属于未定义行为。
所以用这个函数并不一定能保证得到正确的结果。
这种事情嘛。。。既然是交给编译器的。
我想这个实际上应该是为了代码优化。众所周知,C是一个十分注重效率的语言,并且有那种为了效率放弃一切的感觉,不评价这个好不好,反正人家在最受欢迎的语言排行榜第一位的宝座上坐了不知道多少年了。
给编译器更大的自由,编译器就能更好的优化生成的二进制代码。
还有其他的方面,比如越界数组。
就像这样
char str1[]="myworld";
str1[18]='\0';
数组str1哪里来的第19项啊!!!这种东西居然能通过编译!!!
使用越界数组也是C的一个“未定义行为”。C的标准没有规定编译器在碰到这种情况应该怎么做。这个时候编译器的想法应该是——多一事不如少一事,我也不检查这里到底是不是这个数组的范围了,反正你都叫我写了,我就写吧。
还有一个典型的操作就是允许一个随便指的指针的读写。
比如我申请了一个动态区域,然后释放掉了:
int *p;
p=(int *)malloc(4*sizeof(int));
/*各种对p的操作*/
free(p);
p[0]=0;
printf("%d%d%d%d",p[0],p[1],p[2],p[3]);
毫不夸张的说,我自己的程序多次死在这种地方,就是free以后再print。。。
再比如:
int *p;
p=0x1e642a80;
p[8]=24;
这种指针操作居然也给通过????
更要命的是,上面的这些都属于C的“未定义行为"就是说,虽然这些操作可以进行,但是编译器并不保证执行结果。
就是说这种东西不但不报错给通过了,而且还不按照我们想象的样子执行,而是由着编译器的性子随便来?
从这个角度看,真的是太苦逼了。
我还碰到过一段脑残代码,类似这样:
char tips[]="No";
if(condition){
strcpy(tips,"Yes");
}
对这种东西。。。。。呵呵。在Windows下运行就等着被中断吧。。
还有使用未初始化的变量也是一种“未定义行为”,比如:
int x;
printf("%d",x);
通常你也不知道你会在屏幕上看到什么。。。。
GCC的第一版编译器在碰到这种情况的时候,会在你屏幕上开始一个小游戏。(这是开发组满满的恶意啊!!嗯,一定是!!)
C的变量并不会在声明时(或第一次使用前)被初始化,这个特点饱受人们诟病。
不过ANSI C本身肯定是想通过省略这些初始化操作,来提升一点运行速度。
毕竟要初始化一个大数组或是用malloc分配的一大堆空间,还是挺费力的。。。
不过好处是……
不检查数组边界,不检查指针指向地址的情况,不检查强制类型转换是否可以进行。全靠程序员程序的自觉,这点使得C的代码效率会变得很高。
所以说,C虽然大量用于需要程序安全的场合,但是由于“未定义行为”的存在——C绝对不是一个安全的语言!!
但是更多的情况是,之所以这样,所以才会更希望用C来实现。
还有一点需要说明的是。。
最开始那段:
int a=3;
a+=a++;
几乎所有的现代编译器的结果都为7。
那个GCD的一行算法,几乎所有的现代编译器都能正常运行。
虽然是“未定义算法”,这个也算默默的达成了一种协议了吧。
虽然,使用它们仍然是危险的。
【转】关于C的未定义行为的更多相关文章
- IE10,11下_doPostBack未定义错误的解决方法
出现的原因 .NET2.0和.NET4.0一起发布的浏览器定义文件中有一个错误,它们保存相当一部分浏览器版本的定义.但是浏览器的有些版本(比如IE10,11)则不再在这个范围之内.因此,ASP.NET ...
- js未定义判断
if (typeof(homeType) == 'undefined') { //..... //..... } typeof函数判断,如果未定义的就会返回undefined,注意undefined ...
- C++中的"未定义的行为"
2.1 位运算 位运算的运算对象是整数类型的,并且把运算对象看成是一个二进制位的集合.运算对象可以是带符号也可以是无符号.如果是带符号且值为负,那么位运算如何处理运算对象的符号位依赖于机器.而且此时的 ...
- IE8、IE9浏览器下报:JSON未定义 解决方法
IE8.IE9浏览器下报:JSON未定义的问题 解决方法: 在jsp中引入如下代码 <!-- 解决 IE8.IE9 下显示混乱的问题--><% String browserStrin ...
- IIS7下ajax报未定义错误
项目之前在iis6环境下运行的很好,今天在WIN7下发布,结果居然报对象未定义错误,经过个把小时折腾,终于弄清楚原委. 在web.config中关于AjaxPro的设置,在IIS7.0(WIN7中使用 ...
- 未定义标识符string
“未定义标识符string” 解决方法: 头文件加上 #include <iostream>using namespace std; string是标准库的,要加std::string, ...
- 【caffe】未定义函数或变量caffe_
@tag: caffe windows10上配置好caffe后(配置了matlab接口),运行caffe-master/matlab/demo/classification_demo.m报错,提示: ...
- gcc编译时对'xxxx'未定义的引用问题
gcc编译时对’xxxx’未定义的引用问题 gcc编译时对’xxxx’未定义的引用问题 原因 解决办法 gcc 依赖顺序问题 在使用gcc编译的时候有时候会碰到这样的问题,编译为.o(obj) 文件没 ...
- JavaScript 运行时错误: 无法获取未定义或 null 一种解决方案
脚本是肯定没有错误的!! 引用了高版本的jquery jquery-1.10.1.min.js 但在ie10下面就是报错 "JavaScript 运行时错误: 无法获取未定义或 null & ...
- C#引用COM对象,报错:《类型 *** 未定义构造函数, 无法嵌入互操作类型 *** 。请改用适用的接口》的解决办法。
错误信息: 1.类型“SQLDMO.BackupClass”未定义构造函数 2.无法嵌入互操作类型“SQLDMO.BackupClass”.请改用适用的接口. 代码如下: ...
随机推荐
- OpenCV学习(一)
环境:OpenCV 2.4.4 VS2010 第一个Demo,显示一张图片 #include "opencv2/highgui/highgui.hpp" int main( int ...
- 一个分门别列介绍JavaScript各种常用工具的脑图
博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:一个分门别列介绍JavaScript各种常用工具的脑图.
- Quartz定时任务学习(五)触发器
顾名思义,Trigger(触发器)的责任就是触发一个 Job 去执行.当用 Scheduler 注册一个 Job 的时候要创建一个 Trigger 与这个 Job 相关联.Quartz 提供了四种类型 ...
- SpringNote02.Blog系统迁移到Linux下
基于SpringMVC-Hibernate的博客系统还在继续开发中 . 项目地址:https://github.com/defshine/SpringBlog 整个项目迁移到linux下开发,安装in ...
- Matlab mex编程
经典教程:mex 编程 http://classes.soe.ucsc.edu/ee264/Fall11/cmex.pdf Matlab 快速编程: http://www.ee.columbia.ed ...
- GDB 调试程序系列
http://blog.csdn.net/haoel/article/category/9197
- myeclipse2014新感悟
部署有两种方式:1.直接把文件拷贝到 tomcat下的webroot文件夹下 2.myeclipse软件内部点击“deploy”部署 →点击add→tomcat下的webroot文件夹下 点击完“运行 ...
- 一次向svn中增加所有新增文件 svn add all new files
svn st | grep '^\?' | tr '^\?' ' ' | sed 's/[ ]*//' | sed 's/[ ]/\\ /g' | xargs svn add
- 如何正确合理的建立MYSQL数据库索引
索引是快速搜索的关键.MySQL索引的建立对于MySQL的高效运行是很重要的.下面介绍几种常见的MySQL索引类型. 在数据库表中,对字段建立索引可以大大提高查询速度.假如我们创建了一个 mytabl ...
- IT行业智力测试
1.有10筐苹果,其中有1筐是次品,正品苹果每个10两,次品苹果每个9两,现有一称,问怎么一次称出次品是哪筐? 2.有甲.乙.丙.丁四个人,要在夜里过一座桥.他们通过这座桥分别需要耗时1.2.5.10 ...