“Durata della vita” della stringa letterale in C

Non sarebbe inaccessibile il puntatore restituito dalla seguente funzione?

char *foo( int rc ) { switch (rc) { case 1: return("one"); case 2: return("two"); default: return("whatever"); } } 

Quindi la durata di una variabile locale in C / C ++ è praticamente solo all’interno della funzione, giusto? Il che significa, dopo che char* foo(int) termina, il puntatore che ritorna non significa più nulla?

Sono un po ‘confuso sulla durata della var locale. Qualcuno potrebbe darmi un buon chiarimento?

Sì, la durata di una variabile locale è all’interno dell’ambito ( { , } ) in cui è stata creata.
Le variabili locali hanno una memoria automatica o locale.
Automatico perché vengono automaticamente distrutti quando termina l’ambito entro il quale sono stati creati.

Tuttavia, quello che hai qui è un letterale stringa, che è allocato in una memoria di sola lettura definita dall’implementazione. I valori letterali delle stringhe sono diversi dalle variabili locali e rimangono attivi per tutta la durata del programma. Hanno una durata [Ref 1] di durata statica .

Una parola di caucanvas!
Tuttavia, si noti che qualsiasi tentativo di modificare il contenuto di una stringa letterale è un comportamento non definito. I programmi utente non sono autorizzati a modificare il contenuto di una stringa letterale.
Quindi, è sempre incoraggiato l’uso di un const dichiarando una stringa letterale.

 const char*p = "string"; 

invece di,

 char*p = "string"; 

Infatti, in C ++ è deprecato dichiarare una stringa letterale senza la const se non in c. Tuttavia, dichiarare una stringa letterale con un const ti dà il vantaggio che i compilatori di solito ti danno un avvertimento nel caso in cui tenti di modificare la stringa letterale nel secondo caso.

Programma di esempio :

 #include int main() { char *str1 = "string Literal"; const char *str2 = "string Literal"; char source[]="Sample string"; strcpy(str1,source); //No warning or error just Uundefined Behavior strcpy(str2,source); //Compiler issues a warning return 0; } 

Produzione:

cc1: avvertenze trattate come errori
prog.c: nella funzione ‘main’:
prog.c: 9: errore: passare l’argomento 1 di ‘strcpy’ scarta i qualificatori dal tipo di destinazione del puntatore

Si noti che il compilatore avverte per il secondo caso ma non per il primo.


EDIT: per rispondere alla Q che viene posta da un paio di utenti qui:

Qual è l’accordo con i letterali integrali?
In altre parole, questo codice è valido:

 int *foo() { return &(2); } 

La risposta è, No questo codice non è valido, è mal formato e darà un errore del compilatore.
Qualcosa di simile a:

 prog.c:3: error: lvalue required as unary '&' operand 

I valori letterali delle stringhe sono valori-l, ovvero: puoi prendere l’indirizzo di una stringa letterale ma non puoi modificarne il contenuto.
Tuttavia, tutti gli altri letterali ( int , float , char ecc.) Sono valori -r (c standard usa il termine il valore di un’espressione per questi) e il loro indirizzo non può essere preso affatto.


[Ref 1] C99 standard 6.4.5 / 5 “String Literals – Semantics”:

Nella fase di traduzione 7, un byte o un codice di valore zero viene aggiunto ad ogni sequenza di caratteri multibyte risultante da una stringa letterale o letterale. La sequenza di caratteri multibyte viene quindi utilizzata per inizializzare un array di durata e durata di memorizzazione statica sufficiente per contenere la sequenza . Per i letterali stringa di caratteri, gli elementi dell’array hanno carattere char e sono inizializzati con i singoli byte della sequenza di caratteri multibyte; per i letterali a stringa ampia, gli elementi dell’array hanno tipo wchar_t e sono inizializzati con la sequenza di caratteri ampi …

Non è specificato se questi array siano distinti purché i loro elementi abbiano i valori appropriati. Se il programma tenta di modificare tale array, il comportamento non è definito .

È valido, i valori letterali delle stringhe hanno una durata di archiviazione statica, quindi il puntatore non pende.

Per C, che è obbligatorio nella sezione 6.4.5, paragrafo 6:

Nella fase di traduzione 7, un byte o un codice di valore zero viene aggiunto ad ogni sequenza di caratteri multibyte risultante da una stringa letterale o letterale. La sequenza di caratteri multibyte viene quindi utilizzata per inizializzare un array di durata e durata di memorizzazione statica sufficiente per contenere la sequenza.

E per C ++ nella sezione 2.14.5, paragrafi 8-11:

8 I letterali stringa ordinari e i valori letterali stringa UTF-8 vengono anche chiamati valori letterali stringa ristretti. Un letterale stringa stretto ha tipo “array of n const char “, dove n è la dimensione della stringa come definito di seguito e ha durata di archiviazione statica (3.7).

9 Un letterale stringa che inizia con u, ad esempio u"asdf" , è un char16_t letterale stringa char16_t . Un letterale stringa char16_t ha tipo “array of n const char16_t “, dove n è la dimensione della stringa come definito di seguito; ha una durata di archiviazione statica ed è inizializzato con i caratteri specificati. Un singolo c-char può produrre più di un carattere char16_t sotto forma di coppie surrogate.

10 Un letterale stringa che inizia con U, ad esempio U"asdf" , è un char32_t letterale stringa char32_t . Un letterale stringa char32_t ha tipo “array of n const char32_t “, dove n è la dimensione della stringa come definito di seguito; ha una durata di archiviazione statica ed è inizializzato con i caratteri specificati.

11 Un letterale stringa che inizia con L, come L"asdf" , è un letterale a stringa ampia. Un letterale stringa di ampie dimensioni ha tipo “array of n const wchar_t “, dove n è la dimensione della stringa come definito di seguito; ha una durata di archiviazione statica ed è inizializzato con i caratteri specificati.

I valori letterali stringa sono validi per l’intero programma (e non vengono allocati non lo stack), quindi saranno validi.

Inoltre, i valori letterali stringa sono di sola lettura, quindi (per un buon stile) forse dovresti cambiare foo in const char *foo(int)

Buona domanda. In generale, avresti ragione, ma il tuo esempio è l’eccezione. Il compilatore alloca staticamente la memoria globale per una stringa letterale. Pertanto, l’indirizzo restituito dalla tua funzione è valido.

Che sia così è una caratteristica piuttosto comoda di C, non è vero? Consente a una funzione di restituire un messaggio precomposto senza costringere il programmatore a preoccuparsi della memoria in cui è memorizzato il messaggio.

Vedi anche l’osservazione corretta di @ asaelr.

Sì, è un codice valido, caso 1 di seguito. Puoi tranquillamente restituire stringhe C da una funzione in almeno questi modi:

  • const char* a una stringa letterale. Non può essere modificato, non deve essere liberato dal chiamante. Raramente utile allo scopo di restituire un valore predefinito, a causa del problema di liberazione descritto di seguito. Potrebbe avere senso se in realtà è necessario passare da qualche parte un puntatore a funzione, quindi è necessaria una funzione che restituisca una stringa ..

  • char* o const char* nel buffer char statico. Non deve essere liberato dal chiamante. Può essere modificato (sia dal chiamante se non const, o dalla funzione che lo restituisce), ma una funzione che restituisce questo non può (facilmente) avere più buffer, quindi non (facilmente) thread safe, e il chiamante potrebbe dover copiare il reso valore prima di chiamare di nuovo la funzione.

  • char* a un buffer allocato con malloc . Può essere modificato, ma in genere deve essere esplicitamente liberato dal chiamante e ha un overhead di allocazione dell’heap. strdup è di questo tipo.

  • const char* o char* in un buffer, che è stato passato come argomento alla funzione (il puntatore restituito non deve puntare al primo elemento del buffer degli argomenti). Lascia la responsabilità del buffer / gestione della memoria al chiamante. Molte funzioni di stringa standard sono di questo tipo.

Un problema è che mescolare questi in una funzione può essere complicato. Il chiamante deve sapere come deve gestire il puntatore restituito, quanto è valido e se il chiamante deve liberarlo, e non c’è (bello) modo di determinarlo in fase di runtime. Quindi, ad esempio, non è ansible avere una funzione, che a volte restituisce un puntatore a un buffer assegnato all’heap che il chiamante deve free , e talvolta un puntatore a un valore predefinito da letterale stringa, che il chiamante non deve free .

Le variabili locali sono valide solo nell’ambito dell’ambito in cui vengono dichiarate, tuttavia non si dichiarano variabili locali in quella funzione.

È perfettamente valido restituire un puntatore a una stringa letterale da una funzione, poiché esiste una stringa letterale durante l’intera esecuzione del programma, proprio come farebbe una variabile static o una variabile globale.

Se ti stai preoccupando di ciò che stai facendo potrebbe non essere valido indefinito, dovresti triggersre gli avvisi del compilatore per vedere se c’è effettivamente qualcosa che stai facendo male.

str non sarà mai puntatore penzolante. Because it points to static address dove risiedono i valori letterali delle stringhe. Sarà per lo più di readonly e global per il programma quando verrà caricato. Anche se provi a liberare o modificare, genererà un segmentation fault su piattaforms con protezione della memoria .

Una variabile locale è allocata nello stack. Al termine della funzione, la variabile esce dall’ambito e non è più accessibile nel codice. Tuttavia, se si dispone di un puntatore globale (o semplicemente non ancora fuori ambito) assegnato a tale variabile, esso punterà al punto dello stack in cui si trovava tale variabile. Potrebbe essere un valore utilizzato da un’altra funzione o un valore senza significato.

Nell’esempio sopra riportato da te, stai effettivamente restituendo i puntatori assegnati a qualsiasi funzione che chiama quanto sopra. Quindi non diventerebbe un puntatore locale. E inoltre i puntatori che devono essere restituiti, la memoria viene allocata nel segmento globale.

Ringraziandovi,

Viharri PL V.