Ist die Rückgabe eines Zeigers auf eine statische lokale Variable sicher?

Lesezeit: 6 Minuten

Benutzeravatar von John Carter
John Carter

Ich arbeite mit Code, der häufig die Redewendung verwendet, einen Zeiger auf eine statische lokale Variable zurückzugeben. z.B:

char* const GetString()
{
  static char sTest[5];
  strcpy(sTest, "Test");
  return sTest;
}

Gehe ich recht in der Annahme, dass dies sicher ist?

PS, ich weiß, dass dies ein besserer Weg wäre, dasselbe zu tun:

char* const GetString()
{
  return "Test";
}

Bearbeiten:
Entschuldigung, die Funktionssignatur sollte natürlich lauten:

const char* GetString();

Benutzeravatar von Brian R. Bondy
Brian R. Bondy

Erstes Beispiel: Etwas sicher

char* const GetString()
{
  static char sTest[5];
  strcpy(sTest, "Test");
  return sTest;
}

Obwohl dies nicht empfohlen wird, ist dies sicher, der Gültigkeitsbereich einer statischen Variablen bleibt auch dann aktiv, wenn der Gültigkeitsbereich der Funktion endet. Diese Funktion ist überhaupt nicht sehr Thread-sicher. Eine bessere Funktion würde Sie dazu bringen, a zu bestehen char* buffer und ein maxsize für die GetString() Funktion zu füllen.

Insbesondere gilt diese Funktion nicht als a Reentrant-Funktion denn reentrante Funktionen dürfen unter anderem nicht gibt die Adresse an statische (globale) nicht konstante Daten zurück. Sehen reentrante Funktionen.

Zweites Beispiel: Völlig unsicher

char* const GetString()
{
  return "Test";
}

Dies wäre sicher, wenn Sie a const char *. Was du gegeben hast, ist nicht sicher. Der Grund dafür ist, dass Zeichenfolgenliterale in einem Nur-Lese-Speichersegment gespeichert werden können und ihre Änderung zu undefinierten Ergebnissen führen wird.

char* const (const pointer) bedeutet, dass Sie die Adresse, auf die der Zeiger zeigt, nicht ändern können. const char * (Zeiger auf Konstante) bedeutet, dass Sie die Elemente, auf die dieser Zeiger zeigt, nicht ändern können.

Fazit:

Sie sollten entweder Folgendes in Betracht ziehen:

1) Wenn Sie Zugriff auf den Code haben, ändern Sie den GetString einen Parameter von a nehmen char* buffer zu füllen und a maxsize benutzen.

2) Wenn Sie keinen Zugriff auf den Code haben, ihn aber aufrufen müssen, schließen Sie diese Methode in eine andere Funktion ein, die durch einen Mutex geschützt ist. Die neue Methode ist wie in 1 beschrieben.

Benutzeravatar von strager
Nachzügler

static Variablen (in einer Funktion) sind wie bereichsbezogene globale Variablen. Im Allgemeinen sollten sie vermieden werden (wie globale Variablen verursachen sie Wiedereintrittsprobleme), sind aber manchmal nützlich (einige Standardbibliotheksfunktionen verwenden sie). Sie können Zeiger auf globale Variablen zurückgeben, damit Sie Zeiger auf zurückgeben können static auch Variablen.

  • „Im Allgemeinen sollten sie vermieden werden“ mag zu stark sein, aber es ist sicher, dass Sie sich der Risiken und Einschränkungen bewusst sein sollten. +1 für Klarheit auf warum Es ist in Ordnung.

    – dmckee — Ex-Moderator-Kätzchen

    17. Januar 2009 um 18:15 Uhr

  • Ich stimme dmckee zu, die Wiedereintrittsprobleme sind auf das Design der Statik zurückzuführen, die über Funktionsaufrufe hinweg aktiv ist. es ist kein schlechtes Benehmen. aber Sie sollten sich des Risikos in der Tat bewusst sein.

    – Johannes Schaub – litb

    17. Januar 2009 um 18:18 Uhr

Kommt darauf an, was man unter sicher versteht. Es gibt ein paar Probleme, die ich sofort sehe:

  1. Sie haben a zurückgegeben char * const, wodurch Aufrufer die Zeichenfolge an dieser Stelle ändern können. Möglicher Pufferüberlauf. Oder meinst du a const char *?
  2. Möglicherweise liegt ein Problem mit Wiedereintritt oder Parallelität vor.

Um die zweite zu erklären, bedenken Sie Folgendes:

const char * const format_error_message(int err)
{
    static char error_message[MAXLEN_ERROR_MESSAGE];
    sprintf(error_message, "Error %#x occurred", err);
    return error_message;
}

Wenn du es so nennst:

int a = do_something();
int b = do_something_else();

if (a != 0 && b != 0)
{
    fprintf(stderr,
        "do_something failed (%s) AND do_something_else failed (%s)\n",
        format_error_message(a), format_error_message(b));
} 

…was wird gedruckt?

