C語言中,一個星號引發的錯誤


       首先介紹一下產品及問題背景。我們做的后台程序是編譯成可執行程序供Tuxedo中間件調用。整個程序使用的是C語言,編譯生成可執行程序使用makefile的方式,其中供Tuxedo調用的可執行程序是使用Tuxedo提供的buildserver將中間文件(.obj或.o)文件鏈接生成。所以需要我們手工使用編譯器將源代碼編譯成中間文件。在windows環境下,我們使用VC6.0提供的編譯鏈接程序cl.exe。

      由於客戶工期較緊且等不到我們程序發版,所以就臨時發放了一份測試版本的程序。發放程序時向客戶提供了供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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM