"żywotność" ciągów literalnych w C

Czy wskaźnik zwracany przez następującą funkcję nie byłby niedostępny?

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

Więc czas życia zmiennej lokalnej w C / C++ jest praktycznie tylko w ramach funkcji, prawda? Co oznacza, że po zakończeniu char* foo(int) wskaźnik, który zwraca, nie ma już znaczenia?

Jestem trochę zdezorientowany co do życia lokalnego var. Czy ktoś mógłby mi coś wyjaśnić?

Author: RiaD, 2012-04-02

9 answers

Tak, Żywotność zmiennej lokalnej mieści się w zakresie({,}) w którym jest tworzony.
Zmienne lokalne mają magazyn automatyczny lub lokalny.
automatyczne , ponieważ są one automatycznie niszczone po zakończeniu zakresu, w którym są tworzone.

Jednakże, to, co tutaj masz, to literał Łańcuchowy, który jest alokowany w pamięci tylko do odczytu zdefiniowanej w implementacji. Literały ciągów różnią się od zmiennych lokalnych i pozostają żywe w całym programie całe życie.Mają statyczny czas trwania [Ref 1] życie.

Słowo przestrogi!
Należy jednak pamiętać, że każda próba modyfikacji zawartości literału łańcuchowego jest niezdefiniowanym zachowaniem. Programy użytkownika nie mogą modyfikować zawartości ciągu znaków.
Dlatego zawsze zaleca się używanie const podczas deklarowania literału łańcuchowego.

const char*p = "string"; 

Zamiast

char*p = "string";    

W rzeczywistości, w C++ jest przestarzałe, aby zadeklarować literał Łańcuchowy Bez const, choć nie w c. jednak deklarowanie literału łańcuchowego za pomocą const daje tę przewagę, że Kompilatory Zwykle ostrzegają cię, jeśli spróbujesz zmodyfikować literał Łańcuchowy w drugim przypadku.

Przykładowy program:

#include<string.h> 
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; 
} 

Wyjście:

Cc1: Ostrzeżenia traktowane jako błędy
prog.c: w funkcji "main":
prog.c: 9: error: passing argument 1 of' strcpy ' discards qualifiers from pointer target type

Zauważ, że kompilator ostrzega o drugim przypadku, ale nie o pierwszym.


EDIT: aby odpowiedzieć na pytanie zadawane przez kilku użytkowników tutaj:

O co chodzi z integral literals?
Innymi słowy czy ten kod jest poprawny:

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

Odpowiedź brzmi: nie ten kod nie jest poprawny, jest źle uformowany i spowoduje błąd kompilatora.
Coś w stylu:

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

Literały ciągów to l-wartości, czyli: możesz przyjąć adres literalnego łańcucha znaków, ale nie możesz zmienić jego zawartości.
Jednak wszelkie inne literały(int,float,char etc) są wartościami r (standard c używa dla nich terminu wartość wyrażenia) i ich adres nie może być w ogóle wzięty.


[Ref 1]C99 standard 6.4.5 / 5 "String Literals-semantyka":

W fazie translacji 7 do każdego wielobajtu dołączany jest bajt lub kod o wartości zero sekwencja znaków, która wynika z ciągu literałów lub literałów. wielobajtowy ciąg znaków jest następnie używany do inicjalizacji tablicy statycznego czasu przechowywania i długości wystarczającej do przechowywania sekwencji . W przypadku literałów ciągów znaków elementy tablicy mają typ char i są inicjalizowane pojedynczymi bajtami wielobajtowej sekwencji znaków; w przypadku literałów ciągów szerokich elementy tablicy mają typ wchar_t i są inicjowane sekwencją wide postaci...

Nie jest sprecyzowane, czy te tablice są odrębne, pod warunkiem, że ich elementy mają odpowiednie wartości. jeśli program próbuje zmodyfikować taką tablicę, zachowanie jest niezdefiniowane .

 77
Author: Alok Save,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-19 19:17:11

Jest poprawne, literały łańcuchów mają statyczny czas przechowywania, więc wskaźnik nie zwisa.

Dla C, który jest określony w sekcji 6.4.5, ust. 6:

W fazie translacji 7, bajt lub kod o wartości zero jest dołączany do każdego wielobajtowego ciągu znaków, który wynika z literała lub literałów. Multibajtowy ciąg znaków jest następnie używany do inicjalizacji tablicy statycznego czasu przechowywania i długości wystarczającej, aby zawierać Sekwencja.

I dla C++ w sekcji 2.14.5, paragrafy 8-11:

8 zwykłych literałów łańcuchowych i UTF-8 literałów łańcuchowych są również określane jako wąskie literały łańcuchowe. Wąski łańcuch znaków ma typ " array of n const char", gdzie n jest wielkością łańcucha zdefiniowanego poniżej i ma statyczny czas przechowywania (3.7).

9 literał Łańcuchowy zaczynający się od u, taki jak u"asdf", jest char16_t literałem łańcuchowym. A char16_t string literal ma typ " tablica N const char16_t", gdzie n jest wielkością łańcucha zdefiniowanego poniżej; ma statyczny czas przechowywania i jest inicjowany podanymi znakami. Pojedynczy znak c-char może wytworzyć więcej niż jeden znak char16_t w postaci par zastępczych.

10 literał Łańcuchowy zaczynający się od U, taki jak U"asdf", jest char32_t literałem łańcuchowym. Literał char32_t string ma typ " tablica N const char32_t", gdzie n jest wielkością łańcucha zdefiniowanego poniżej; ma statyczny czas przechowywania i jest inicjowany podanym postaci.

