Perché strcpy attiva un errore di segmentazione con le variabili globali?


7

Così ho qualche codice C:

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

/* putting one of the "char*"s here causes a segfault */ 
void main() { 
    char* path = "/temp"; 
    char* temp; 
    strcpy(temp, path); 
} 

Questo compila, corre, e si comporta come sembra. Tuttavia, se uno o entrambi i puntatori di caratteri vengono dichiarati come variabile globale, strcpy genera un errore di segmentazione. Perché succede? Evidentemente c'è un errore nella mia comprensione dell'ambito.

  0

Poiché non penso che risolverà il problema, mi limiterò a commentare che strncpy è altamente raccomandato su strcpy. 23 set. 082008-09-23 18:36:46

  0

Josh Gagnon: In realtà, strncpy non pone un terminatore null quando la lunghezza della stringa di input è> = il buffer. strcpy è perfettamente sicuro se sai che il buffer è abbastanza grande. Altrimenti usa 'snprintf (buffer, buffer_len,"% s ", src)', dato che snprintf mette sempre un terminatore nullo (assicurati solo buffer_len> 0). 05 ago. 112011-08-05 06:40:39

  0

@Josh: preferisco 'strlcpy'. Sfortunatamente glibc non lo supporta, quindi non ho molte possibilità di usarlo. Suppongo che potrei sempre implementare la mia implementazione personale e aggiungerla alla mia libreria personale di intestazioni di malloc di null-checking e funzioni relative ai file, ma mi infastidisce ancora che molte versioni di Unix ce l'hanno mentre Linux in genere non lo fa. 05 ago. 112011-08-05 15:42:30

  0

In effetti mi ha quasi fregato lo scorso semestre del college, mentre stavo testando un programma per il quarto e ultimo grado di compiti a casa per uno dei miei corsi (che avevo già perso uno dei compiti a casa pensando che fosse dovuto tre giorni dopo in realtà era) remoto e accidentalmente connesso al server Unix piuttosto che a quello Linux senza accorgersene, e compilato e gestito bene lì, ma quando arriva l'esame ricevo una e-mail da uno dei TA della classe che dice "il tuo programma non verrà eseguito o compilato nella casella di test [Linux] ". 05 ago. 112011-08-05 15:45:38

  0

(Fortunatamente avevo incluso righe per 'strlcat' e' strncat' [una differenza simile a quella tra 'strlcpy' e' strncpy' riguardo a come funzionano e cosa li implementa] versioni dell'operazione che veniva eseguita, quindi era solo questione di commentare una riga e decommentare la riga dopo.) ... Non mi aspettavo che prendesse così tanti commenti. 05 ago. 112011-08-05 15:47:57

16

Come altri posters menzionati, la radice del problema è che temp non è inizializzato. Quando viene dichiarato come variabile automatica nello stack, conterrà qualsiasi traccia di spazzatura nella relativa posizione di memoria. Apparentemente per il compilatore + CPU + OS in esecuzione, la spazzatura in quella posizione è un puntatore valido. La strcpy "riesce" in quanto non segfault, ma in realtà ha copiato una stringa in qualche posizione arbitraria altrove nella memoria. Questo tipo di problema di corruzione della memoria colpisce la paura nei cuori dei programmatori C ovunque poiché è straordinariamente difficile eseguire il debug.

Quando si sposta la dichiarazione della variabile temporanea in ambito globale, questa viene posizionata nella sezione BSS e automaticamente azzerata. I tentativi di dereferenziamento * temp risultano in un segfault.

Quando si sposta il percorso * verso l'ambito globale, quindi * temp si sposta di una posizione nella pila. La spazzatura in quella posizione apparentemente non è un puntatore valido, e quindi il dereferenziamento * temp risulta in un segfault.

  0

Come previsto, lo scambio dell'ordine delle dichiarazioni delle variabili rende il programma segfault. Grazie! 23 set. 082008-09-23 19:58:04


8

si è dimenticato di allocare e inizializzare temp:

temp = (char *)malloc(TEMP_SIZE); 

Basta assicurarsi TEMP_SIZE è abbastanza grande. È inoltre possibile calcolare questo a run-time, quindi assicurarsi che la dimensione è abbastanza (dovrebbe essere almeno strlen (percorso))

  0

Non è necessario inizializzare la memoria su temp - strcpy si prenderà cura di questo, anche se è solo l'impostazione del termine null iniziale. 23 set. 082008-09-23 18:38:36

  0

Inoltre, deve essere almeno un strlen (percorso) +1 per adattarsi al termine null o Cattivo risultato T (M). 23 set. 082008-09-23 18:39:23

  0

Vuol dire che non hai bisogno della riga "temp [0] = 0", dal momento che strcpy() aggiungerà il terminatore NULL per te. 23 set. 082008-09-23 18:45:32

  0

strcpy non si preoccupa di cosa punta la stringa di destinazione finché è abbastanza grande da contenere il risultato. Temp [0] = 0 non ha senso e anche se la stringa sorgente è vuota, sarà comunque impostata da strcpy. Se vuoi cancellare l'intera stringa, devi usare memset (o qualcos'altro) 23 set. 082008-09-23 18:47:43

  0

Spiacente, frainteso 'strcpy' per 'strcat'. Ho risolto la mia risposta 23 set. 082008-09-23 18:49:59

  0

