簡體   English   中英

Windows下用cygwin編譯的C程序可以正常工作,Linux下分割錯誤。 Cygwin GCC是“壞”的嗎?

[英]C program compiled with cygwin in Windows works, segmentation fault under Linux. Is cygwin GCC 'bad'?

對於我的Programming 102類,我們被要求提供在Linux下編譯和運行的C代碼。 我的硬盤驅動器上沒有足夠的可用空間來與Windows一起安裝Linux,因此我使用cygwin來編譯程序。

我必須提供的最新程序可以在cygwin下編譯並運行良好。 它在Linux下可以很好地編譯,但是執行的一半會產生分段錯誤。 我向給我們班級的研究生解釋了這一點,他說cygwin的GCC版本允許編譯和執行更簡單的代碼。

我通過google找到的一些參考資料尚無定論。 我發現一個線程說,Linux下seg錯誤的原因是內存泄漏。 為什么這不會影響cygwin版本?

我會使用大學的計算機,但無法在它們上使用Subversion,這會嚴重阻礙我的工作。 (我是編碼的新手,通常需要能夠恢復到X修訂版)。

cygwin的GCC版本對它編譯的代碼是否真的更“放松”? 如果是這樣,編碼時有什么明顯的問題要注意嗎? 有沒有其他選擇可以編寫將在Linux下運行的代碼?

編輯

感謝您的答復。 我在最初的帖子中不夠明確:代碼中存在一個錯誤幾乎是我所能接受的(畢竟,我對編程很陌生,畢竟對於C語言,它確實是綠色的)。 我的TA暗示cygwin的GCC是一種不那么可靠的編譯器-允許運行比GNU / Linux下的編譯器更草率的代碼。 我發現這很奇怪,因此在互聯網上進行了搜索,但實際上找不到任何有關該事實的參考。

我不僅在指責編譯器對我的代碼,還想知道該程序在Windows下運行並在Linux下崩潰的原因是什么。 答復是:在這方面說明了Windows / Linux下不同的內存管理器和堆/堆棧布局。

cygwin的GCC與GNU / Linux一樣“好”的結論,以及我的越野車程序在一個程序而不是另一個程序下運行的根本操作系統/絕對的運氣是否正確?

關於發布源代碼,這是一項家庭作業,因此,如果可能的話,我寧願自己找到問題:)

編輯2

我已經接受jalf的回答,因為它談到了使程序在Windows而不是Linux下運行的原因,這是我真正想知道的。 感謝所有其他貢獻者,他們都是非常有趣且有益的答復。

找到問題並解決后,我將上載一個包含此非工作版本所有源代碼的zip文件,以防萬一有人好奇地了解我到底在做什么:)

編輯3

對於那些有興趣查看代碼的人,我發現了問題,這確實是由於指針造成的。 我試圖從一個函數返回一個指針。 我試圖返回的指針在函數內部被聲明,因此一旦函數執行就被銷毀。 有問題的代碼在第22-24行中被注釋掉。

隨意嘲笑我的代碼。

/**
*  Returns array of valid searches based on current coordinate
*/
void determine_searches(int row, int col, int last_row, int last_col, int *active_search){
    // define coordinate categories and related valid search directions
    int Library0[] = {2, 3, 4, -1};
    int Library1[] = {4, 5, 6, -1};
    int Library2[] = {2, 3, 4, 5, 6, -1};
    int Library3[] = {0, 1, 2, 3, 4, 5, 6, 7, -1};
    int Library4[] = {0, 1, 2, -1};
    int Library5[] = {0, 6, 7, -1};
    int Library6[] = {0, 1, 2, 6, 7, -1};
    int Library7[] = {0, 1, 2, 3, 4, -1};
    int Library8[] = {0, 4, 5, 6, 7, -1};

    int * Library[] = { 
        Library0, Library1, Library2,
        Library3, Library4, Library5,
        Library6, Library7, Library8,
    };

    // declare (and assign memory to) the array of valid search directions that will be returned
    //int *active_search;
    //active_search = (int *) malloc(SEARCH_DIRECTIONS * sizeof(int));


    // determine which is the correct array of search directions based on the current coordinate
    // top left corner
        int i = 0;
    if(row == 0 && col == 0){
        while(Library[0][i] != -1){
            active_search[i] = Library[0][i];
            i++;
        }
    }
    // top right corner
    else if(row == 0 && col == last_col){
        while(Library[1][i] != -1){
            active_search[i] = Library[1][i];
            i++;
        }
    }
    // non-edge columns of first row
    else if(row == 0 && (col != 0 || col != last_col)){
        while(Library[2][i] != -1){
            active_search[i] = Library[2][i];
            i++;
        }
    }
    // non-edge coordinates (no edge columns nor rows)
    else if(row != 0 && row != last_row && col != 0 && col != last_col){
        while(Library[3][i] != -1){
            active_search[i] = Library[3][i];
            i++;
        }
    }
    // bottom left corner
    else if(row == last_row && col == 0){
        while(Library[4][i] != -1){
            active_search[i] = Library[4][i];
            i++;
        }
    }
    // bottom right corner
    else if(row == last_row && col == last_col){
        while(Library[5][i] != -1){
            active_search[i] = Library[5][i];
            i++;
        }
    }
    // non-edge columns of last row
    else if(row == last_row && (col != 0 || col != last_col)){
        while(Library[6][i] != -1){
            active_search[i] = Library[6][i];
            i++;
        }
    }
    // non-edge rows of first column
    else if((row != 0 || row != last_row) && col == 0){
        while(Library[7][i] != -1){
            active_search[i] = Library[7][i];
            i++;
        }
    }
    // non-edge rows of last column
    else if((row != 0 || row != last_row) && col == last_col){
        while(Library[8][i] != -1){
            active_search[i] = Library[8][i];
            i++;
        }
    }
    active_search[i] = -1;
}

