Impossibile compilare con GCC su Ubuntu 12.04


9

Sto cercando di compilare ed eseguire il programma C di seguito sui miei computer Ubuntu e Windows con GCC e VC9. Tuttavia, sto affrontando i seguenti problemi:

Sulla macchina Ubuntu:

GCC si compila bene, ma quando eseguito, mi viene mostrato questo messaggio:

Segmentation Fault (Core Dump).

Sulla macchina Windows:

VC9 Compila e funziona bene. GCC viene compilato correttamente, ma il processo termina quando viene eseguito il programma.

Hai bisogno dell'assistenza di un esperto qui. Ecco il mio codice:

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

Aggiornare:

Il merito va a Eliah non solo per avermi aiutato a rintracciare l'errore, ma anche per avermi presentato gdbe il suo strumento di back-tracing ( bt) che sono così utili nel debug di un programma compilato in gcc. Ecco la versione modificata, ho elaborato dopo alcune prove ed errori:

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

3
Penso che non sia un problema di compilazione, ma piuttosto un problema di runtime. Otterresti più aiuto da StackOverflow .
Oaskamay,

Sei sicuro che questo è davvero funziona bene dopo essere stato compilato con VC9?
Eliah Kagan,

Sì, al 100%. ma non con gcc.
Prahlad Yeri,

@PrahladYeri Cool! Ho spiegato le ragioni di ciò nella mia risposta . (Ciò significa anche che dovremmo probabilmente considerare questa domanda sull'argomento, poiché riguarda il comportamento specifico di Ubuntu *. GCC in Windows mostra un comportamento comparabile ma non c'è un messaggio di errore ed è difficile sapere esattamente cosa sta succedendo lì - inoltre, quando GCC su Ubuntu e Microsoft Visual C ++ funzionano in modo diverso, penso che Ask Ubuntu sia un posto ragionevole per chiedere perché GCC su Ubuntu funziona così. Detto questo, ulteriori domande su come farlo nel modo giusto appartengono allo Stack Overflow.)
Eliah Kagan,

La modifica di una stringa letterale in C è un comportamento indefinito. Per favore, ricordalo.
jn1kk,

Risposte:


15

Si verifica un errore di segmentazione quando un programma tenta di accedere alla memoria al di fuori dell'area che è stata allocata per esso.

In questo caso, un programmatore C esperto può vedere che il problema si sta verificando nella linea in cui sprintfviene chiamato. Ma se non riesci a capire dove si sta verificando l'errore di segmentazione o se non vuoi disturbarti a leggere il codice per provare a capirlo, puoi costruire il tuo programma con simboli di debug (con gcc, il -gflag fa questo ) e quindi eseguirlo tramite un debugger.

Ho copiato il tuo codice sorgente e incollato in un file che ho nominato slope.c. Quindi l'ho costruito in questo modo:

gcc -Wall -g -o slope slope.c

( -WallÈ facoltativo. È solo per far sì che produca avvisi per più situazioni. Questo può aiutare a capire anche cosa potrebbe essere sbagliato.)

Quindi ho eseguito il programma nel debugger gdb eseguendo prima gdb ./slopedi iniziare gdbcon il programma e quindi, una volta nel debugger, dando il runcomando al debugger:

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

(Non preoccuparti per il mio you have broken Linux kernel i386 NX... supportmessaggio; non impedisce gdbdi essere utilizzato in modo efficace per il debug di questo programma.)

Queste informazioni sono altamente criptiche ... e se non hai installato i simboli di debug per libc, otterrai un messaggio ancora più criptico che ha un indirizzo esadecimale invece del nome della funzione simbolica _IO_default_xsputn. Fortunatamente, non importa, perché quello che vogliamo davvero sapere è dove si sta verificando il problema nel tuo programma .

Quindi, la soluzione è guardare indietro, per vedere quali chiamate di funzione hanno avuto luogo prima di quella particolare chiamata di funzione in una libreria di sistema in cui il SIGSEGVsegnale è stato finalmente attivato.

gdb(e qualsiasi debugger) ha questa funzione integrata: si chiama stack trace o backtrace . Uso il btcomando debugger per generare un backtrace 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)

Puoi vedere che la tua mainfunzione chiama la calc_slopefunzione (che hai previsto), e quindi calc_slopechiama sprintf, che è (su questo sistema) implementato con chiamate a un paio di altre funzioni di libreria correlate.

Ciò a cui sei generalmente interessato è la chiamata di funzione nel tuo programma che chiama una funzione al di fuori del tuo programma . A meno che non ci sia un bug nella libreria / librerie stesse che stai usando (in questo caso, la libreria C standard libcfornita dal file della libreria libc.so.6), il bug che causa l'arresto anomalo è nel tuo programma e spesso si troverà in o vicino al ultima chiamata nel tuo programma.

In questo caso, questo è:

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

Ecco dove il tuo programma chiama sprintf. Lo sappiamo perché sprintfè il prossimo passo in avanti. Ma anche senza affermarlo, lo sai perché è quello che succede sulla linea 26 , e dice:

... at slope.c:26

Nel tuo programma, la riga 26 contiene:

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

(È necessario utilizzare sempre un editor di testo che mostri automaticamente i numeri di riga, almeno per la riga attualmente in uso. Questo è molto utile nell'interpretazione sia degli errori di compilazione sia dei problemi di runtime rilevati durante l'utilizzo di un debugger.)

Come discusso nella risposta di Dennis Kaarsemaker , sè un array a un byte. (Non zero, poiché il valore che gli è stato assegnato "", è lungo un byte, ovvero è uguale a { '\0' }, nello stesso modo in cui "Hello, world!\n"è uguale a{ 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' } .)

Quindi, perché potrebbe funzionare ancora su alcune piattaforme (e apparentemente funziona quando compilato con VC9 per Windows)?

Le persone spesso dicono che quando si alloca memoria e quindi si tenta di accedere alla memoria al di fuori di essa, ciò produce un errore. Ma questo non è proprio vero. Secondo gli standard tecnici C e C ++, ciò che realmente produce è comportamento indefinito.

In altre parole, tutto può succedere!

Tuttavia, alcune cose sono più probabili di altre. Perché un piccolo array nello stack sembrerà funzionare, in alcune implementazioni, come un array più grande nello stack?

Questo dipende dal modo in cui viene implementata l'allocazione dello stack, che può variare da piattaforma a piattaforma. Il file eseguibile potrebbe allocare più memoria nel suo stack di quanto si intenda effettivamente utilizzare in qualsiasi momento. A volte ciò può consentire di scrivere in posizioni di memoria che non sono state esplicitamente rivendicate nel codice. È molto probabile che questo sia ciò che accade quando costruisci il tuo programma in VC9.

Tuttavia, non dovresti fare affidamento su questo comportamento anche in VC9. Potrebbe dipendere potenzialmente da diverse versioni di librerie che potrebbero esistere su diversi sistemi Windows. Ma è ancora più probabile il problema che lo spazio di stack aggiuntivo sia allocato con l'intenzione che verrà effettivamente utilizzato, e così potrebbe effettivamente essere utilizzato.Quindi si sperimenta l'intero incubo del "comportamento indefinito", in cui, in questo caso, più di una variabile potrebbe finire memorizzata nello stesso posto, dove scrivere su una sovrascrive l'altra ... ma non sempre, perché a volte scrive su variabili sono memorizzati nella cache nei registri e non vengono effettivamente eseguiti immediatamente (o le letture delle variabili possono essere memorizzate nella cache o si può presumere che una variabile sia la stessa di prima perché la memoria assegnata ad essa è nota dal compilatore per non essere stata scritta attraverso la variabile stessa).

E questo mi porta all'altra probabile possibilità per cui il programma ha funzionato quando è stato creato con VC9. È possibile, e un po 'probabile, che un array o un'altra variabile sia stata effettivamente allocata dal programma (che può includere l'assegnazione da una libreria utilizzata dal programma) per utilizzare lo spazio dopo l'array a un byte s. Quindi, trattare scome un array più lungo di un byte avrebbe l'effetto di accedere al contenuto di quello / quelle variabili / array, che potrebbe anche essere cattivo.

In conclusione, quando si verifica un errore come questo, è fortunato ad avere un errore come "Errore di segmentazione" o "Errore di protezione generale". Quando non lo possiedi, potresti non scoprire fino a quando non è troppo tardi che il tuo programma abbia un comportamento indefinito.


1
Grazie per una spiegazione così lucida. Questo è esattamente ciò di cui avevo bisogno .. !!
Prahlad Yeri,

9

Ciao buffer overflow!

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

Allocare un byte per una stringa nello stack e quindi procedere a scrivere più di un byte su di esso. E per finire, leggi oltre la fine di quella matrice. Leggere un manuale C e in particolare la sezione sulle stringhe e l'allocazione della memoria per esse.


Sì, ne sono venuto a conoscenza più tardi. Ma quando ho scritto questo, il compilatore VC9 non solo ha permesso, ma mi ha anche mostrato i risultati in modo corretto. Ho stampato le strisce e mi ha mostrato 4, non 1 !!
Prahlad Yeri,

Potresti anche consigliarmi come devo fare per correggere questo? Come avrai supposto dal codice, non ho modo di allocare anticipatamente dimensioni fisse a * s. La sua lunghezza è il numero di cifre nella variabile curr che non può essere conosciuto fino a quando non lo converto in una stringa !! ?
Prahlad Yeri,

Posso, ma dovresti davvero andare su Stack Overflow per consigli di programmazione, dato che qui è abbastanza offtopico.
Dennis Kaarsemaker,

1
@DennisKaarsemaker La domanda originale qui potrebbe non essere fuori tema poiché apparentemente comporta un comportamento diverso tra Ubuntu e un'altra piattaforma (e ho spiegato il motivo più probabile di ciò nella mia risposta ). Sono d'accordo sul fatto che le domande su come allocare correttamente le stringhe in C appartengono allo Stack Overflow e non qui.
Eliah Kagan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.