C语言中史上最愚蠢的Bug

C语言中史上最愚蠢的Bug

本文来自“The most stupid C bug ever”,很有意思,分享给大家。我相信这样的bug,就算你是高手你也会犯的。你来看看作者犯的这个Bug吧。。

首先,作者想用一段程序来创建一个文件,如果有文件名的话,就创建真正的文件,如果没有的话,就调用?tmpfile()?创建临时文件。他这段程序就是HTTP下载的C程序。code==200就是HTTP的返回码。


1

2

3

4

else if (code == 200) {     // Downloading whole file

    /* Write new file (plus allow reading once we finish) */

    g = fname ? fopen(fname, "w+") : tmpfile();

}

但是这个程序,只能在Unix/Linux下工作,因为 Microsoft 的?tmpfile()的实现?居然选择了 C:\ 作为临时文件的存放目录,这对于那些没有管理员权限的人来说就出大问题了,在Windows 7下,就算你有管理员权限也会有问题。所以,上面的程序在Windows平台下需要用不同的方式来处理,不能直接使用Windows的tmpfile()函数。

于是作者就先把这个问题记下来,在注释中写下了FIXME:


1

2

3

4

5

6

7

else if (code == 200) {     // Downloading whole file

    /* Write new file (plus allow reading once we finish) */

    // FIXME Win32 native version fails here because

    //   Microsoft‘s version of tmpfile() creates the file in C:\

    g = fname ? fopen(fname, "w+") : tmpfile();

}

然后,作者觉得需要写一个跨平台的编译:


1

2

3

4

5

6

7

FILE * tmpfile ( void ) {

#ifndef _WIN32

    return tmpfile();

#else

    //code for Windows;

#endif

}

然后,作者觉得这样实现很不好,会发现名字冲突,因为这样一来这个函数太难看了。于是他重构了一下他的代码——写一个自己实现的tmpfile() – w32_tmpfile,然后,在Windows 下用宏定义来重命名这个函数为tmpfile()。(陈皓注:这种用法是比较标准的跨平台代码的写法)


1

2

3

4

5

6

7

#ifdef _WIN32

  #define tmpfile w32_tmpfile

#endif

FILE * w32_tmpfile ( void ) {

    //code for Windows;

}

搞定!编译程序,运行。靠!居然没有调用到我的w32_tmpfile(),什么问题?调试,单步跟踪,果然没有调用到!难道是问号表达式有问题?改成if – else 语句,好了!


1

2

3

4

5

if(NULL != fname) {

    g = fopen(fname, "w+");

} else {

    g = tmpfile();

}

问号表达式不应该有问题吧,难道我们的宏对问号表达式不起作用,这难道是编译器的预编译的一个bug?作者怀疑到。

现在我们把所有的代码连在一起看,并比较一下:

能正常工作的代码

能工作的代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

#ifdef _WIN32

#  define tmpfile w32_tmpfile

#endif

FILE * w32_tmpfile ( void ) {

    code for Windows;

}

else if (code == 200) {     // Downloading whole file

    /* Write new file (plus allow reading once we finish) */

    // FIXME Win32 native version fails here because

    //     Microsoft‘s version of tmpfile() creates the file in C:\

    //g = fname ? fopen(fname, "w+") : tmpfile();

    if(NULL != fname) {

        g = fopen(fname, "w+");

    } else {

        g = tmpfile();

    }

}

不能正常工作的代码

不能工作的代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

#ifdef _WIN32

#  define tmpfile w32_tmpfile

#endif

FILE * w32_tmpfile ( void ) {

    code for Windows;

}

else if (code == 200) {     // Downloading whole file

    /* Write new file (plus allow reading once we finish) */

    // FIXME Win32 native version fails here because

    //    Microsoft‘s version of tmpfile() creates the file in C:\

    g = fname ? fopen(fname, "w+") : tmpfile();

}

也许你在一开始就看到了这个bug,但是作者没有。所有的问题都出在注释上:


1

2

3

/* Write new file (plus allow reading once we finish) */

// FIXME Win32 native version fails here because

//     Microsoft‘s version of tmpfile() creates the file in C:\

你看到了最后那个C:\吗?在C中,“\” 代表此行没有结束,于是,后面的代码也成了注释。这就是这个bug的真正原因

而之所以改成if-else能工作的原因是因为作者注释了老的问号表达式的代码,所以,那段能工作的代码成了:


1

2

3

4

5

6

7

/* Write new file (plus allow reading once we finish) */

// FIXME Win32 native version fails here because Microsoft‘s version of tmpfile() creates the file in C:    //g = fname ? fopen(fname, "w+") : tmpfile();

if(NULL != fname) {

    g = fopen(fname, "w+");

} else {

    g = tmpfile();

}

我相信,当作者找到这个问题的原因后,一定会骂一句“妈的”!我也相信,这个bug花费了作者很多时间!

最后,我也share一个我以前犯的一个错。

我有一个小函数,需要传入一个int* pInt的类型,然后我需要在我的代码里 把这个int* pInt作除数。于是我的代码成了下面的这个样子:

float result = num/*pInt;
….

/*  some comments */

-x<10 ? f(result):f(-result);

因为我在我当时用vi编写代码,所以没有语法高亮,而我的程序都编译通过了,但是却出现了很奇怪的事。我也不知道,用gdb调式的时候,发现有些语句直接就过了。这个问题让我花了很多时间,最后发现问题原来是没有空格导致的,TNND,下面我用代码高亮的插件来显示上面的代码,


1

2

3

4

5

6

float result = num/*pInt;

....

/*  some comments */

-x<10 ? f(result):f(-result);