我並不是說聽起來很粗魯,但可能是因為您的代碼不好,而不是編譯器。 ;)這樣的問題實際上比您想象的要普遍得多,因為不同的OS和編譯器將具有不同的方式來組織應用程序在堆棧和堆中的數據。 前者可能特別成問題,特別是如果您最終覆蓋了堆棧上的內存,或者引用了系統決定用於其他用途的釋放內存。 因此,基本上,您有時可能會擺脫它,但是有時您的應用程序會窒息而死。 不管哪種方式,如果它出現段錯誤,那是因為您試圖引用不允許的內存,所以它更像是“快樂的巧合”,它不會在另一個系統/編譯器下崩潰。

但是,實際上,segfault是segfault,因此您應該調試代碼以查找內存損壞,而不是調整編譯器的配置來找出問題所在。

編輯 :好的,現在我明白您的意思了……我以為您遇到的問題是“ X很爛,但Y正常!” 態度,但似乎是您的助教。 ;)

無論如何,這里還有一些調試問題的提示,例如:

  • 查找指針算術,為可能的“ doh”進行引用/解引用。 錯誤。 特別是在要添加/減去一個的任何地方(又稱柵欄錯誤)。
  • 在問題區域以及使用這些指針的任何相關區域,注釋掉對malloc / free的調用。 如果代碼不再崩潰,那么您將朝着正確的方向前進。
  • 假設您至少已經確定了代碼崩潰的一般區域,請在該區域中插入早期return語句,然后找出代碼不會崩潰的地方。 這可以幫助找到這一點,並在您的代碼實際上崩潰之間的區域的某處 請記住,這樣的段錯誤可能不一定直接在您的錯誤所在的代碼行發生。
  • 使用系統上可用的內存調試工具。
    • 在Unix上,請查看本指南以調試unix上內存 ,以及valgrind profiler (@ Sol,thx提醒我這一點)
    • 在Visual Studio / Windows上,您的好朋友CrtCheckMemory()非常方便。 另外,請閱讀CRT內存調試模式 ,因為它們是在VS中工作的更好的功能之一。 通常,只要記住各種模式,僅在VS中打開內存選項卡就足以診斷出此類錯誤。
    • 在Mac OSX中,可以在malloc_error_break上設置一個斷點(來自gdb或Xcode),這使得調試器在malloc檢測到內存損壞時就中斷。 我不確定是否可以在其他Unix風格中使用它,但是Google進行的快速搜索似乎表明它僅適用於Mac。 同樣, 對於OSX似乎存在一個相當“實驗”的valgrind版本。

就像其他人所說的那樣,即使這不是問題的重點,您也可能希望在此處發布一些代碼。 讓每個人都在這里戳一下您的代碼,看看他們是否可以找到導致段錯誤的原因,這仍然是一種很好的學習體驗。

