функции, возвращающие указатель на символ - PullRequest
5 голосов
/ 26 февраля 2010

Я натолкнулся на множество функций, возвращающих указатели на символы в одном устаревшем приложении. Некоторые из них возвращают указатели на локальные массивы символов. Кажется, это вызывает сбои после нескольких вызовов (не сразу!), См. Использование ниже

char *f1(){
  char buff[20];
  char *ptr;

  ----
  ----
  ptr=buff;
 return ptr;
}

---
---

f2(f1());

f1 () возвращает локальную переменную-указатель и затем передает ее другой функции. Я получил сбой непосредственно, когда он скомпилирован с использованием режима _DEBUG в MS DEV. Но в режиме выпуска это не вызывает немедленного сбоя, но это может произойти после выполнения множества таких вызовов.

Когда я изменил использование, как показано ниже, оно работает без проблем. Является ли следующее использование безопасно?

strcpy(arr,f1()); /* arr is fixed char array*/
f2(arr);

Ответы [ 12 ]

12 голосов
/ 26 февраля 2010

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

3 голосов
/ 26 февраля 2010

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

3 голосов
/ 26 февраля 2010

Нет, это не безопасно. Просто вызов strcpy может изменить стек достаточно, чтобы впоследствии вызвать проблемы, поскольку адрес возврата и параметры могут перезаписать массив.

2 голосов
/ 26 февраля 2010

Это не безопасно. Причина проста:

Любая переменная в функции будет размещена в стеке, чья память освобождается после возврата из функции. Тот факт, что память освобождена, не означает, что ее содержимое изменилось.

Это означает, что содержимое памяти, которое вы поместили в переменную char buff[20], все еще находится в позиции памяти buff или ptr (начиная с ptr=buff). Всякий раз, когда вы вызываете другую функцию (или выполняете другой блок), ее переменные функции / блока также попадают в стек, создавая возможность изменения содержимого памяти, для которого указана позиция ptr.

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

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

Решение состоит в том, чтобы использовать malloc, поскольку malloc выделяет память не в стеке, а в куче. Память кучи не освобождается, если вы не решили сделать это (по бесплатному вызову).

Этот подход гарантирует, что память, указанная ptr, безопасна для использования любой другой функцией.

Недостаток этого решения является внутренним: как только память не освобождается, если вы не делаете это программно, если вы забудете освободить эту память и потерять содержимое ptr, эта память будет выделена для вашей программы, но никогда не достижимо, потеряно до тех пор, пока ваша программа работает. Эта память станет утечкой памяти: -)

Это одна из причин, почему на некоторых языках есть сборщики мусора ... но это другая история: -)

PS .: Я думаю, что это безопасно (если ваша программа однопоточная), хотя я не рекомендую делать что-то подобное:

{
  char safe_buffer[20];
  char *unsafe_ptr;
  int i;
  unsafe_ptr = f1();
  /*Copy the buffer without calling any function
    not to change the stack content
  */
  for(i=0;i<20 && *(unsafe_ptr + i) != 0;i++)
  {
    *(safe_buffer + i) = *(unsafe_ptr + i);
  }
  *(safe_buffer + i) = 0;
  f2(safe_buffer);
}
2 голосов
/ 26 февраля 2010

Безопасно ли следующее использование?

номер

Если ваша функция возвращает указатель на что-либо, убедитесь, что она выделяет область памяти и возвращает указатель на нее:

char *safe_f1(void) {
    char *ptr;
    ptr = (char *) malloc(20 * sizeof(char));
    ...
    return ptr;
}
2 голосов
/ 26 февраля 2010

Никогда не возвращайте указатель на локальную переменную. Это может работать в некоторых ситуациях, но в целом у вас будет много проблем с этим. Вместо этого:

  • документирует вашу функцию, что она возвращает указатель на выделенную память и что возвращаемый буфер должен быть освобожден вызывающим
  • добавить буфер и аргумент размера и заполнить буфер (это обычно делается в Win32 API)
  • если используется C ++, используйте std :: string
2 голосов
/ 26 февраля 2010

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

1 голос
/ 26 февраля 2010

Нет, видите, buff[20] доступно только внутри функции f1. Если быть точным, память распределяется по стеку f1 s.

Вам нужно создать новый buff[20] в куче, используя malloc и вернуть указатель на эту память изнутри f1. Другой способ - создать buff[20] вне f1 (из функции, которая вызывает f1) и отправить ее в качестве аргумента f1. f1 может затем изменить содержимое буфера и даже не должен его возвращать.

1 голос
/ 26 февраля 2010

Нет ... все еще не безопасно.

Во время выполнения strcpy(arr,f1()); указатель, используемый в качестве второго аргумента, уже указывает на несуществующий массив.

0 голосов
/ 26 февраля 2010

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

void f1(char *)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...