Non c'è bisogno di lanciare il puntatore restituito da malloc - malloc restituisce un puntatore vuoto e quindi non è necessario alcun cast. Inoltre, il cast potrebbe nuocerti perché potrebbe nascondere problemi con malloc che non viene definito correttamente (come la definizione implicita di malloc sta restituendo un int) a questa riga di codice. 04 giu. 092009-06-04 15:10:29


1

La cosa importante da notare:
destinazione stringa dest deve essere sufficientemente grande per ricevere il copia.
Nella tua situazione, temp non ha memoria allocata per la copia.

copiato dalla pagina man di strcpy:

DESCRIPTION 
    The strcpy() function copies the string pointed to by src (including 
    the terminating '\0' character) to the array pointed to by dest. The 
    strings may not overlap, and the destination string dest must be large 
    enough to receive the copy. 

9

variabile La temperatura non indica alcuna memorizzazione (memoria) ed è inizializzato.

se temp è dichiarato come char temp[32]; quindi il codice funzionerebbe indipendentemente da dove è stato dichiarato. Tuttavia, ci sono altri problemi con la dichiarazione di temp con una dimensione fissa come quella, ma questa è una domanda per un altro giorno.

Ora, perché si blocca quando viene dichiarato globalmente e non localmente. Fortuna ...

Se dichiarato localmente, il valore di temp deriva dal valore che potrebbe mai essere in pila in quel momento. È una fortuna che punti a un indirizzo che non causa un crash. Tuttavia, è la memoria di cestinazione utilizzata da qualcun altro.

Se dichiarati globalmente, nella maggior parte dei processori queste variabili verranno memorizzate in segmenti di dati che utilizzeranno pagine a richiesta zero. Pertanto char *temp appare come se fosse stato dichiarato char *temp=0.


1

Si sta invocando un comportamento non definito, poiché non si sta inizializzando la variabile temp. Indica una posizione casuale in memoria, quindi il tuo programma potrebbe funzionare, ma molto probabilmente segfault. È necessario avere la stringa di destinazione sia un array, o fa puntare a memoria dinamica:

// Make temp a static array of 256 chars 
char temp[256]; 
strncpy(temp, 256, path); 

// Or, use dynamic memory 
char *temp = (char *)malloc(256); 
strncpy(temp, 256, path); 

Inoltre, utilizzare strncpy() invece di strcpy() per evitare sovraccarichi del buffer.

  0

Adam, è una buona idea avere tutti quei numeri magici in giro? ;-) 23 set. 082008-09-23 18:43:12

  0

che ha ancora bug in quanto se la stringa sorgente è di 256 lunghezze, allora le stringhe non hanno una terminazione nulla. 23 set. 082008-09-23 18:45:08

  0

Non è necessario eseguire il cast del puntatore restituito da malloc-malloc restituisce un puntatore vuoto e quindi non è necessario alcun cast. Inoltre, il cast potrebbe nuocerti perché potrebbe nascondere problemi con malloc che non viene definito correttamente (come la definizione implicita di malloc sta restituendo un int) a questa riga di codice. 04 giu. 092009-06-04 15:11:20

  0

@Daniel Papasian: il cast non è necessario in C, ma è necessario in C++ e di solito mi piace rendere il mio codice C compatibile con C++ come possibile. L'uso di funzioni definite implicitamente senza dichiararle è deprecato in C e non dovrebbe essere usato nel nuovo codice C. Compilo sempre con -Wall, che include la dichiarazione delle funzioni esplicite. 04 giu. 092009-06-04 15:28:08


3

Come accennato in precedenza, si è dimenticato di allocare spazio per temp. Preferisco strdup a malloc+strcpy. Fa quello che vuoi fare.

  0

Non sapevo di strdup. Grazie. 23 set. 082008-09-23 20:03:06


2

No, questo non funziona a prescindere dalle variabili: sembra proprio che l'abbia fatto perché hai ottenuto (dis) fortuna. È necessario allocare spazio per memorizzare il contenuto della stringa, piuttosto che lasciare la variabile non inizializzata.

Le variabili non inizializzate nello stack puntano a posizioni casuali della memoria.Se questi indirizzi sono validi, il tuo codice calpesterà tutto ciò che era presente, ma non riceverai un errore (ma potrebbero verificarsi problemi di corruzione della memoria in qualche altro punto del tuo codice).

Globalmente falliscono perché solitamente vengono impostati su schemi specifici che puntano alla memoria non mappata. Tentando di dereferenziare questi ti dà immediatamente un segfault (che è meglio - lasciandolo in seguito rende il bug molto difficile da rintracciare).


2

mi piacerebbe riscrivere primo frammento di Adamo come

// Make temp a static array of 256 chars 
char temp[256]; 
strncpy(temp, sizeof(temp), path); 
temp[sizeof(temp)-1] = '\0'; 

questo modo:

1. don't have magic numbers laced through the code, and 
2. you guarantee that your string is null terminated. 

Il secondo punto è la perdita dell'ultimo carattere della stringa di origine, se è > = 256 caratteri.

  0

Quando si utilizza wchar_t, questo non funziona. Usare sizeof funziona solo con i caratteri. Lo so, questa è una domanda sui caratteri, ma trovo un sacco di bug in cui le persone usano sizeof con wchar_t. 23 set. 082008-09-23 19:25:48