但是,是的,問題在於,影響C程序的因素有很多,取決於平台的因素,以及基本上是隨機的因素。 虛擬內存意味着有時 ,訪問未分配的內存似乎可以正常工作,因為您訪問了先前分配的頁面的未使用部分。 有時,它會出現段錯誤,因為您打到的頁面根本沒有分配給您的進程。 這真的是無法預測的。 這取決於您的內存分配位置,是在頁面邊緣還是中間? 這取決於操作系統和內存管理器,以及到目前為止已分配了哪些頁面,並且……您明白了。 系統上安裝了不同的編譯器,相同版本的不同版本,不同的OS,不同的軟件,驅動程序或硬件,當您訪問未分配的內存時,是否遇到段錯誤,一切都可以改變。

至於電訊局長聲稱cygwin更“松懈”的說法,這是一個垃圾,原因很簡單。 編譯器均未捕獲該錯誤! 如果“本機” GCC編譯器確實沒有那么松懈,那么它將在編譯時給您一個錯誤。 分段錯誤不是由編譯器生成的。 編譯器無法做很多事情來確保您得到的是段錯誤,而不是看起來有效的程序。

我還沒有聽說過Cygwin下有關GCC怪異的任何具體信息,但是在您的情況下,使用-Wall命令行選項對gcc顯示所有警告可能是個好主意,以查看是否發現了可能引起問題的任何內容代碼中的段錯誤。

您的代碼中肯定有一個錯誤。 Windows內存管理器可能比Linux內存管理器更寬松。 在Windows上,您可能會在內存方面做不好的事情(例如覆蓋數組邊界,內存泄漏,雙重釋放等),但這讓您無法使用它。 與此相關的著名故事可以在 http://www.joelonsoftware.com/articles/APIWar.html (在該文章(有點冗長)上搜索“ SimCity”)。

Cygwin的gcc版本可能具有其他默認標志和經過調整的設置(例如wchar_t為2個字節),但是我懷疑它對代碼的使用更加“松懈”,即使這樣-您的代碼也不應崩潰。 如果是這樣,則很可能是您的代碼中存在一個需要修復的錯誤。 例如,您的代碼可能取決於wchar_t的特定大小,或者執行的代碼根本無法保證正常工作,例如寫入字符串文字。

如果您編寫干凈的代碼,那么它也可以在linux上運行。 我目前正在運行firefox和KDE桌面,它們一起包含數百萬條C ++行,但我看不到這些應用程序崩潰了:)

我建議您將代碼粘貼到您的問題中,以便我們可以找出問題所在。

同時,您可以在gdb運行您的程序, gdb是Linux的調試器。 您還可以在啟用所有Mudflap檢查和啟用所有警告的情況下進行編譯。 mudflaps在運行時檢查代碼是否存在各種違規情況:

[js@HOST2 cpp]$ cat mudf.cpp
int main(void)
{
  int a[10];
  a[10] = 3;  // oops, off by one.
  return 0;
}
[js@HOST2 cpp]$ g++ -fmudflap -fstack-protector-all -lmudflap -Wall mudf.cpp
[js@HOST2 cpp]$ MUDFLAP_OPTIONS=-help ./a.out
  ... showing many options ...
[js@HOST2 cpp]$ ./a.out 
*******                 
mudflap violation 1 (check/write): time=1234225118.232529 ptr=0xbf98af84 size=44
pc=0xb7f6026d location=`mudf.cpp:4:12 (main)'                                   
      /usr/lib/libmudflap.so.0(__mf_check+0x3d) [0xb7f6026d]                    
      ./a.out(main+0xb9) [0x804892d]                                            
      /usr/lib/libmudflap.so.0(__wrap_main+0x4f) [0xb7f5fa5f]                   
Nearby object 1: checked region begins 0B into and ends 4B after                
mudflap object 0x9731f20: name=`mudf.cpp:3:11 (main) int a [10]'                
bounds=[0xbf98af84,0xbf98afab] size=40 area=stack check=0r/3w liveness=3        
alloc time=1234225118.232519 pc=0xb7f5f9fd                                      
number of nearby objects: 1                                                     
*** stack smashing detected ***: ./a.out terminated                             
======= Backtrace: =========
....

您可以執行許多Mudflap檢查,以上檢查均使用默認選項運行a.out。 valgrind是另一種有助於解決此類錯誤的工具,它也可以幫助您發現泄漏或被上述一個錯誤清除。 將環境變量“ MALLOC_CHECK_”設置為1也會打印違反消息。 有關該變量的其他可能值,請參見malloc的聯機幫助頁。

要檢查程序崩潰的位置,可以使用gdb

[js@HOST2 cpp]$ cat test.cpp
int main() {
    int *p = 0;
    *p = 0;
}
[js@HOST2 cpp]$ g++ -g3 -Wall test.cpp
[js@HOST2 cpp]$ gdb ./a.out
...
(gdb) r
Starting program: /home/js/cpp/a.out

