C语言中,一个星号引发的错误
2012-12-06 22:44
246 查看
首先介绍一下产品及问题背景。我们做的后台程序是编译成可执行程序供Tuxedo中间件调用。整个程序使用的是C语言,编译生成可执行程序使用makefile的方式,其中供Tuxedo调用的可执行程序是使用Tuxedo提供的buildserver将中间文件(.obj或.o)文件链接生成。所以需要我们手工使用编译器将源代码编译成中间文件。在windows环境下,我们使用VC6.0提供的编译链接程序cl.exe。
由于客户工期较紧且等不到我们程序发版,所以就临时发放了一份测试版本的程序。发放程序时向客户提供了供Tuxedo调用的可执行程序,还提供了打乱后的源代码(将程序中注释去掉,并扰乱程序排版)。今天下午,问题来了。客户反映程序执行结果不正确,拿了一份客户提供的数据库备份,恢复至本地,使用我们目前正在开发测试的程序死活重现不了问题。我觉得软件开发人员最大的苦恼便是别人明确说有问题,而自己无论如何都重现不了,这是最无奈的。后来直接取发放给客户的程序,换台机器重新执行,问题重现。于是使用发放给客户的打乱的源码重新编译,执行以便查找原因。令人苦闷的是使用当初的源码编译出的程序执行结果却又是正确的,又使问题更加扑朔迷离。无奈之下只好将我们目前最新的没问题的程序发放给客户供他们测试。如果程序员发现自己编写的代码出现了问题,而又不想追根究底查找原因,这种程序员便不算好程序员。于是我们就慢慢查。根据SVN上的修改,找到发放给客户程序的时点,然后取下来进行编译重现问题。终于在一个版本中重现了该问题。并定位到了出错的代码行。定位到问题之后非常惊讶。怎么会出现这种问题。
问题的原因是:在比较偏僻的地方,有一行注释,注释里偏僻的地方有一个不起眼的星号*,这个该死的星号让编译器实际编译的代码与我们预期要编译的代码产生了偏差。
为了便于描述,我将问题特征抽象并使用如下代码描述:
上述代码,看上去很显然结果是什么都不输出。可现实是:
VC6.0环境下,编译执行:
见证奇迹的时刻!
程序竟然输出了test is true。看到这里大家有没有感觉到吃惊。
为了探究原因,使用cl /P参数,将源代码做预编译(去掉源代码中的注释并展开源文件和宏),不做编译,查看预编译后的结果。
此时会生成预编译后的文件main.i,打开查看,关键代码如下:
奇迹发生了有木有,if判断语句没了有木有!看来cl是把if( test )当成了注释给删掉了哇!
由于VC6.0的资料很难再找到,只找到微软VS2012关于C注释的解释:
http://technet.microsoft.com/zh-cn/office/wfwda74e(de-de).aspx
“注释”是字符序列由编译器将一个空白字符和否则将忽略的一个正斜杠/星号组合 (/*) 开头。 注释可以包括任何字符组合可以从可用的字符集的,包括换行符,但是,排除 “结束注释”分隔符 (*)。描述还是不清楚。英文原文见:http://technet.microsoft.com/en-us/office/wfwda74e(de-de).aspx
也没发现什么问题,但产生问题的代码的注释中存在一个*,于是尝试将该星号去除
预编译如下:
很神奇有没有,if语句又回来了。鉴于篇幅,就不再将执行结果贴出来了。结果必然是什么都不输出。确定无疑是由该星号引起的。
cl误认为注释开始于第一行,结束于第二行。if语句后若没有注释,编译会报错。
于是乎继续尝试,将星号恢复,但星号后加一个空格:
if语句再次回归,后再测试星号后加英文字母,if语句都不会将被当做注释。
得出结论:若注释中存在星号*且后紧跟汉字,cl.exe 编译时并不能准确判断该行注释的结尾。(也可能是自己只是浅薄把注释写错,而并非cl判断错误)
*后跟空格没事,跟英文字母没事,为毛跟个汉字你就不认识了!
后来在linux上测试,使用
预编译,结果如下:
没有此现象,使用VS2010的cl程序,也不存在此问题。
只因在程序注释里多写了个星号,导致出现的问题十分怪异,耗费人力去查找。由于做的是金融IT系统,若此问题没有被发现,而跑到了客户的生产环境中,导致交易数据混乱,真是会要人命啊!真是一个星号引发了一场“血案”呐。
C语言交流群:219952490
由于客户工期较紧且等不到我们程序发版,所以就临时发放了一份测试版本的程序。发放程序时向客户提供了供Tuxedo调用的可执行程序,还提供了打乱后的源代码(将程序中注释去掉,并扰乱程序排版)。今天下午,问题来了。客户反映程序执行结果不正确,拿了一份客户提供的数据库备份,恢复至本地,使用我们目前正在开发测试的程序死活重现不了问题。我觉得软件开发人员最大的苦恼便是别人明确说有问题,而自己无论如何都重现不了,这是最无奈的。后来直接取发放给客户的程序,换台机器重新执行,问题重现。于是使用发放给客户的打乱的源码重新编译,执行以便查找原因。令人苦闷的是使用当初的源码编译出的程序执行结果却又是正确的,又使问题更加扑朔迷离。无奈之下只好将我们目前最新的没问题的程序发放给客户供他们测试。如果程序员发现自己编写的代码出现了问题,而又不想追根究底查找原因,这种程序员便不算好程序员。于是我们就慢慢查。根据SVN上的修改,找到发放给客户程序的时点,然后取下来进行编译重现问题。终于在一个版本中重现了该问题。并定位到了出错的代码行。定位到问题之后非常惊讶。怎么会出现这种问题。
问题的原因是:在比较偏僻的地方,有一行注释,注释里偏僻的地方有一个不起眼的星号*,这个该死的星号让编译器实际编译的代码与我们预期要编译的代码产生了偏差。
为了便于描述,我将问题特征抽象并使用如下代码描述:
#include<stdio.h> int main() { int test = 0; /*hello *星号*/ if( test )/* 2012 */ { printf("test is true\n"); } return 0; }
上述代码,看上去很显然结果是什么都不输出。可现实是:
VC6.0环境下,编译执行:
E:\codetest\ctest>cl main.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. main.c Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:main.exe main.obj E:\codetest\ctest>main.exe test is true
见证奇迹的时刻!
程序竟然输出了test is true。看到这里大家有没有感觉到吃惊。
为了探究原因,使用cl /P参数,将源代码做预编译(去掉源代码中的注释并展开源文件和宏),不做编译,查看预编译后的结果。
E:\codetest\ctest>cl /P main.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. main.c
此时会生成预编译后的文件main.i,打开查看,关键代码如下:
int main() { int test = 0; { printf("test is true\n"); } return 0; }
奇迹发生了有木有,if判断语句没了有木有!看来cl是把if( test )当成了注释给删掉了哇!
由于VC6.0的资料很难再找到,只找到微软VS2012关于C注释的解释:
http://technet.microsoft.com/zh-cn/office/wfwda74e(de-de).aspx
“注释”是字符序列由编译器将一个空白字符和否则将忽略的一个正斜杠/星号组合 (/*) 开头。 注释可以包括任何字符组合可以从可用的字符集的,包括换行符,但是,排除 “结束注释”分隔符 (*)。描述还是不清楚。英文原文见:http://technet.microsoft.com/en-us/office/wfwda74e(de-de).aspx
也没发现什么问题,但产生问题的代码的注释中存在一个*,于是尝试将该星号去除
#include<stdio.h> int main() { int test = 0; /*hello 星号*/ if( test )/* 2012 */ { printf("test is true\n"); } return 0; }
预编译如下:
int main() { int test = 0; if( test ) { printf("test is true\n"); } return 0; }
很神奇有没有,if语句又回来了。鉴于篇幅,就不再将执行结果贴出来了。结果必然是什么都不输出。确定无疑是由该星号引起的。
cl误认为注释开始于第一行,结束于第二行。if语句后若没有注释,编译会报错。
于是乎继续尝试,将星号恢复,但星号后加一个空格:
/*源码如下*/
#include<stdio.h>
int main()
{
int test = 0;
/*hello * 星号*/
if( test )/* 2012 */
{
printf("test is true\n");
}
return 0;
}
===========================================
/*预编译后代码如下*/
int main() { int test = 0; if( test ) { printf("test is true\n"); } return 0; }
if语句再次回归,后再测试星号后加英文字母,if语句都不会将被当做注释。
得出结论:若注释中存在星号*且后紧跟汉字,cl.exe 编译时并不能准确判断该行注释的结尾。(也可能是自己只是浅薄把注释写错,而并非cl判断错误)
*后跟空格没事,跟英文字母没事,为毛跟个汉字你就不认识了!
后来在linux上测试,使用
gcc -E main.c > test.txt
预编译,结果如下:
int main() { int test = 0; if( test ) { printf("hello world\n"); } return 0; }
没有此现象,使用VS2010的cl程序,也不存在此问题。
只因在程序注释里多写了个星号,导致出现的问题十分怪异,耗费人力去查找。由于做的是金融IT系统,若此问题没有被发现,而跑到了客户的生产环境中,导致交易数据混乱,真是会要人命啊!真是一个星号引发了一场“血案”呐。
C语言交流群:219952490
相关文章推荐
- 第一次犯一个括号位置引发的逻辑错误
- SpinBox引发的一个错误
- 一个隐式转换引发的执行计划错误
- 遇到一个<iostream>引发的非常难缠的连接错误LNK2001
- 没有空参构造函数引发的一个错误
- 一个关于C++ Inline关键字的引发的一个错误
- mysql中一个普通ERROR 1135 (HY000)错误引发的血案
- C语言malloc()的一个问题——堆越界的一个错误。
- c语言中指针的一个小错误
- 记一个iOS开发中cell的重用机制引发的错误
- c语言的一个小错误
- 一个c语言规范引发的一串问题
- 一个翻译错误引发的焦虑
- 一个对象合并的函数引发的错误
- mysql中一个普通ERROR 1135 (HY000)错误引发的血案
- 一个分号引发的错误!——undefined is not a function
- 一个关于C语言循环的错误
- Linux环境下段错误的产生原因及调试方法小结 最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且项目工程庞大复杂,出现了不少问题,其中遇到最多、花费时间最长的问题就是
- SpringMVC+Hibernate 一个经典的由懒加载机制引发的错误
- 一个由快速排序引发的段错误