Wäre der von der folgenden Funktion zurückgegebene Zeiger nicht unzugänglich?
char *foo(int rc)
{
switch (rc)
{
case 1:
return("one");
case 2:
return("two");
default:
return("whatever");
}
}
Die Lebensdauer einer lokalen Variablen in C / C ++ liegt also praktisch nur innerhalb der Funktion, oder? Was bedeutet, dass char* foo(int)
der zurückgegebene Zeiger nach dem Beenden nichts mehr bedeutet, oder?
Ich bin etwas verwirrt über die Lebensdauer einer lokalen Variablen. Was ist eine gute Klarstellung?
c
function
local-variables
string-literals
lifetime
user113454
quelle
quelle
int rc
. Seine Lebensdauer endet bei jedem derreturn
-s. Die Zeiger, die Sie zurückgeben, beziehen sich auf Zeichenfolgenliterale. String-Literale haben eine statische Speicherdauer: Ihre Lebensdauer ist mindestens so lang wie die des Programms.strerror
Funktion offensichtlich noch nie gesehen .Antworten:
Ja, die Lebensdauer einer lokalen Variablen liegt innerhalb des Bereichs (
{
,}
), in dem sie erstellt wird.Lokale Variablen werden automatisch oder lokal gespeichert. Automatisch, weil sie automatisch zerstört werden, sobald der Bereich, in dem sie erstellt wurden, endet.
Was Sie hier haben, ist jedoch ein Zeichenfolgenliteral, das in einem implementierungsdefinierten Nur-Lese-Speicher zugewiesen wird. String-Literale unterscheiden sich von lokalen Variablen und bleiben während der gesamten Programmlebensdauer erhalten. Sie haben eine statische Lebensdauer [Ref 1] .
Ein Wort der Warnung!
Beachten Sie jedoch, dass jeder Versuch, den Inhalt eines Zeichenfolgenliteral zu ändern, ein undefiniertes Verhalten (UB) ist. Benutzerprogramme dürfen den Inhalt eines String-Literal nicht ändern.
Daher wird immer empfohlen, eine
const
Weile zu verwenden, um ein Zeichenfolgenliteral zu deklarieren.const char*p = "string";
anstatt,
char*p = "string";
Tatsächlich ist es in C ++ veraltet, ein Zeichenfolgenliteral ohne das zu deklarieren,
const
jedoch nicht in C. Wenn Sie jedoch ein Zeichenfolgenliteral mit a deklarieren, habenconst
Sie den Vorteil, dass Compiler normalerweise eine Warnung ausgeben, wenn Sie versuchen, das Zeichenfolgenliteral in zu ändern zweiter Fall.Beispielprogramm :
#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; }
Ausgabe:
Beachten Sie, dass der Compiler für den zweiten Fall warnt, nicht jedoch für den ersten.
Um die Frage zu beantworten, die einige Benutzer hier gestellt haben:
Was ist mit integralen Literalen los?
Mit anderen Worten, ist der folgende Code gültig?
int *foo() { return &(2); }
Die Antwort lautet: Nein, dieser Code ist ungültig. Es ist schlecht geformt und gibt einen Compilerfehler aus.
Etwas wie:
prog.c:3: error: lvalue required as unary ‘&’ operand
String-Literale sind l-Werte, dh: Sie können die Adresse eines String-Literals übernehmen, dessen Inhalt jedoch nicht ändern.
Jedoch auch jede andere Literale (
int
,float
,char
, etc.) sind R-Werte (C - Standard verwendet den Begriff der Wert eines Ausdrucks für diese) und deren Adresse gar nicht zu entnehmen.[Ref 1] C99 Standard 6.4.5 / 5 "String Literals - Semantics":
quelle
char (*)[4]
. Dies liegt daran, dass der Typ "abc" istchar[4]
und der Zeiger auf ein Array mit 4 Zeichen als "deklariert " ist.char (*)[4]
Wenn Sie also die Adresse übernehmen müssen, müssen Sie dies alschar (*a)[4] = &"abc";
und "Ja" tun , es ist gültig.char[4]
. (Wegen der'\0'
)char const s[] = "text";
nicht nicht machts
einen Zeichenliteral, und dahers
wird am Ende des Bereichs zerstört werden, so dass alle überlebenden Zeiger auf sie baumeln lassen werden.Es ist gültig. String-Literale haben eine statische Speicherdauer, sodass der Zeiger nicht baumelt.
Für C ist dies in Abschnitt 6.4.5, Absatz 6 vorgeschrieben:
Und für C ++ in Abschnitt 2.14.5, Absätze 8-11:
quelle
String-Literale sind für das gesamte Programm gültig (und werden nicht dem Stack zugewiesen), sodass sie gültig sind.
Auch Stringliterale sind schreibgeschützt, so (für guten Stil) sollten Sie vielleicht ändern
foo
zuconst char *foo(int)
quelle
&"abc"
ist nichtchar*
. Es ist eine Adresse des Arrays und sein Typ istchar(*)[4]
. Jedoch entwederreturn &"abc";
undchar *a="abc";return a;
sind gültig.const
, und es wird völlig legal sein, aber es ist immer noch ein schlechter Stil.Ja, es ist ein gültiger Code, siehe Fall 1 unten. Sie können C-Strings von einer Funktion auf mindestens folgende Weise sicher zurückgeben:
const char*
zu einem String-Literal. Es kann nicht geändert werden und darf nicht vom Anrufer freigegeben werden. Aufgrund des unten beschriebenen Freigabeproblems ist es für die Rückgabe eines Standardwerts selten nützlich. Es kann sinnvoll sein, wenn Sie tatsächlich irgendwo einen Funktionszeiger übergeben müssen, sodass Sie eine Funktion benötigen, die einen String zurückgibt.char*
oderconst char*
zu einem statischen Zeichenpuffer. Es darf vom Anrufer nicht freigegeben werden. Es kann geändert werden (entweder vom Aufrufer, wenn nicht const, oder von der Funktion, die es zurückgibt), aber eine Funktion, die dies zurückgibt, kann nicht (leicht) mehrere Puffer haben, so dass es nicht (leicht) threadsicher ist und der Aufrufer möglicherweise benötigt um den zurückgegebenen Wert zu kopieren, bevor die Funktion erneut aufgerufen wird.char*
zu einem Puffer zugeordnet mitmalloc
. Es kann geändert werden, muss jedoch normalerweise vom Aufrufer explizit freigegeben werden und hat den Heap-Zuordnungsaufwand.strdup
ist von diesem Typ.const char*
oderchar*
an einen Puffer, der als Argument an die Funktion übergeben wurde (der zurückgegebene Zeiger muss nicht auf das erste Element des Argumentpuffers zeigen). Die Verantwortung für die Puffer- / Speicherverwaltung liegt beim Anrufer. Viele Standardzeichenfolgenfunktionen sind von diesem Typ.Ein Problem ist, dass das Mischen dieser in einer Funktion kompliziert werden kann. Der Aufrufer muss wissen, wie er mit dem zurückgegebenen Zeiger umgehen soll, wie lange er gültig ist und ob der Anrufer ihn freigeben soll, und es gibt keine (nette) Möglichkeit, dies zur Laufzeit zu bestimmen. So können Sie beispielsweise keine Funktion haben, die manchmal einen Zeiger auf einen vom Heap zugewiesenen Puffer zurückgibt, den der Aufrufer benötigt
free
, und manchmal einen Zeiger auf einen Standardwert aus dem Zeichenfolgenliteral, den der Aufrufer nicht mussfree
.quelle
Gute Frage. Im Allgemeinen hätten Sie Recht, aber Ihr Beispiel ist die Ausnahme. Der Compiler reserviert statisch globalen Speicher für ein Zeichenfolgenliteral. Daher ist die von Ihrer Funktion zurückgegebene Adresse gültig.
Dass dies so ist, ist ein ziemlich praktisches Merkmal von C, nicht wahr? Es ermöglicht einer Funktion, eine vorkomponierte Nachricht zurückzugeben, ohne den Programmierer zu zwingen, sich Gedanken über den Speicher zu machen, in dem die Nachricht gespeichert ist.
Siehe auch @ asaelrs korrekte Beobachtung bezüglich
const
.quelle
const char *a = "abc";
und das weglassen&
. Der Grund dafür ist, dass eine Zeichenfolge in doppelten Anführungszeichen in die Adresse ihres ursprünglichen Zeichens aufgelöst wird.Lokale Variablen sind nur in dem Bereich gültig, in dem sie deklariert sind. Sie deklarieren jedoch keine lokalen Variablen in dieser Funktion.
Es ist absolut gültig, einen Zeiger von einer Funktion auf ein Zeichenfolgenliteral zurückzugeben, da während der gesamten Ausführung des Programms ein Zeichenfolgenliteral vorhanden ist, genau wie eine
static
oder eine globale Variable.Wenn Sie sich Sorgen darüber machen, was Sie tun, könnte dies undefiniert ungültig sein, sollten Sie Ihre Compiler-Warnungen aufdrehen, um festzustellen, ob tatsächlich etwas falsch ist.
quelle
&"abc"
ist nicht vom Typchar*
, jedoch beides"abc"
und&"abc"
gilt während der gesamten Programmausführung.str
wird niemals ein baumelnder Zeiger sein, da er auf eine statische Adresse verweist, an der sich Zeichenfolgenliterale befinden.Es ist meistens schreibgeschützt und global für das Programm, wenn es geladen wird.
Selbst wenn Sie versuchen, freizugeben oder zu ändern, wird auf Plattformen mit Speicherschutz ein Segmentierungsfehler ausgelöst .
quelle
Auf dem Stapel wird eine lokale Variable zugewiesen. Nach Beendigung der Funktion verlässt die Variable den Gültigkeitsbereich und ist im Code nicht mehr verfügbar. Wenn Sie jedoch einen globalen (oder einfach - noch nicht außerhalb des Gültigkeitsbereichs) Zeiger haben, den Sie zugewiesen haben, um auf diese Variable zu zeigen, zeigt er auf die Stelle im Stapel, an der sich diese Variable befand. Dies kann ein Wert sein, der von einer anderen Funktion verwendet wird, oder ein bedeutungsloser Wert.
quelle
In dem von Ihnen gezeigten obigen Beispiel geben Sie die zugewiesenen Zeiger tatsächlich an die Funktion zurück, die die oben genannten Funktionen aufruft. Es würde also kein lokaler Zeiger werden. Darüber hinaus wird für die Zeiger, die zurückgegeben werden müssen, Speicher im globalen Segment zugewiesen.
quelle