Program received signal SIGSEGV, Segmentation fault.
0x080483df in main () at test.cpp:3
3           *p = 0;
(gdb) bt
#0  0x080483df in main () at test.cpp:3
(gdb)

使用-g3編譯代碼以包含許多調試信息,因此gdb可以幫助您找到程序崩潰的確切行。 以上所有技術均適用於C和C ++。

幾乎可以肯定是指針錯誤或緩沖區溢出,可能是未初始化的變量。

未初始化的指針通常不會指向任何東西,但是有時它將指向某個東西。 從中讀取或寫入該文件通常會使程序崩潰,但隨后可能不會崩潰。

從釋放的內存中寫入或讀取是同一回事。 您可能會擺脫它,然后又可能沒有。

這些情況完全取決於堆棧,堆的布局方式以及運行時所執行的操作。 可能會導致一個糟糕的程序只能在一個編譯器/運行時組合上運行,而不能在另一個程序上運行,僅因為在一個程序上它會覆蓋無關緊要的東西(太多),或者未初始化的變量“碰巧”包含一個有效的程序所使用的上下文的值。

GCC的版本可能不是問題。 在運行時庫中的差異和代碼中的錯誤更可能是針對Windows版本的運行時而不會顯現的錯誤。 如果需要更具體的答案,則可能需要發布段錯誤代碼和更多背景信息。

通常,最好在要用於運行代碼的環境下進行開發。

您是否在進行任何特定於平台的假設,例如數據類型的大小, struct數據結構對齊方式字節順序

分段錯誤意味着您嘗試訪問您無法訪問的內存,這通常意味着您嘗試取消引用空指針,或者您兩次刪除了內存或得到了野生指針。 有兩個原因可能使您在cygwin上似乎沒問題,而在Linux上卻沒問題:要么是內存管理器出現了問題,要么是其中一個比另一個更幸運。 幾乎可以肯定,您的代碼有錯誤。

要解決此問題,請查看指針的用法。 考慮用智能指針代替原始指針。 考慮進行搜索刪除,然后立即將指針歸零(嘗試刪除空指針是安全的)。 如果您可以在Linux上破解,請嘗試通過gdb進行堆棧跟蹤,看看在發生這種情況時是否有明顯的錯誤。 檢查如何使用所有未初始化的指針。 如果您有權使用內存調試工具,請使用它。

一些提示:

  1. 發布您的代碼。 我敢打賭,您將獲得一些好的建議,使您成為一名更好的程序員。

  2. 使用-wall選項打開警告,並更正報告的所有問題。 同樣,它可以幫助您成為一個更好的程序員。

  3. 使用調試器單步執行代碼。 除了幫助您了解問題所在之外,它還可以使您成為更好的程序員。

  4. 繼續使用Subversion或其他源代碼控制系統。

  5. 在您確定問題所在之前,切勿怪罪編譯器(或操作系統或硬件)。 即使這樣,也要懷疑自己的代碼。


Linux上的GCC是與Cygwin上的GCC相同的源代碼。 由於Cygwin POSIX仿真層和基礎Windows API,因此平台之間存在差異。 可能額外的層比底層硬件更寬容,但這並不算數。

由於這是家庭作業,所以我說發布代碼是一個更好的主意。 有什么比從專業程序員那里獲得投入更好的學習方法呢? 不過,我建議您將您在附近評論中實施的所有建議記入功勞。

分段錯誤是由於訪問地址不存在(或先前已釋放)而導致的。 我發現非常有趣的是,在cygwin下,該代碼未進行段錯誤。 這可能意味着您的程序使用了指向其他進程的地址空間的野指針,並且實際上能夠讀取它(gasp),或者(更有可能)直到導致該段錯誤的代碼才在該程序在Linux下運行之前才到達。

我建議以下內容:

  1. 粘貼您的代碼,因為這是一個非常有趣的問題
  2. 將此副本發送給cygwin開發人員
  3. 如果您需要生產在Linux下運行的更多程序,則可以獲取便宜的Linux VPS,這將使您的生活更加輕松。

在Linux下工作后(即裝入VPS),請嘗試使用以下程序:

  • 廣東發展銀行
  • 瓦爾格朗德
  • 痕跡

此外,您可以嘗試使用電圍欄之類的庫來捕獲程序運行時發生的此類事件。

最后,確保-Wall傳遞給gcc,您希望它能傳達警告。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM