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 gdb
Back-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;
}
Antworten:
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
sprintf
aufgerufen 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 (mitgcc
, das-g
Flag 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:(
-Wall
Dies 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,
gdb
indem ich zuerst ausgeführt habegdb ./slope
, umgdb
mit dem Programm zu beginnen , und dann, einmal im Debugger, dem Debugger denrun
Befehl gegeben habe:(Machen Sie sich keine Sorgen um meine
you have broken Linux kernel i386 NX
...support
Nachricht. Sie verhindert nicht, dassgdb
dieses 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
SIGSEGV
Signal schließlich ausgelöst wurde.gdb
(und jeder Debugger) hat diese Funktion eingebaut: Sie wird als Stack-Trace oder Backtrace bezeichnet . Ich benutze denbt
Debugger-Befehl, um eine Rückverfolgung zu generieren ingdb
:Sie können sehen, dass Ihre
main
Funktion diecalc_slope
Funktion (die Sie beabsichtigt haben)calc_slope
aufruft und dann aufruftsprintf
, 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
libc
bereitgestellte Standard-C-Bibliotheklibc.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:
Dort ruft Ihr Programm auf
sprintf
. Wir wissen das, weilsprintf
es 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:In Ihrem Programm enthält Zeile 26:
(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 ,
s
handelt 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 alsos
als 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.
quelle
Hallo Pufferüberlauf!
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.
quelle