Dasselbe gilt für das Einfädeln.

Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Grundsätzlich ja, es ist sicher in dem Sinne, dass der Wert auf unbestimmte Zeit bestehen bleibt, da er statisch ist.

Es ist nicht sicher in dem Sinne, dass Sie einen konstanten Zeiger auf variable Daten zurückgegeben haben, anstatt einen Variablenzeiger auf konstante Daten. Besser ist es, wenn die aufrufenden Funktionen die Daten nicht verändern dürfen:

const char *GetString(void)
{
    static char sTest[5];
    strncpy(sTest, "Test", sizeof(sTest)-1);
    sTest[sizeof(sTest)-1] = '\0';
    return sTest;
}

In dem gezeigten einfachen Fall ist es kaum notwendig, sich um Pufferüberläufe zu kümmern, obwohl meine Version des Codes sich Sorgen macht und eine Nullterminierung sicherstellt. Eine Alternative wäre die Verwendung der Funktion TR24731 strcpy_s stattdessen:

const char *GetString(void)
{
    static char sTest[5];
    strcpy_s(sTest, sizeof(sTest), "Test");
    return sTest;
}

Noch wichtiger ist, dass beide Varianten einen (variablen) Zeiger auf konstante Daten zurückgeben, sodass der Benutzer den String nicht ändern und (wahrscheinlich) außerhalb des Bereichs des Arrays trampeln sollte. (Wie @trager in den Kommentaren betont, gibt die Rückgabe von a const char * ist keine Garantie dafür, dass der Benutzer nicht versucht, die zurückgegebenen Daten zu ändern. Sie müssen jedoch den zurückgegebenen Zeiger so umwandeln, dass er nicht konstant ist, und dann die Daten ändern. dies ruft undefiniertes Verhalten hervor und an diesem Punkt ist alles möglich.)

Ein Vorteil der wörtlichen Rückgabe besteht darin, dass die No-Write-Zusage normalerweise vom Compiler und Betriebssystem erzwungen werden kann. Die Zeichenfolge wird in das Textsegment (Codesegment) des Programms eingefügt, und das Betriebssystem generiert einen Fehler (Segmentierungsverletzung unter Unix), wenn der Benutzer versucht, die Daten zu ändern, auf die der Rückgabewert verweist.

[At least one of the other answers notes that the code is not re-entrant; that is correct. The version returning the literal is re-entrant. If re-entrancy is important, the interface needs to be fixed so that the caller provides the space where the data is stored.]

Johannes Schaub - Benutzerbild der litb
Johannes Schaub – litb

Ja, es ist absolut sicher. Die Lebensdauer der lokalen Statik ist die der gesamten Programmausführung in C. Sie können also einen Zeiger darauf zurückgeben, da das Array auch nach der Rückkehr der Funktion am Leben ist und der zurückgegebene Zeiger gültig dereferenziert werden kann.

Benutzeravatar von Nuclear
Nuklear

Dies ist sehr nützlich, da Sie die Funktion direkt als printf-Parameter verwenden können. Aber wie bereits erwähnt, verursachen mehrere Aufrufe der Funktion innerhalb eines einzigen Aufrufs ein Problem, da die Funktion denselben Speicher verwendet und ein zweimaliger Aufruf die zurückgegebene Zeichenfolge überschreibt. Aber ich habe dieses Stück Code getestet und es scheint zu funktionieren – Sie können eine Funktion sicher aufrufen, bei der Givemestring höchstens MAX_CALLS-mal verwendet wird und sich korrekt verhält.

#define MAX_CALLS 3
#define MAX_LEN 30

char *givemestring(int num)
{
        static char buf[MAX_CALLS][MAX_LEN];
        static int rotate=0;

        rotate++;
        rotate%=sizeof(buf)/sizeof(buf[0]);

        sprintf(buf[rotate],"%d",num);
        return buf[rotate];

}

Das einzige Problem ist die Thread-Sicherheit, aber dies kann mit lokalen Thread-Variablen (dem __thread-Schlüsselwort von gcc) gelöst werden.

Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Ja, dies wird häufig verwendet, um den Textteil einer Suche zurückzugeben, dh um eine Fehlernummer in eine benutzerfreundliche Zeichenfolge zu übersetzen.

Es ist ratsam, dies in Fällen zu tun, in denen Sie:

fprintf(stderr, "Error was %s\n", my_string_to_error(error_code));

Wenn my_string_to_error() einen zugewiesenen String zurückgibt, würde Ihr Programm angesichts der oben genannten (sehr) häufigen Verwendung einer solchen Funktion lecken.

char const *foo_error(...)
{
    return "Mary Poppins";
}

… ist auch in Ordnung, einige hirntote Compiler möchten jedoch möglicherweise, dass Sie es umwandeln.

Sehen Sie sich einfach Saiten auf diese Weise an, geben Sie kein Buch zurück 🙂

1411440cookie-checkIst die Rückgabe eines Zeigers auf eine statische lokale Variable sicher?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy