【转】关于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”.请改用适用的接口. 代码如下: ...
随机推荐
- HTML5与CSS3权威指南.pdf4
拖放API HTML5实现了直接拖放操作API,简化HTML4利用mousedown.mousemove等事件实现的操作 实现拖放的步骤 1要将被拖动元素的draggable属性设置为true,img ...
- use of undeclared identifier *** , did you mean ***. in xcode
A property is not the same thing os a instance variable, you should read a little bit of them, there ...
- hdoj 2063 过山车【匈牙利算法+邻接矩阵or邻接表】
过山车 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...
- 校友信息管理&SNS互动平台之前言、目录及说明
开篇 刚刚主持完成了某985大学(为了保密和避免广告嫌疑,此处不指出具体大学的名称)的“校友信息管理系统&SNS互动平台”大型项目,本着总结经验,技术共享的原则,本系列文章将全面介绍该项目的需 ...
- http协议和web本质
转载:http://www.cnblogs.com/dinglang/archive/2012/02/11/2346430.html http协议和web本质 当你在浏览器地址栏敲入“http://w ...
- Oracle的SCN与检查点机制
Oracle的SCN与检查点机制 SCN在Oracle的文档上以多种形式出现,一种是System Change Number,另一种是System Commit Number,在大多数情况下,Syst ...
- ios开发所有的iCON 的大小
新浪微博: 16x16png,80x80png,120x120png qq开放平台: 16*16png 20k以内 512*512jpg或者png 200以内 微信开放平台: 28x28 png,10 ...
- Java-工厂设计模式
引言: 工厂设计模式分为三种: 简单工厂 工厂方法 抽象工厂 联想四种情况 (1).还没有工厂时代:假如还没有工业革命,如果一个客户要一件产品,一般的做法是客户去创建一件产品,然后拿来用. (2).简 ...
- iOS判断iPhone型号
链接: http://stackoverflow.com/questions/11197509/ios-how-to-get-device-make-and-modelhttp://stackover ...
- BTrace: DTrace for Java2
BTrace: DTrace for Java… ish 时间 2012-04-24 16:17:55 dtrace.org 原文 http://dtrace.org/blogs/ahl/2012 ...