Kann nicht mit GCC unter Ubuntu 12.04 kompiliert werden

9

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

Auf Ubuntu-Maschine:

GCC wird gut kompiliert, aber wenn es ausgeführt wird, wird mir diese Eingabeaufforderung angezeigt:

Segmentation Fault (Core Dump).

Auf Windows-Computern:

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

Benötigen Sie hier Ihre kompetente 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:

Dank geht an Eliah, der mir nicht nur dabei geholfen hat, den Fehler zu verfolgen, sondern mir auch das gdbBack-Tracing-Tool ( bt) vorgestellt hat, das beim Debuggen eines von gcc kompilierten Programms sehr hilfreich ist. Hier ist die modifizierte Version, die ich nach einigem Ausprobieren ausgearbeitet 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;
}
Prahlad Yeri
quelle
3
Ich denke, es ist kein Kompilierungsproblem, sondern eher ein Laufzeitproblem. Sie würden mehr Hilfe von StackOverflow erhalten .
Oaskamay
Sind Sie sicher , dass diese wirklich läuft fein , nachdem sie mit VC9 zusammengestellt werden?
Eliah Kagan
Ja, 100%. aber nicht mit gcc.
Prahlad Yeri
@ PrahladYeri Cool! Ich habe Gründe dafür in meiner Antwort erklärt . (Dies bedeutet auch, dass wir diese Frage wahrscheinlich thematisch betrachten sollten, da es sich um Ubuntu * -spezifisches * Verhalten handelt. GCC unter Windows zeigt ein vergleichbares Verhalten, aber es gibt keine Fehlermeldung und es ist schwer genau zu wissen, was dort passiert - außerdem wann GCC unter Ubuntu und Microsoft Visual C ++ funktionieren unterschiedlich. Ich denke, Ask Ubuntu ist ein vernünftiger Ort, um zu fragen, warum GCC unter Ubuntu so funktioniert. Vor diesem Hintergrund gehören weitere Fragen dazu, wie man es richtig macht, zu Stack Overflow.)
Eliah Kagan
Das Ändern eines Zeichenfolgenliteral in C ist ein undefiniertes Verhalten. Bitte denken Sie daran.
jn1kk

Antworten:

15

Ein Segmentierungsfehler tritt auf, wenn ein Programm versucht, auf Speicher außerhalb des ihm zugewiesenen Bereichs zuzugreifen.

In diesem Fall kann ein erfahrener C-Programmierer sehen, dass das Problem in der Zeile auftritt, in der er sprintfaufgerufen wird. Wenn Sie jedoch nicht erkennen können, wo Ihr Segmentierungsfehler auftritt, oder wenn Sie sich nicht die Mühe machen möchten, den Code zu lesen, um ihn herauszufinden, können Sie Ihr Programm mit Debug-Symbolen erstellen (mit gcc, das -gFlag tut dies ) und führen Sie es dann durch einen Debugger.

Ich habe Ihren Quellcode kopiert und in eine von mir genannte Datei eingefügt slope.c. Dann habe ich es so gebaut:

gcc -Wall -g -o slope slope.c

( -WallDies ist optional. Es dient nur dazu, Warnungen für weitere Situationen zu erstellen. Dies kann auch dazu beitragen, herauszufinden, was möglicherweise falsch ist.)

Dann habe ich das Programm im Debugger ausgeführt, gdbindem ich zuerst ausgeführt habe gdb ./slope, um gdbmit dem Programm zu beginnen , und dann, einmal im Debugger, dem Debugger den runBefehl gegeben 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 um meine you have broken Linux kernel i386 NX... supportNachricht. Sie verhindert nicht, dass gdbdieses Programm effektiv zum Debuggen verwendet wird.)

Diese Informationen sind sehr kryptisch ... und wenn Sie keine Debug-Symbole für libc installiert haben, erhalten Sie eine noch kryptischere Nachricht mit einer hexadezimalen Adresse anstelle des symbolischen Funktionsnamens _IO_default_xsputn. Zum Glück spielt es keine Rolle, denn wir möchten wirklich wissen, wo in Ihrem Programm das Problem auftritt.

Die Lösung besteht also darin, rückwärts zu schauen, um zu sehen, welche Funktionsaufrufe vor diesem bestimmten Funktionsaufruf in einer Systembibliothek stattgefunden haben, in der das SIGSEGVSignal schließlich ausgelöst wurde.

gdb(und jeder Debugger) hat diese Funktion eingebaut: Sie wird als Stack-Trace oder Backtrace bezeichnet . Ich benutze den btDebugger-Befehl, um eine Rückverfolgung zu generieren in 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 (die Sie beabsichtigt haben) calc_slopeaufruft und dann aufruft sprintf, die (auf diesem System) mit Aufrufen einiger anderer verwandter Bibliotheksfunktionen implementiert wird.

Was Sie allgemein interessiert, ist der Funktionsaufruf in Ihrem Programm , der eine Funktion außerhalb Ihres Programms aufruft . Sofern in der Bibliothek / den Bibliotheken selbst, die Sie verwenden, kein Fehler vorliegt (in diesem Fall die in der Bibliotheksdatei libcbereitgestellte Standard-C-Bibliothek libc.so.6), befindet sich der Fehler, der den Absturz verursacht, in Ihrem Programm und befindet sich häufig in oder in der Nähe von letzter Aufruf in 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 dies angegeben ist, wissen Sie das, denn genau das passiert in Zeile 26 und es heißt:

... 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 sowohl von Fehlern bei der Kompilierung als auch von Laufzeitproblemen, die bei der Verwendung eines Debuggers auftreten.)

