"ż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ć?
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.
#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 .
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"
, jestchar16_t
literałem łańcuchowym. Achar16_t
string literal ma typ " tablica Nconst 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 znakchar16_t
w postaci par zastępczych.10 literał Łańcuchowy zaczynający się od U, taki jak
U"asdf"
, jestchar32_t
literałem łańcuchowym. Literałchar32_t
string ma typ " tablica Nconst 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 Nconst wchar_t
", gdzie n jest wielkością łańcucha zdefiniowanego poniżej; ma statyczny czas przechowywania i jest inicjowany podanymi znakami.
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)
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
.
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*
lubconst 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*
LUBchar*
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
.
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.
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 .
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.
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.
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