Kompilierung mit GCC unter Ubuntu 12.04 nicht möglich

Kompilierung mit GCC unter Ubuntu 12.04 nicht möglich

Ich versuche, das folgende C-Programm auf meinen Ubuntu- und Windows-Rechnern mit GCC und VC9 zu kompilieren und auszuführen. Allerdings habe ich folgende Probleme:

Auf dem Ubuntu-Rechner:

GCC kompiliert einwandfrei, aber beim Ausführen wird mir diese Eingabeaufforderung angezeigt:

Segmentation Fault (Core Dump).

Auf einem Windows-Computer:

VC9 kompiliert und läuft einwandfrei. GCC kompiliert einwandfrei, aber der Prozess wird beendet, wenn das Programm ausgeführt wird.

Brauche hier eure fachkundige Unterstützung. Hier ist mein Code:

#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;
}

Aktualisieren:

Der Verdienst geht anEliahdafür, dass sie mir nicht nur geholfen haben, den Fehler aufzuspüren, sondern mir auch das gdbBacktracing-Tool ( bt) vorgestellt haben, das beim Debuggen eines mit GCC kompilierten Programms sehr hilfreich ist. Hier ist die modifizierte Version, die ich nach einigem Ausprobieren erstellt habe:

#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;
}

Antwort1

ASegmentierungsfehlertritt auf, wenn ein Programm versucht, auf Speicher außerhalb des dafür zugewiesenen Bereichs zuzugreifen.

In diesem Fall kann ein erfahrener C-Programmierer erkennen, dass das Problem in der Zeile auftritt, in der sprintfaufgerufen wird. Wenn Sie jedoch nicht feststellen können, wo Ihr Segmentierungsfehler auftritt, oder wenn Sie sich nicht die Mühe machen möchten, den Code durchzulesen, umversuchenum es herauszufinden, können Sie Ihr Programm mit Debugsymbolen erstellen (mit gcc, das -gFlag macht das) und es dann durch einen Debugger laufen lassen.

Ich habe Ihren Quellcode kopiert und in eine Datei mit dem Namen eingefügt slope.c. Dann habe ich es wie folgt erstellt:

gcc -Wall -g -o slope slope.c

(Dies -Wallist optional. Es dient nur dazu, dass für mehr Situationen Warnungen ausgegeben werden. Dies kann auch dabei helfen, herauszufinden, was möglicherweise falsch ist.)

Anschließend habe ich das Programm im Debugger ausgeführt , gdbindem ich es zunächst gestartet gdb ./slopeund gdbdann im Debugger den folgenden runBefehl eingegeben habe:

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

(Machen Sie sich keine Sorgen wegen meiner you have broken Linux kernel i386 NX...- supportMeldung. Sie verhindert nicht, gdbdass das Programm effektiv zum Debuggen verwendet werden kann.)

Diese Information ist höchst kryptisch... und wenn Sie keine Debugsymbole für libc installiert haben, erhalten Sie eine noch kryptischere Nachricht, die eine hexadezimale Adresse anstelle des symbolischen Funktionsnamens enthält _IO_default_xsputn. Glücklicherweise spielt das keine Rolle, denn was wir wirklich wissen wollen, istwo in Ihrem Programmdas Problem besteht.

Die Lösung besteht also darin, zurückzublicken und zu sehen, welche Funktionsaufrufe vor dem jeweiligen Funktionsaufruf in einer Systembibliothek stattgefunden haben, in der das SIGSEGVSignal schließlich ausgelöst wurde.

gdb(und jeder Debugger) hat diese Funktion eingebaut: Sie heißtStapelüberwachungoderRückverfolgung. Ich verwende den btDebugger-Befehl, um einen Backtrace in folgendem Format zu generieren 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)

Sie können sehen, dass Ihre mainFunktion die calc_slopeFunktion aufruft (wie beabsichtigt) und dann calc_slopeaufruft sprintf, was (auf diesem System) mit Aufrufen einiger anderer verwandter Bibliotheksfunktionen implementiert wird.

Was Sie im Allgemeinen interessiert, ist der Funktionsaufrufin Ihrem Programmdas eine Funktion aufruftaußerhalb Ihres Programms. Sofern es keinen Fehler in der/den von Ihnen verwendeten Bibliothek(en) selbst gibt (in diesem Fall die Standard-C-Bibliothek, die libcin der Bibliotheksdatei bereitgestellt wird libc.so.6), liegt der Fehler, der den Absturz verursacht, in Ihrem Programm undoftwird beim oder nahe dem letzten Anruf seinin Ihrem Programm.

In diesem Fall ist das:

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

Dort ruft Ihr Programm auf sprintf. Wir wissen das, weil sprintfes der nächste Schritt ist. Aber auch ohne dass es das angibt, wissen Sie das, weildas passiert in Zeile 26, und dort heißt es:

... at slope.c:26

In Ihrem Programm enthält Zeile 26:

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

(Sie sollten immer einen Texteditor verwenden, der automatisch Zeilennummern anzeigt, zumindest für die Zeile, in der Sie sich gerade befinden. Dies ist sehr hilfreich bei der Interpretation von Fehlern zur Kompilierungszeit und von Laufzeitproblemen, die bei der Verwendung eines Debuggers auftreten.)

Wie inAntwort von Dennis Kaarsemaker, sist ein Ein-Byte-Array. (Nicht null, da der Wert, den Sie ihm zugewiesen haben, "", ein Byte lang ist, d. h., er ist gleich { '\0' }, genauso wie "Hello, world!\n"gleich ist { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }.)

Warum alsokönntefunktioniert dies auf einigen Plattformen noch (und anscheinend auch, wenn es mit VC9 für Windows kompiliert wird)?

Es wird oft behauptet, dass ein Fehler auftritt, wenn Sie Speicher zuordnen und dann versuchen, auf Speicher außerhalb davon zuzugreifen. Aber das stimmt nicht wirklich. Laut den technischen Standards von C und C++ führt dies tatsächlich zuundefiniertes Verhalten.

Mit anderen Worten: Alles kann passieren!

Dennoch sind manche Dinge wahrscheinlicher als andere. Warum scheint ein kleines Array auf dem Stapel bei manchen Implementierungen wie ein größeres Array auf dem Stapel zu funktionieren?

Dies hängt davon ab, wie die Stapelzuweisung implementiert wird, die von Plattform zu Plattform unterschiedlich sein kann. Ihre ausführbare Datei kann ihrem Stapel mehr Speicher zuweisen, als tatsächlich zu einem bestimmten Zeitpunkt verwendet werden soll. Manchmal können Sie dadurch an Speicherorte schreiben, die Sie nichtausdrücklichin Ihrem Code beansprucht. Es ist sehr wahrscheinlich, dass dies passiert, wenn Sie Ihr Programm in VC9 erstellen.

Jedoch,Sie sollten sich auch in VC9 nicht auf dieses Verhalten verlassen.Es könnte möglicherweise von verschiedenen Versionen von Bibliotheken abhängen, die auf verschiedenen Windows-Systemen vorhanden sein könnten. Abernoch wahrscheinlicherist das Problem, dass der zusätzliche Stapelspeicherplatz mit der Absicht zugewiesen wird, dass er tatsächlich verwendet wird, und soes kann tatsächlich verwendet werden.Dann erleben Sie den ganzen Albtraum des „undefinierten Verhaltens“, wobei in diesem Fall mehrere Variablen an derselben Stelle gespeichert werden könnten, wobei das Schreiben in die eine Variable die andere überschreibt ... aber nicht immer, denn manchmal werden Schreibvorgänge in Variablen in Registern zwischengespeichert und nicht sofort ausgeführt (oder Lesevorgänge in Variablen werden zwischengespeichert, oder es wird angenommen, dass eine Variable dieselbe ist wie vorher, weil der Compiler weiß, dass in den ihr zugewiesenen Speicher nicht über die Variable selbst geschrieben wurde).

Und das bringt mich zu der anderen wahrscheinlichen Möglichkeit, warum das Programm funktionierte, als es mit VC9 erstellt wurde. Es ist möglich und ziemlich wahrscheinlich, dassein Array oder eine andere Variablewurde tatsächlich von Ihrem Programm zugewiesen (was auch eine Zuweisung durch eine von Ihrem Programm verwendete Bibliothek beinhalten kann), um den Speicherplatz nach dem ein Byte großen Array zu verwenden s. Die Behandlung als ein Array, das länger als ein Byte ist, hätte also szur Folge, dass auf den Inhalt dieser Variablen/Arrays zugegriffen wird, was ebenfalls schlecht sein könnte.

Zusammenfassend lässt sich sagen, dass es bei einem solchen Fehlerglücklichum einen Fehler wie "Segmentierungsfehler" oder "Allgemeiner Schutzfehler" zu erhalten. Wenn SienichtWenn Sie das haben, werden Sie möglicherweise erst feststellen, wenn es zu spät ist, dass Ihr Programmundefiniertes Verhalten.

Antwort2

Hallo Pufferüberlauf!

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

Sie reservieren ein Byte für einen String auf dem Stack und schreiben dann mehr als ein Byte hinein. Und um das Ganze abzurunden, lesen Sie über das Ende dieses Arrays hinaus. Bitte lesen Sie ein C-Handbuch und insbesondere den Abschnitt über Strings und die Speicherreservierung für sie.

verwandte Informationen