14

私のProgramming102クラスでは、Linuxでコンパイルおよび実行されるCコードを提供するように求められます。ハードドライブにWindowsと一緒にLinuxをインストールするのに十分な空き容量がないため、cygwinを使用してプログラムをコンパイルします。

私がコンパイルで提供しなければならなかった最新のプログラムは、cygwinの下で正常に実行されます。Linuxでは正常にコンパイルされますが、実行の途中でセグメンテーション違反が発生します。私はこれをクラスを提供する大学院生に説明しました、そして彼はcygwinのバージョンのGCCはより滑らかなコードをコンパイルして実行することを可能にする、と言いました。

私がグーグルで見つけたいくつかの参考文献は決定的なものではありませんでした。私が見つけたあるスレッドは、Linuxでのセグメンテーション違反の原因はメモリリークであると述べました。なぜこれがcygwinバージョンに影響を与えないのでしょうか?

私は大学のコンピューターを使用しますが、Subversionを使用できないため、作業が大幅に妨げられます。(私はコーディングに不慣れで、多くの場合、以前にXリビジョンに戻せるようにする必要があります)。

cygwinのバージョンのGCCは、コンパイルするコードで本当に「緩い」のでしょうか?もしそうなら、コーディング時に注意すべき明らかな問題はありますか?Linuxで実行されるコードを記述できるようにするための代替手段はありますか?

編集

返信ありがとうございます。私は元の投稿で十分に明確ではありませんでした:私のコードにバグがあることは私にはほとんど与えられていました(私はプログラミングにまったく慣れておらず、結局のところ、Cに関しては本当に環境に優しいです)。私のTAは、cygwinのGCCは信頼性の低いコンパイラであり、GNU/Linuxで見られるものよりもはるかにスロープなコードを実行できます。私はこれが奇妙だと思ったのでインターネットで検索しましたが、その事実への言及は実際には見つかりませんでした。

コンパイラと私のコードを非難するだけでなく、プログラムがWindowsで実行され、Linuxでクラッシュする理由は何でしょうか。返信は次のとおりです。Windows/Linuxでのさまざまなメモリマネージャーとヒープ/スタックレイアウトがその点を示していました。

cygwinのGCCはGNU/Linuxと同じくらい「良い」であり、私のバグのあるプログラムが一方の下で実行され、もう一方の下で実行されないのは、基盤となるオペレーティングシステム/幸運であるという結論はほとんど正しいでしょうか?

ソースコードの投稿に関しては、宿題なので、できれば自分で問題を見つけたいと思います:)

編集2

LinuxではなくWindowsでプログラムを実行する理由について説明しているjalfの回答を受け入れました。これは、私が本当に知りたかったことです。貢献してくれた他のみんなのおかげで、彼らは皆とても興味深くそして有益な返事でした。

問題を見つけて修正したら、この機能していないバージョンのすべてのソースコードを含む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;
}
4

11 に答える 11

14

失礼に聞こえるつもりはありませんが、コンパイラではなく、おそらくあなたのコードが悪いのです。;)このような問題は、実際にはあなたが思っているよりも一般的です。なぜなら、OSやコンパイラが異なれば、スタックやヒープ内のアプリケーションのデータを整理する方法も異なるからです。前者は、特にスタック上のメモリを上書きしたり、システムが他の目的で使用することを決定した解放されたメモリを参照したりする場合に、特に問題になる可能性があります。つまり、基本的には、それでうまくいくこともありますが、それ以外の場合は、アプリが窒息して死んでしまいます。いずれにせよ、segfaultが発生した場合は、許可されていないメモリを参照しようとしたためです。したがって、別のシステム/コンパイラでクラッシュしなかったのは、より「偶然の一致」です。

ただし、実際には、segfaultはsegfaultであるため、コンパイラの構成を微調整して何が問題になっているのかを把握するのではなく、コードをデバッグしてメモリの破損を探す必要があります。

編集:わかりました、今あなたが何を意味しているのかわかります...「Xはダメですが、Yは問題なく動作します!」という問題が発生していると思いました。態度ですが、それを持っているのはあなたのTAのようです。;)

