Я столкнулся с партией функций, возвратив символьные указатели в одном унаследованном приложении. Некоторые из них возвращающий указатели на локальные символьные массивы. Это, кажется, вызывает катастрофические отказы после нескольких вызовов (не сразу!) посмотрите использование ниже
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);
Нет, это неопределенное поведение. Это просто работает в вашем случае, но может перестать работать в любой момент.
Безопасно ли следующее использование?
Нет.
Если ваша функция возвращает указатель на что-либо, убедитесь, что она выделяет область памяти и возвращает указатель на нее:
char *safe_f1(void) {
char *ptr;
ptr = (char *) malloc(20 * sizeof(char));
...
return ptr;
}
Я бы предложил два возможных решения:
Используйте статический char buff [20]
в f1
, если функция не вызывается из нескольких потоков или внешний мир не сохраняет указатель за пределами strcpy.
Используйте return strdup (ptr);
и free
указатель вне f1
. Это проще в использовании, чем malloc
(хотя технически то же самое). Он медленнее, чем 1., но потокобезопасен.
Нет, это небезопасно. Только вызов strcpy может изменить стек достаточно, чтобы впоследствии вызвать проблемы, поскольку возвращаемый адрес и параметры могут перезаписать массив.
-121--4548566-Функция f1 возвращает временный (buff), который освобождается при возврате функции. Внутри функции необходимо использовать malloc ().
-121--4548569-Нет.. все еще небезопасно.
В момент выполнения strcpy (arr, f1 ());
указатель, используемый в качестве второго аргумента, уже указывает на несуществующий массив.
Нет, это небезопасно. Просто вызов strcpy может изменить стек настолько, что впоследствии возникнут проблемы, поскольку адрес возврата и параметры могут перезаписать массив.
решения malloc интересны, за исключением того, что память должна быть освобождена после использования. (вне функции). Иначе произойдет утечка памяти.
Функция f1 возвращает временную память (buff), которая освобождается при возврате функции. Необходимо использовать malloc() внутри функции.
Никогда не возвращать указатель на локальную переменную. Это может сработать в некоторых ситуациях, но в целом у вас будет много проблем с этим. Вместо этого:
Нет, видите ли, buff[20]
доступен только внутри функции f1
. Точнее говоря, память выделяется на стеке f1
.
Вам нужно создать новый buff[20]
на куче с помощью malloc
и вернуть указатель на эту память внутри f1
. Другой способ - создать buff[20]
вне f1 (из функции, которая вызывает f1
) и отправить его в качестве аргумента в f1
. f1
может затем изменить содержимое буфера и даже не обязательно возвращать его.
На самом деле, лучше всего было бы изменить f1 () для использования malloc (). Ваше решение не соответствует определенному поведению.
Это небезопасно. Причина проста:
Любая переменная в функции будет выделена на стеке, память которого освобождается после возвращения функции. Тот факт, что память освобождается, не означает, что ее содержимое изменяется.
Это означает, что содержимое памяти, которое вы поместили в переменную char buff[20]
, по-прежнему находится в позиции памяти buff
или ptr
(поскольку ptr=buff
). При вызове другой функции (или выполнении другого блока) переменные функции/блока также попадают в стек, что создает возможность изменения содержимого памяти, на которую указывает позиция ptr
.
В примере strcpy
, который вы написали, вам повезло, что переменные функции strcpy не попали в стек в позиции, которая находилась внутри старого массива buff
. Именно по этой причине у вас сложилось впечатление, что это было безопасно.
Вывод заключается в том, что вы никак не можете реально гарантировать, что содержимое освобожденной памяти в стеке не изменится между двумя вызовами функций.
Решением является использование malloc
, потому что malloc
выделяет память не на стеке, а на куче. Память кучи не деаллоцируется, пока вы не решите это сделать (вызовом free).
Такой подход гарантирует, что память, на которую указывает 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);
}
Я бы посоветовал изменить эти функции, чтобы они принимали указатель, который они используют.
void f1(char *)
Таким образом, каждый фрагмент кода, вызывающий функцию, должен принимать решение о том, куда записывается память. и удалить любую выделенную память.