Holly Shit!  我的代码成了:


1

float result = num-x<10 ? f(result):f(-result);

妈的!我的这个错误在愚蠢程度上和上面那个作者出的错误有一拼。

(全文完)

时间: 2024-10-12 13:57:45

C语言中史上最愚蠢的Bug的相关文章

C语言中整数上溢,浮点数上溢,浮点数下溢问题

1.整数上溢的问题:在我的系统中int最大值为2147483647 ,如果出现整数上溢,运行结果为: 如果是无符号整数,出现整数上溢,运行结果为: 2.如果是浮点型,例如系统的最大值是3.4e38 ,如果*100,就会发生浮点数的上溢.本机运行结果为: 如果出现下溢,本机运行结果为: 出现NaN

D语言中使用UnCompress类出现问题 Bug

UnCompress类对zip解压做了进一步封装,然而这个做得并不完善.使用它来解压数据时会出现不完整的情况. if(encoding == "gzip") { UnCompress decmp = new UnCompress; scope(exit)delete decmp; auto tmp = cast(char[])decmp.uncompress(buffer); //auto tmp = cast(char[])uncompress(buffer,0,47); //转换数

javascript语言中的毒瘤(上)

javascript语言中的毒瘤(上) 最近翻了<javascript语言精髓>,对js有了更进一步的了解,特别是js的糟糕特性,下面,结合书中的要点,给大家分享一下js的几个糟糕特性. 全局变量 全局变量实在所有作用域均可以访问的变量,在一些小型项目中全局变量给我们获取和使用函数,数据等提供了灵活,便捷:但是随着程序的越来越庞大,全局变量就变得越来越难维护:(因为一个变量可能会在很多地方被修改,还有可能被覆盖),一旦出现问题也很难定位和调试. Js的问题不仅在于它容许使用全局变量,而且在于它

Android 史上最强多语言国际化,不仅第一次会跟随系统,而且会保存用户的语言设置

1.我等屌丝喜欢简单粗暴,首先来一幅图 哥们我是大陆人,当然默认语言是 中文简体,但是我刚刚切换成了繁体了 2.看下配置文件,按照这个格式 ,看图吧,简单粗暴,别问为什么,你就按照这样写,如果你想知道为什么这样写,以及详细的步骤,请百度 :  Android 多语言 阿拉伯语 ar, 德语 de ,英语 en ,西班牙 es, 法语 fr ,日语 ja ,韩语 ko ,葡萄牙 pt , 我大天朝 和 港澳台 就略过了,不解释, 3.注意下所有语言配置文件  string.xml 里面的文本格式是

史上最清晰的JavaScript的原型讲解

一说起JavaScript就要谈的几个问题,原型就是其中的一个.说了句大话,史上最清晰.本来是想按照大纲式的行文写一下,但写到后边感觉其实就一个概念,没有什么条理性,所以下面就简单按照概念解释的模式谈下这个问题. 1.JavaScript的原型是什么? 原型,首先他是个对象.和在以对象为核心的JavaScript这门语言中的其他普通对象来说一样,只不过他的角色有点特殊.但首先要明白他就是一个对象,是一个无序的属性和值的序列对. 2.谁会具有原型这个对象? 所有的对象(包括函数这个对象)在默认的情

史上最难PHPer笔试题,40分就能月薪过万!附答案

请批判性的学习,欢迎大牛指正错误 1.有关PHP字符串的说法,不对的是:A.如果一个脚本的编码是 ISO-8859-1,则其中的字符串也会被编码为 ISO-8859-1.B.PHP的字符串在内部是字节组成的数组,用花括号访问或修改字符串对多字节字符集很不安全.C.substr().strpos().strlen().htmlentities() 处理字符串时依据的编码方式是相同的.D.一个布尔值 Boolean 的 true 被转换成 string 的 " 1 ",false 被转换成

史上最全开源大数据工具汇总

摘要 史上最全的开源大数据工具,非常实用,请务必收藏! 史上最全的开源大数据工具,非常实用,请务必收藏! 查询引擎 Phoenix Salesforce公司出品,Apache HBase之上的一个SQL中间层,完全使用Java编写 Stinger 原叫Tez,下一代Hive, Hortonworks主导开发,运行在YARN上的DAG计算框架 Presto Facebook开源 Spark SQL Spark上的SQL执行引擎 Pig 基于Hadoop MapReduce的脚本语言 Clouder

强烈推荐:Android史上最强大的自定义任务软件Tasker

强烈推荐:Android史上最强大的自定义任务软件Taskerhttp://bbs.mumayi.com/thread-28387-1-1.html(出处: 木蚂蚁手机乐园) Android上的Tasker绝对称得上是Android系统的神器之一,与Auto Memory Manager不同,Tasker不是加速型的软件,而是系统增强型的软件,由于有众多系统状态可控制,故使得Tasker一跃成为Android系统中最闪亮的明星.但Tasker也无疑是最难使用的软件,由于可以控制的地方太多,反而让

史上最强大的 Vim 代码补全引擎:YouCompleteMe

引言 自己之前Vim的代码补全插件都是使用 ctags + autotag + taglist + omnicppcompele 的组合,而且很多时候都没有提示或提示补全,而且每次打开一个新文件文件都需要创建一个 ctag 标签库,代码补全才能正能工作,十分的繁琐蛋疼.当时我就在想,既然源代码都有了,难道就没有一个 Vim 插件能的通过已有的源代码,实时构建语法书来提供代码补全提示吗?通过一番搜索,终于发现了她--YouCompleteMe,看了介绍,我开始迫不及待的试用了,顿时感觉从小米加步枪