11 literał Łańcuchowy zaczynający się na L, taki jak L"asdf", jest szerokim literałem łańcuchowym. Szeroki łańcuch znaków ma typ " array of N const wchar_t", gdzie n jest wielkością łańcucha zdefiniowanego poniżej; ma statyczny czas przechowywania i jest inicjowany podanymi znakami.

 74
Author: Daniel Fischer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-09 21:08:18

Literały łańcuchowe są ważne dla całego programu (i nie są przydzielane nie do stosu), więc będą ważne.

Również literały ciągów są tylko do odczytu, więc (dla dobrego stylu) może powinieneś zmienić foo na const char *foo(int)

 14
Author: asaelr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-02 02:42:27

Dobre pytanie. Ogólnie rzecz biorąc, miałbyś rację, ale twój przykład jest wyjątkiem. Kompilator statycznie przydziela globalną pamięć dla literału łańcuchowego. W związku z tym adres zwracany przez twoją funkcję jest ważny.

To, że tak jest, jest raczej wygodną cechą C, prawda? Pozwala to funkcji na zwrócenie wiadomości bez zmuszania programisty do martwienia się o pamięć, w której wiadomość jest przechowywana.

Zobacz też poprawną obserwację @asaelr re const.

 6
Author: thb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-02 02:53:11

Tak, jest to poprawny kod, przypadek 1 poniżej. Można bezpiecznie zwracać ciągi C z funkcji przynajmniej w następujący sposób:

  • const char* do ciągów literalnych. Nie może być modyfikowany, nie może być uwolniony przez rozmówcę. Rzadko przydatne do zwracania wartości domyślnej, ze względu na problem zwalniania opisany poniżej. Może mieć sens, jeśli musisz przekazać gdzieś wskaźnik funkcji, więc potrzebujesz funkcji zwracającej ciąg znaków..

  • char* lub const char* do statycznego bufor char. Nie może być uwolniony przez rozmówcę. Może być modyfikowana (albo przez wywołujący, jeśli nie const, albo przez zwracającą go funkcję), ale funkcja zwracająca to nie może (łatwo) mieć wielu buforów, więc nie jest (łatwo) bezpieczna dla wątku i może być konieczne skopiowanie zwróconej wartości przed ponownym wywołaniem funkcji.

  • char* do bufora przydzielonego za pomocą malloc. Może być modyfikowany, ale zwykle musi być wyraźnie uwolniony przez wywołującego i ma narzut alokacji sterty. strdup jest z tego Typ.

  • const char* LUB char* do bufora, który został przekazany jako argument do funkcji (zwrócony wskaźnik nie musi wskazywać na pierwszy element bufora argumentu). Pozostawia odpowiedzialność za zarządzanie buforem/pamięcią dzwoniącemu. Wiele standardowych funkcji łańcuchowych jest tego typu.

Jeden problem polega na tym, że mieszanie ich w jedną funkcję może się skomplikować. Rozmówca musi wiedzieć, jak powinien obsługiwać zwracany wskaźnik, jak długo jest ważny i czy rozmówca powinien uwolnij go i nie ma (miłego) sposobu na określenie tego w czasie wykonywania. Tak więc nie można na przykład mieć funkcji, która czasami zwraca wskaźnik do przydzielonego sterty bufora, który musi być wywołujący free, a czasami wskaźnik do domyślnej wartości z literalnego łańcucha znaków, którego wywołujący nie musi free.

 5
Author: hyde,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-09 21:27:55

Zmienne lokalne są ważne tylko w zakresie, w jakim zostały zadeklarowane, jednak nie deklaruje się żadnych zmiennych lokalnych w tej funkcji.

Jest całkowicie poprawne zwracanie wskaźnika do literału łańcuchowego z funkcji, ponieważ literał Łańcuchowy istnieje przez całe wykonanie programu, podobnie jak static lub zmienna globalna.

Jeśli martwisz się, że to, co robisz, może być niepoprawne undefined, powinieneś podkręcić ostrzeżenia kompilatora, aby sprawdzić, czy w rzeczywistości istnieje wszystko, co robisz źle.

 3
Author: AusCBloke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-02 02:58:35

Str nigdy nie będzie zwisał wskaźnik. Because it points to static address gdzie znajdują się literały łańcuchowe . Będzie to głównie readonly i global do programu, gdy zostanie załadowany . Nawet jeśli spróbujesz uwolnić lub zmodyfikować, rzuci segmentation fault na platformach z ochroną pamięci .

 1
Author: qwr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-30 20:05:25

Zmienna lokalna jest przydzielana na stosie. Po zakończeniu funkcji zmienna wychodzi poza zakres i nie jest już dostępna w kodzie. Jeśli jednak masz globalny (lub po prostu - jeszcze nie poza zakresem) wskaźnik, który przypisałeś do tej zmiennej, wskaże on miejsce w stosie, w którym znajdowała się ta zmienna. Może to być wartość używana przez inną funkcję lub wartość bez znaczenia.

 0
Author: Imp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-02 02:44:43

W powyższym przykładzie pokazanym przez Ciebie, zwracasz przydzielone wskaźniki do dowolnej funkcji, która wywołuje powyższe. Więc nie stałby się lokalnym wskaźnikiem. A ponadto wskaźniki, które mają być zwrócone, pamięć jest alokowana w segmencie globalnym.

Thanking You,

Viharri P L V.

 0
Author: VIHARRI PLV,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-02 07:08:05