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 funkcji, prawda? Co oznacza, że po char* foo(int)
zakończeniu zwracany wskaźnik już nic nie znaczy, prawda?
Jestem trochę zdezorientowany co do czasu życia zmiennej lokalnej. Co to jest dobre wyjaśnienie?
c
function
local-variables
string-literals
lifetime
user113454
źródło
źródło
int rc
. Jego żywotność kończy się na każdym zreturn
-s. Wskaźniki, które zwracasz, dotyczą literałów łańcuchowych. Literały łańcuchowe mają statyczny czas trwania: ich żywotność jest co najmniej tak długa, jak czas trwania programu.strerror
Najwyraźniej nigdy nie widziałeś tej funkcji.Odpowiedzi:
Tak, czas życia zmiennej lokalnej mieści się w zakresie (
{
,}
), w którym została utworzona.Zmienne lokalne mają pamięć automatyczną lub lokalną. Automatyczne, ponieważ są automatycznie niszczone po zakończeniu zakresu, w którym zostały utworzone.
Jednak mamy tutaj literał łańcuchowy, który jest przydzielony w implementacji pamięci tylko do odczytu. Literały łańcuchowe różnią się od zmiennych lokalnych i pozostają żywe przez cały okres istnienia programu. Mają statyczny czas trwania [Ref 1] żywotność.
Słowo ostrzeżenia!
Należy jednak pamiętać, że każda próba zmodyfikowania zawartości literału ciągu jest niezdefiniowanym zachowaniem (UB). Programy użytkownika nie mogą modyfikować zawartości literału ciągu.
W związku z tym zawsze zaleca się użycie
const
while deklarując literał ciągu.const char*p = "string";
zamiast,
char*p = "string";
W rzeczywistości w C ++ deklarowanie literału tekstowego bez
const
chociaż nie w C.Jednakże zadeklarowanie literału ciągu znaków za pomocą aconst
daje ci tę zaletę, że kompilatory zwykle dają ostrzeżenie w przypadku próby zmodyfikowania literału ciągu w drugi przypadek.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; }
Wynik:
Zwróć uwagę, że kompilator ostrzega przed drugim przypadkiem, ale nie w pierwszym.
Aby odpowiedzieć na pytanie zadane przez kilku użytkowników:
O co chodzi z literałami całkowitymi?
Innymi słowy, czy poniższy kod jest prawidłowy?
int *foo() { return &(2); }
Odpowiedź brzmi: nie, ten kod jest nieprawidłowy. Jest źle sformułowany i spowoduje błąd kompilatora.
Coś jak:
prog.c:3: error: lvalue required as unary ‘&’ operand
Literały łańcuchowe są l-wartościami, tj .: Możesz wziąć adres literału ciągu, ale nie możesz zmienić jego zawartości.
Jednak inne literałami (
int
,float
,char
, etc.) są wartości R (średnia C używa określenia wartości wyrażenia dla nich) i ich adres nie może być wykorzystany w ogóle.[Ref 1] C99 standard 6.4.5 / 5 "Literały łańcuchowe - semantyka":
źródło
char (*)[4]
. Dzieje się tak, ponieważ typ "abc" tochar[4]
i wskaźnik do tablicy składającej się z 4 znaków jest zadeklarowany jakochar (*)[4]
, więc jeśli potrzebujesz wziąć jego adres, musisz to zrobić tak, jakchar (*a)[4] = &"abc";
i Tak, jest poprawny.char[4]
. (Z powodu'\0'
)char const s[] = "text";
nie nie uczynićs
znak literalny, a zatems
będą niszczone na końcu zakresu, więc wszelkie wskazówki, które przeżyły do niego będzie zwisać.To jest ważne. Literały ciągów mają statyczny czas trwania, więc wskaźnik nie zwisa.
Dla C, co jest wymagane w sekcji 6.4.5, paragraf 6:
A dla C ++ w sekcji 2.14.5, akapity 8-11:
źródło
Literały łańcuchowe obowiązują dla całego programu (i nie są przydzielane poza stosem), więc będą prawidłowe.
Ponadto, literały łańcuchowe są tylko do odczytu, więc (dla dobrego stylu) może powinieneś zmienić
foo
naconst char *foo(int)
źródło
&"abc"
nie jestchar*
. jest to adres tablicy, a jej typ tochar(*)[4]
. Jednak obareturn &"abc";
ichar *a="abc";return a;
są ważne.const
i będzie to całkowicie legalne, ale nadal w złym stylu.Tak, to ważny kod, patrz przypadek 1 poniżej. Możesz bezpiecznie zwrócić ciągi C z funkcji przynajmniej w ten sposób:
const char*
do literału ciągu. Nie może być modyfikowany i nie może być zwolniony przez dzwoniącego. Rzadko jest użyteczne w celu zwrócenia wartości domyślnej z powodu problemu zwalniania opisanego poniżej. Może to mieć sens, jeśli faktycznie potrzebujesz gdzieś przekazać wskaźnik funkcji, więc potrzebujesz funkcji zwracającej ciąg znaków.char*
lubconst char*
do statycznego bufora znaków. Dzwoniący nie może go uwolnić. Może być modyfikowana (albo przez wywołującego, jeśli nie jest stałą, albo przez funkcję, która ją zwraca), ale funkcja zwracająca to nie może (łatwo) mieć wielu buforów, więc nie jest (łatwo) bezpieczna wątkowo, a wywołujący może potrzebować aby skopiować zwróconą wartość przed ponownym wywołaniem funkcji.char*
do bufora przydzielonego za pomocąmalloc
. Można go zmodyfikować, ale zwykle musi być jawnie zwolniony przez obiekt wywołujący i ma narzut alokacji sterty.strdup
jest tego typu.const char*
lubchar*
do bufora, który został przekazany jako argument do funkcji (zwrócony wskaźnik nie musi wskazywać na pierwszy element bufora argumentów). Pozostawia odpowiedzialność za zarządzanie buforem / pamięcią dzwoniącemu. Wiele standardowych funkcji łańcuchowych jest tego typu.Jednym z problemów jest to, że mieszanie ich w jednej funkcji może się skomplikować. Wzywający musi wiedzieć, jak powinien obsłużyć zwrócony wskaźnik, jak długo jest ważny i czy dzwoniący powinien go zwolnić, i nie ma (przyjemnego) sposobu na określenie tego w czasie wykonywania. Nie możesz więc na przykład mieć funkcji, która czasami zwraca wskaźnik do bufora przydzielonego na stertę, którego potrzebuje wywołanie
free
, a czasami wskaźnik do domyślnej wartości z literału ciągu, którego wywołanie nie możefree
.źródło
Dobre pytanie. Ogólnie rzecz biorąc, miałbyś rację, ale twój przykład jest wyjątkiem. Kompilator statycznie przydziela pamięć globalną dla literału ciągu. Dlatego adres zwrócony przez twoją funkcję jest prawidłowy.
To, że tak jest, jest raczej wygodną cechą C, prawda? Umożliwia funkcji zwrócenie wstępnie utworzonej wiadomości bez zmuszania programisty do martwienia się o pamięć, w której wiadomość jest przechowywana.
Zobacz także poprawną obserwację @ asaelr dotyczącą
const
.źródło
const char *a = "abc";
, pomijając&
. Powodem jest to, że ciąg w podwójnych cudzysłowach jest tłumaczony na adres swojego początkowego znaku.Zmienne lokalne są ważne tylko w zakresie, w jakim zostały zadeklarowane, jednak nie deklarujesz żadnych zmiennych lokalnych w tej funkcji.
Zwrócenie wskaźnika do literału ciągu znaków z funkcji jest całkowicie poprawne, ponieważ literał łańcuchowy istnieje przez całe wykonanie programu, tak samo jak
static
zmienna globalna lub zmienna globalna.Jeśli martwisz się, że to, co robisz, może być nieprawidłowe i nieokreślone, powinieneś włączyć ostrzeżenia kompilatora, aby sprawdzić, czy faktycznie jest coś, co robisz źle.
źródło
&"abc"
nie jest typuchar*
, jednak oba"abc"
i&"abc"
są ważne przez cały czas wykonywania programu.str
nigdy nie będzie wiszącym wskaźnikiem, ponieważ wskazuje na adres statyczny którym znajdują się literały łańcuchowe.Po załadowaniu będzie on głównie tylko do odczytu i będzie globalny dla programu.
Nawet jeśli spróbujesz zwolnić lub zmodyfikować, spowoduje to błąd segmentacji na platformach z ochroną pamięci .
źródło
Na stosie alokowana jest zmienna lokalna. 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 wskazywania tej zmiennej, wskaże on miejsce na stosie, w którym ta zmienna się znajdowała. Może to być wartość używana przez inną funkcję lub wartość bez znaczenia.
źródło
W powyższym przykładzie pokazanym przez ciebie, w rzeczywistości zwracasz przydzielone wskaźniki do dowolnej funkcji, która wywołuje powyższe. Więc nie stałby się lokalnym wskaźnikiem. Co więcej, dla wskaźników, które mają zostać zwrócone, przydzielana jest pamięć w segmencie globalnym.
źródło