Подтвердить что ты не робот

Возвращает указатель на статическую локальную переменную?

Я работаю с некоторым кодом, который широко использует идиому возврата указателя на статическую локальную переменную. например:

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

Я правильно понимаю, что это безопасно?

PS, я знаю, что это будет лучший способ сделать то же самое:

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

Edit: Извинения, подпись функции должна, конечно, быть:

const char* GetString();
4b9b3361

Ответ 1

Первый пример: несколько безопасный

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

Хотя это и не рекомендуется, это безопасно, область действия статической переменной остается в живых, даже когда объем функции заканчивается. Эта функция не очень безопасна для потоков. Лучшая функция позволит вам передать char* buffer и a maxsize для функции GetString() для заполнения.

В частности, эта функция не считается реентерабельной функцией, поскольку реентерабельные функции не должны, среди прочего, возвращать адрес статическим (глобальным) непостоянным данным, См. реентеративные функции.

Второй пример: полностью небезопасный

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

Это было бы безопасно, если вы сделали const char *. То, что вы дали, небезопасно. Причина в том, что строковые литералы могут быть сохранены в сегменте памяти только для чтения и позволяют им изменять, что приведет к результатам undefined.

char* const (указатель const) означает, что вы не можете изменить адрес, на который указывает указатель. const char * (указатель на const) означает, что вы не можете изменить элементы, на которые указывает этот указатель.

Вывод:

Вы должны рассмотреть либо:

1) Если у вас есть доступ к коду, измените GetString, чтобы принять параметр char* buffer для заполнения и maxsize для использования.

2) Если у вас нет доступа к коду, но вы должны его вызвать, оберните этот метод в другую функцию, которая защищена мьютексом. Новый метод описан в 1.

Ответ 2

В принципе, да, это безопасно в том смысле, что значение будет длиться бесконечно, потому что оно статично.

Это небезопасно в том смысле, что вы вернули постоянный указатель на переменные данные, а не указатель переменной на постоянные данные. Лучше, если вызывающим функциям не разрешено изменять данные:

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

В показанном простом случае вряд ли стоит беспокоиться о переполнении буфера, хотя моя версия кода вызывает беспокойство и обеспечивает нулевое завершение. Альтернативой было бы использовать TR24731 функцию strcpy_s вместо:

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

Что еще более важно, оба варианта возвращают указатель (переменный) к постоянным данным, поэтому пользователь не должен изменять модификацию строки и (возможно) выталкивать вне диапазона массива. (Как отмечает @strager в комментариях, возврат const char * не является гарантией того, что пользователь не будет пытаться модифицировать возвращаемые данные. Однако они должны отбросить возвращаемый указатель, чтобы он был неконстантным, а затем изменить данные, это вызывает поведение undefined, и в этот момент все возможно.)

Одно из преимуществ буквального возврата заключается в том, что обещание без записи обычно может выполняться компилятором и операционной системой. Строка будет помещена в сегмент текста (кода) программы, и операционная система будет генерировать ошибку (нарушение сегментации в Unix), если пользователь попытается изменить данные, на которые указывает возвращаемое значение.

[По крайней мере, один из других ответов отмечает, что код не является повторным; это верно. Версия, возвращающая литерал, является повторной попыткой. Если важно повторное подключение, интерфейс должен быть исправлен, чтобы вызывающий абонент предоставил пространство, в котором хранятся данные.]

Ответ 3

Это зависит от того, что вы подразумеваете под безопасностью. Есть несколько проблем, которые я вижу сразу:

  • Вы вернули char * const, который позволит вызывающим абонентам изменять строку в этом месте. Потенциальный переполнение буфера. Или вы имели в виду const char *?
  • У вас может возникнуть проблема с повторной установкой или с concurrency.

Чтобы объяснить второе, рассмотрим следующее:

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;
}

Если вы вызываете это так:

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));
} 

... что будет напечатано?

То же самое для потоковой передачи.

Ответ 4

static переменные (в функции) подобны глобальным переменным. В общем, их следует избегать (например, глобальных переменных, они вызывают проблемы с повторным подключением), но иногда полезны (некоторые стандартные функции библиотеки используют их). Вы можете возвращать указатели на глобальные переменные, поэтому вы можете также возвращать указатели на переменные static.

Ответ 5

Да, это совершенно безопасно. Время жизни локальной статики - это все исполнение программы в C. Поэтому вы можете вернуть указатель на нее, так как массив будет доступен даже после возвращения функции, а возвращаемый указатель может быть действительно отменен.

Ответ 6

Это очень полезно, так как вы можете использовать функцию непосредственно как параметр printf. Но, как уже упоминалось, многократные вызовы функции внутри одного вызова вызовут проблему, потому что функция использует одно и то же хранилище, а вызов дважды переписывает возвращаемую строку. Но я тестировал этот кусок кода, и он, похоже, работает - вы можете безопасно вызвать функцию, где givemestring используется не чаще MAX_CALLS и будет вести себя корректно.

#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];

}

Единственная проблема - безопасность потоков, но это можно решить с помощью локальных переменных потока (gcc __thread keyword)

Ответ 7

Да, это часто используется для возврата части текста некоторого поиска, т.е. для перевода некоторого номера ошибки в дружественную человеку строку.

Его разумно сделать это в тех случаях, когда вы:

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

Если my_string_to_error() возвращает выделенную строку, ваша программа будет течь, учитывая указанное (очень) общее использование такой функции.

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

... тоже хорошо, некоторые компиляторы с мертвым мозгом могут захотеть, чтобы вы его набросили.

Просто смотрите строки таким образом, не возвращайте книгу:)

Ответ 8

Возвращающий указатель на локальную переменную, будь то статический или нет, потенциально небезопасен, поскольку срок службы статической переменной ограничен функцией, это будет обвисший указатель.