Ubuntu 12.04 で GCC を使ってコンパイルできない

Ubuntu 12.04 で GCC を使ってコンパイルできない

私は、GCC と VC9 の両方を搭載した Ubuntu と Windows マシンで以下の C プログラムをコンパイルして実行しようとしています。しかし、以下の問題に直面しています。

Ubuntu マシンの場合:

GCC は正常にコンパイルされますが、実行すると次のプロンプトが表示されます。

Segmentation Fault (Core Dump).

Windows マシンの場合:

VC9 は正常にコンパイルおよび実行されます。GCC は正常にコンパイルされますが、プログラムを実行するとプロセスが終了します。

専門家の助けが必要です。これが私のコードです:

#include <string.h>
#include <stdio.h>

int calc_slope(int input1,int input2)
{
    int sum=0;
    int start=input1;
    int end=input2;
    int curr=start;

    //some validation:
    if (input1>input2)
        return -1;


    while(curr<=end)
    {
        if (curr>100)
        {
            char *s="";
            int length;
            int left;
            int right;
            int cent;

            sprintf(s,"%d",curr);
            length=strlen(s);
            s++;
            do
            {
                //printf("curr=%d char=%c pointer=%d length=%d \n",curr,*s,s,length);
                left = *(s-1) - '0';
                cent = *s - '0';
                right = *(s+1) - '0';
                //printf("curr=%d l=%d c=%d r=%d\n",curr,left,cent,right);
                if ( (cent>left && cent>right) || (cent<left && cent<right) )
                {
                    sum+=1; //we have either a maxima or a minima.
                }

                s++;
            } while (*(s+1)!='\0');
        }
        curr++;
    }

    return sum;
}

int main()
{
    printf("%d",calc_slope(1,150));
    return 0;
}

アップデート:

功績はエリアエラーの追跡を助けてくれただけでなく、gcc でコンパイルされたプログラムのデバッグに非常に役立つgdbとそのバックトレース ツール ( bt) を紹介してくれました。試行錯誤の末に完成した修正版がこちらです。

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int calc_slope(int input1,int input2)
{
    int sum=0;
    int start=input1;
    int end=input2;
    int curr=start;

    //some validation:
    if (input1>input2)
        return -1;


    while(curr<=end)
    {
        if (curr>100)
        {
            int size=10;
            char *s=(char*)malloc((size+1) * sizeof(char));
            int left;
            int right;
            int cent;

            sprintf(s,"%d",curr);
            s++;
            do
            {
                left = *(s-1) - '0';
                cent = *s - '0';
                right = *(s+1) - '0';
                if ( (cent>left && cent>right) || (cent<left && cent<right) )
                {
                    sum+=1; //we have either a maxima or a minima.
                }

                s++;
            } while (*(s+1)!='\0');
        }
        curr++;
    }

    return sum;
}

int main()
{
    printf("%d",calc_slope(1,150));
    return 0;
}

答え1

セグメンテーション違反プログラムが割り当てられた領域外のメモリにアクセスしようとしたときに発生します。

この場合、経験豊富なCプログラマーは、が呼び出される行で問題が発生していることがわかりますsprintf。しかし、セグメンテーション違反がどこで発生しているかがわからない場合、またはコード全体を読んで解決したくない場合は、試すこれを理解するには、デバッグ シンボルを使用してプログラムをビルドし ( ではgcc-gフラグがこれを実行します)、デバッガーで実行します。

ソース コードをコピーして、 という名前のファイルに貼り付けましたslope.c。次に、次のようにビルドしました。

gcc -Wall -g -o slope slope.c

(は-Wallオプションです。より多くの状況で警告を生成するようにするためだけです。これは何が間違っているのかを把握するのにも役立ちます。)

gdb次に、最初にプログラムを実行してデバッガーでプログラムをgdb ./slope実行しgdb、次にデバッガー内でrunデバッガーにコマンドを指定してプログラムを実行しました。

ek@Kip:~/source$ gdb ./slope
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/ek/source/slope...done.
(gdb) run
Starting program: /home/ek/source/slope 
warning: Cannot call inferior functions, you have broken Linux kernel i386 NX (non-executable pages) support!

Program received signal SIGSEGV, Segmentation fault.
0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6

you have broken Linux kernel i386 NX(私の...メッセージについては心配しないでください。このプログラムをデバッグするために効果的に使用することをsupport妨げるものではありません。)gdb

この情報は非常に不可解です...そして、libcのデバッグシンボルがインストールされていない場合は、シンボリック関数名の代わりに16進アドレスを持つさらに不可解なメッセージが表示されます_IO_default_xsputn。幸いなことに、それは問題ではありません。なぜなら、私たちが本当に知りたいのはプログラムのどこに問題が発生しています。

SIGSEGVしたがって、解決策は、シグナルが最終的にトリガーされたシステム ライブラリ内の特定の関数呼び出しに至るまでに、どのような関数呼び出しが行われたかを遡って確認することです。

gdb(そしてどのデバッガーにも)この機能が組み込まれています。これはスタックトレースまたはバックトレースデバッガー コマンドを使用してbt、バックトレースを生成しますgdb

(gdb) bt
#0  0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6
#1  0x00178e04 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
#2  0x0019b234 in vsprintf () from /lib/i386-linux-gnu/libc.so.6
#3  0x0017ff7b in sprintf () from /lib/i386-linux-gnu/libc.so.6
#4  0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26
#5  0x08048578 in main () at slope.c:52
(gdb)

関数が 関数 (意図したとおり) をmain呼び出し、次にを呼び出すことがわかります。これは (このシステムでは) 他のいくつかの関連ライブラリ関数への呼び出しで実装されています。calc_slopecalc_slopesprintf

一般的に興味があるのは関数呼び出しですあなたのプログラムで関数を呼び出すプログラム外使用しているライブラリ自体にバグがない限り(この場合、libcライブラリファイルによって提供される標準Cライブラリlibc.so.6)、クラッシュの原因となるバグはプログラムにあり、頻繁最後の呼び出し時またはそれに近いあなたのプログラムで。

この場合、次のようになります。

#4  0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26

ここでプログラムが を呼び出します。が次のステップであるsprintfため、これがわかります。しかし、それが示されていなくても、これがわかるのは、sprintf26行目でそれが起こります、そしてこう言います。

... at slope.c:26

プログラムの 26 行目には以下が含まれます。

            sprintf(s,"%d",curr);

(少なくとも現在表示している行については、行番号を自動的に表示するテキスト エディターを常に使用する必要があります。これは、コンパイル時のエラーと、デバッガーの使用中に明らかになる実行時の問題の両方を解釈するのに非常に役立ちます。)

で議論したようにデニス・カースメーカーの回答は、s1 バイトの配列です。(ゼロではありません。割り当てた値 は""1 バイトの長さであるため、つまり は と等しく、 が と等しいの{ '\0' }と同じです。)"Hello, world!\n"{ 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }

なぜできたこれはまだ一部のプラットフォームで動作しますか (Windows 用の VC9 でコンパイルした場合は動作するようです)?

メモリを割り当てた後にその外部のメモリにアクセスしようとするとエラーが発生するとよく言われます。しかし、これは正しくありません。CおよびC++の技術標準によると、実際に発生するのは次のようになります。未定義の動作。

つまり、何が起きてもおかしくないのです!

それでも、他のものよりも起こりやすいものがあります。スタック上の小さな配列が、一部の実装では、スタック上の大きな配列のように機能するように見えるのはなぜでしょうか。

これはスタック割り当ての実装方法に帰着し、プラットフォームごとに異なる可能性があります。実行可能ファイルは、実際に使用される予定よりも多くのメモリをスタックに割り当てる場合があります。これにより、意図していないメモリ位置に書き込むことができる場合があります。明示的にコード内で主張されています。VC9 でプログラムをビルドすると、これが発生する可能性が非常に高くなります。

しかし、VC9 でもこの動作に頼るべきではありません。異なるWindowsシステムに存在する可能性のある異なるバージョンのライブラリに依存する可能性があります。しかしさらに可能性が高い問題は、余分なスタックスペースが実際に使用されることを意図して割り当てられているため、実際に使われるかもしれません。次に、「未定義の動作」という悪夢を体験します。この場合、複数の変数が同じ場所に格納され、1 つの変数への書き込みによって他の変数が上書きされる可能性があります...ただし、常にそうなるとは限りません。変数への書き込みがレジスタにキャッシュされ、実際にはすぐに実行されない場合があるためです (または、変数への読み取りがキャッシュされる場合や、割り当てられたメモリが変数自体を介して書き込まれていないことがコンパイラによって認識されているため、変数が以前と同じであると想定される場合があります)。

そして、VC9でビルドされたプログラムがなぜ動作したのかというもう一つの可能​​性について考えてみましょう。配列または他の変数実際には、1 バイト配列の後のスペースを使用するためにプログラムによって割り当てられました (プログラムが使用しているライブラリによって割り当てられる場合もあります) s。したがって、s1 バイトより長い配列として扱うと、その変数/配列の内容にアクセスすることになり、これも問題になる可能性があります。

結論として、このような間違いがあった場合、ラッキー「セグメンテーション違反」や「一般保護違反」などのエラーが発生します。しないそうしないと、プログラムが未定義の動作。

答え2

こんにちは、バッファオーバーフローです!

char *s="";
sprintf(s,"%d",curr);
length=strlen(s);

スタック上の文字列に 1 バイトを割り当て、その後、複数のバイトを書き込みます。さらに、その配列の末尾を超えて読み取ります。C のマニュアル、特に文字列とそれらのメモリ割り当てに関するセクションを読んでください。

関連情報