Wie in Dennis Kaarsemakers Antwort erläutert , shandelt es sich um ein Ein-Byte-Array. (Nicht Null, da der Wert , den Sie zugewiesen haben, ""ist ein Byte lang, das heißt, ist es gleich { '\0' }, in der gleichen Art und Weise , die "Hello, world!\n"zu gleich { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }.)

Warum könnte dies auf einer Plattform immer noch funktionieren (und anscheinend auch, wenn es mit VC9 für Windows kompiliert wurde)?

Die Leute sagen oft, dass, wenn Sie Speicher zuweisen und dann versuchen, auf Speicher außerhalb davon zuzugreifen, dies einen Fehler erzeugt. Das stimmt aber nicht wirklich. Nach den technischen Standards von C und C ++ führt dies tatsächlich zu undefiniertem Verhalten.

Mit anderen Worten, alles kann passieren!

Dennoch sind einige Dinge wahrscheinlicher als andere. Warum scheint ein kleines Array auf dem Stapel in einigen 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 variieren kann. Ihre ausführbare Datei weist ihrem Stapel möglicherweise mehr Speicher zu, als tatsächlich zu einem bestimmten Zeitpunkt verwendet werden soll. Manchmal können Sie auf diese Weise in Speicherorte schreiben, auf die Sie in Ihrem Code keinen expliziten Anspruch erhoben haben. Es ist sehr wahrscheinlich, dass dies passiert, wenn Sie Ihr Programm in VC9 erstellen.

Allerdings sollten Sie nicht verlassen sich auf dieses Verhalten auch in VC9. Dies kann möglicherweise von verschiedenen Versionen von Bibliotheken abhängen, die auf verschiedenen Windows-Systemen vorhanden sein können. Aber noch wahrscheinlicher ist das Problem , dass die zusätzlichen Stapelspeicher mit der Absicht zugeordnet ist , dass es tatsächlich verwendet werden, und so kann es tatsächlich verwendet werden.Dann erleben Sie den Albtraum eines "undefinierten Verhaltens", bei dem in diesem Fall mehr als eine Variable an derselben Stelle gespeichert werden kann, an der das Schreiben in eine Variable die andere überschreibt ... aber nicht immer, weil manchmal in Variablen geschrieben wird werden in Registern zwischengespeichert und nicht sofort ausgeführt (oder Lesevorgänge in Variablen können zwischengespeichert werden, oder es kann angenommen werden, dass eine Variable dieselbe ist wie zuvor, da dem Compiler bekannt ist, dass der ihr zugewiesene Speicher nicht durchgeschrieben wurde die Variable selbst).

Und das bringt mich zu der anderen wahrscheinlichen Möglichkeit, warum das Programm bei der Erstellung mit VC9 funktioniert hat. Es ist möglich und etwas wahrscheinlich, dass ein Array oder eine andere Variable tatsächlich von Ihrem Programm zugewiesen wurde (einschließlich der Zuweisung durch eine Bibliothek, die Ihr Programm verwendet), um den Speicherplatz nach dem Ein-Byte-Array zu verwenden s. Wenn Sie also sals Array behandeln, das länger als ein Byte ist, hat dies den Effekt, dass Sie auf den Inhalt dieser / dieser Variablen / Arrays zugreifen, was ebenfalls schlecht sein kann.

Wenn Sie einen solchen Fehler haben, können Sie glücklicherweise einen Fehler wie "Segmentierungsfehler" oder "Allgemeiner Schutzfehler" erhalten. Wenn Sie das nicht haben, werden Sie möglicherweise erst herausfinden, wenn es zu spät ist, dass Ihr Programm ein undefiniertes Verhalten aufweist.

Eliah Kagan
quelle
1
Vielen Dank für diese klare Erklärung. Genau das brauchte ich .. !!
Prahlad Yeri
9

Hallo Pufferüberlauf!

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

Sie weisen einer Zeichenfolge auf dem Stapel ein Byte zu und schreiben dann mehr als ein Byte darauf. Und um das Ganze abzurunden, lesen Sie über das Ende dieses Arrays hinaus. Bitte lesen Sie ein C-Handbuch und insbesondere den Abschnitt über Zeichenfolgen und die Zuweisung von Speicher.

Dennis Kaarsemaker
quelle
Ja, das habe ich später erfahren. Aber als ich das schrieb, erlaubte der VC9-Compiler nicht nur, sondern zeigte mir auch die Ergebnisse richtig. Ich habe die Strlen gedruckt und es zeigte mir 4, nicht 1 !!
Prahlad Yeri
Können Sie mir auch raten, wie ich das korrigieren soll? Wie Sie aus dem Code vermutet haben müssen, habe ich keine Möglichkeit, * s im Voraus eine feste Größe zuzuweisen. Seine Länge ist die Anzahl der Stellen in der Variablen curr, die erst bekannt werden können, wenn ich sie in einen String konvertiere !! ?
Prahlad Yeri
Ich kann, aber Sie sollten sich wirklich an Stack Overflow wenden, um Ratschläge zur Programmierung zu erhalten, da dies hier ziemlich unauffällig ist.
Dennis Kaarsemaker
1
@DennisKaarsemaker Die ursprüngliche Frage hier ist möglicherweise nicht vom Thema abweichend, da es sich anscheinend um ein Verhalten handelt, das sich zwischen Ubuntu und einer anderen Plattform unterscheidet (und den wahrscheinlichsten Grund dafür habe ich in meiner Antwort erläutert ). Ich bin damit einverstanden, dass Fragen zum richtigen Zuweisen von Zeichenfolgen in C zum Stapelüberlauf gehören und nicht hier.
Eliah Kagan