とにかく、次のような問題をデバッグするためのヒントがいくつかあります。

  • ポインタ演算を探し、可能な「doh!」を参照/逆参照します。エラー。足し算/引き算をしている場所(別名、フェンスポストエラー)は特に疑わしいものです。
  • 問題のある領域、およびそれらのポインターが使用されている関連領域の周りのmalloc/freeへの呼び出しをコメントアウトします。コードがクラッシュしなくなったら、正しい方向に進んでいます。
  • 少なくともコードがクラッシュしている一般的な領域を特定したと仮定して、そこにアーリーリターンステートメントを挿入し、コードがクラッシュしないポイントを見つけます。これは、そのポイントとコードが実際にクラッシュする場所の間のどこかにある領域を見つけるのに役立ちます。このようなセグメンテーション違反は、バグがあるコード行で直接発生するとは限らないことを忘れないでください。
  • システムで利用可能なメモリデバッグツールを使用します。
    • Unixの場合は、 UNIXのメモリのデバッグについてこのガイドを確認し、valgrindプロファイラー(@ Sol、これについて思い出させるためのthx)を確認してください。
    • Visual Studio / Windowsでは、親友のCrtCheckMemory()がかなり便利です。また、CRTメモリのデバッグパターンを読んでください。これらはVSでの作業の優れた機能の1つです。多くの場合、さまざまなパターンを覚えたら、VSでメモリタブを開いたままにしておくだけで、このようなバグを診断できます。
    • Mac OSXでは、malloc_error_breakにブレークポイントを設定できます(gdbまたはXcodeのいずれかから)。これにより、mallocがメモリの破損を検出するたびにデバッガーが中断します。それが他のUNIXフレーバーで利用できるかどうかはわかりませんが、グーグルですばやく検索すると、Macのみであることが示されているようです。また、OSXにはかなり「実験的な」外観のvalgrindが存在するようです
于 2009-02-09T22:21:20.647 に答える
4

CygwinでのGCCの奇妙さについて具体的なことは聞いたことがありませんが、あなたの場合は、gccの-Wallコマンドラインオプションを使用してすべての警告を表示し、原因となる可能性のあるものが見つかるかどうかを確認することをお勧めします。コード内のsegfault。

于 2009-02-09T22:20:39.290 に答える
4

あなたは間違いなくあなたのコードのどこかにバグがあります。WindowsのメモリマネージャがLinuxのメモリマネージャよりも緩んでいる可能性があります。Windowsでは、メモリを使って悪いことをしている可能性があります(配列の境界の上書き、メモリリーク、ダブルフリーなど)が、それを回避することができます。これに関連する有名な物語はで見つけることができますhttp://www.joelonsoftware.com/articles/APIWar.html(その(やや長い)記事で「SimCity」を検索してください)。

于 2009-02-09T22:23:09.347 に答える
2

Cygwinのバージョンのgccには、他のデフォルトのフラグと微調整された設定(たとえば、wchar_tは2バイト)がある場合がありますが、コードが特に「緩い」とは思えません。コードがクラッシュすることはありません。もしそうなら、おそらく修正が必要なコードのバグがあります。たとえば、コードがwchar_tの特定のサイズに依存している場合や、文字列リテラルへの書き込みなど、まったく機能することが保証されていないコードを実行している場合があります。

クリーンなコードを書くと、Linuxでも実行されます。私は現在、FirefoxとKDEデスクトップを実行しています。これらは一緒に数百万のC ++ラインで構成されており、これらのアプリがクラッシュすることはありません:)

コードを質問に貼り付けて、何が問題になっているのかを確認できるようにすることをお勧めします。

gdbそれまでの間、Linux用のデバッガーであるでプログラムを実行できます。すべてのマッドフラップチェックを有効にして、すべての警告を有効にしてコンパイルすることもできます。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: =========
....

実行できるマッドフラップチェックは多数あり、上記はデフォルトオプションを使用してa.outを実行します。この種のバグに役立つもう1つのツールはvalgrind、です。これは、上記のような1つのバグによるリークやオフを見つけるのにも役立ちます。環境変数「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++に等しく適用できます。

于 2009-02-09T22:21:37.277 に答える
2

ほぼ確実にポインタ エラーまたはバッファ オーバーランであり、初期化されていない変数である可能性があります。

初期化されていないポインターは、通常、何も指していませんが、何かを指している場合もあります。そこから読み書きすると、通常はプログラムがクラッシュしますが、それでもクラッシュしない可能性があります。

解放されたメモリからの書き込みまたは読み取りは同じ話です。あなたはそれでうまくいくかもしれませんが、そうではないかもしれません。

これらの状況は、スタック、ヒープがどのように配置されているか、およびランタイムが何をしているかによって異なります。あるコンパイラ/ランタイムの組み合わせで動作し、別の組み合わせでは動作しない悪いプログラムを作成することは十分に可能です.使用されているコンテキストの値。

于 2009-02-09T22:35:21.203 に答える
1

いくつかのヒント:

  1. コードを投稿してください。私はあなたがあなたをより良いプログラマーにするいくつかの良いインプットを得るだろうに違いない。

  2. オプションで警告をオンにし、-wall報告された問題を修正します。繰り返しますが、それはあなたをより良いプログラマーにするのに役立ちます。

  3. デバッガーを使用してコードをステップ実行します。問題がどこにあるかを理解するのに役立つだけでなく、それはあなたをより良いプログラマーにするのに役立ちます。

  4. Subversionまたは他のソースコード管理システムを引き続き使用します。

  5. 問題を特定したことが確実になるまで、コンパイラ(またはOSやハードウェア)のせいにしないでください。それでも、自分のコードには疑いを持ってください。


Linux上のGCCは、Cygwin上のGCCと同じソースコードです。プラットフォーム間には、CygwinPOSIXエミュレーションレイヤーと基盤となるWindowsAPIのために違いがあります。余分なレイヤーが基盤となるハードウェアよりも寛容である可能性がありますが、それは期待されていません。

宿題なので、コードを投稿する方がいいと思います。プロのプログラマーから意見を聞くよりも、学ぶためのより良い方法はありますか?ただし、近くのコメントで実装した提案はすべてクレジットすることをお勧めします。

于 2009-02-09T22:49:18.003 に答える
1

GCCのバージョンはおそらく問題ではありません。ランタイムライブラリの違いと、Windowsバージョンのランタイムに対して実行したときに現れないコードのバグである可能性が高くなります。より具体的な回答が必要な場合は、segfaultsのコードといくつかの背景情報を投稿することをお勧めします。

一般に、コードの実行に使用する環境で開発するのが最善です。

于 2009-02-09T22:21:36.497 に答える
1

セグメンテーション違反は、アクセスできなかったメモリにアクセスしようとしたことを意味します。これは通常、null ポインタを逆参照しようとしたか、メモリを二重に削除したか、ワイルド ポインタを取得したことを意味します。Linux では問題なく cygwin で問題ないように見えたのには、2 つの理由があります。メモリ マネージャーに問題があったか、どちらかのほうがうまくいったかのどちらかです。ほとんどの場合、コードのエラーです。

これを修正するには、ポインタの使用状況を確認してください。生のポインターをスマート ポインターに置き換えることを検討してください。delete の検索を行い、その直後にポインターをゼロにすることを検討してください (null ポインターを削除しようとしても安全です)。Linux でクラックを取得できる場合は、gdb を使用してスタック トレースを取得し、その行に明らかに問題があるかどうかを確認してください。初期化されていないすべてのポインターの使用方法を調べます。メモリ デバッグ ツールにアクセスできる場合は、それを使用します。

于 2009-02-09T22:39:26.077 に答える
1

データ型のサイズ、 s でのデータ構造のアラインメントエンディアンなど、プラットフォーム固有の仮定を行っていますか?struct

于 2009-02-09T22:28:23.903 に答える