Допустимо ли передавать указатель на переменную стека в realloc()?
-
19-08-2019 - |
Вопрос
int main()
{
char myString = NULL;
realloc(&myString, 5);
strncpy((char *)&myString, "test", 5);
}
Кажется, работает нормально, но я все еще немного сбит с толку соотношением стека и кучи.Разрешено ли это?Если это разрешено, делает myString
нужно ли освобождать его вручную или он будет освобожден, когда выйдет за рамки?
Редактировать:Спасибо за ответы, так что я предполагаю, что это в равной степени незаконно
//I want the code to change myString to "tests"
char myString[5] = "test";
realloc(&myString, strlen(myString)+2);
myString[4] = 's';
myString[5] = '\0';
Решение
Нет, это совершенно неправильно. realloc следует использовать только для перераспределения памяти, выделенной malloc, то, что вы делаете, работает только случайно, и в конечном итоге будет ужасно аварийно завершаться
char *myString = malloc(x);
myString = realloc(myString,y);
free(myString)
Вам лучше использовать new и delete, а еще лучше использовать std :: string. Р>
Другие советы
Некоторые проблемы с опубликованным вами кодом:
- Да, вам нужно освободить все, что вы выделяете с помощью malloc и realloc, а также других связанных функций выделения памяти в стиле C.
- Я думаю, вы имели в виду char *myString , а не char.Передача адреса чего-либо в стеке (вашего символа) совершенно неверна.
- Вам нужно инициализировать указатель myString char равным NULL, прежде чем использовать его в realloc.
- Вы должны передавать 4 в strncpy, а не 5, если бы у вас была строка большего размера, вы бы перезаписывали память.
- Вы должны освободить буфер, который вы создали в своем примере
- Вы должны проверять возвращаемое значение вашего вызова realloc.перераспределить()
[Что касается возвращаемого значения realloc:] После успешного завершения с размером, не равным 0, realloc() возвращает указатель на (возможно, перемещенное) выделенное пространство.Если размер равен 0, либо нулевой указатель или уникальный указатель что может быть успешно пройден на бесплатно() возвращается.Если доступной памяти недостаточно , realloc() возвращает нулевой указатель и присваивает errno значение [ENOMEM].
- повторное выделение будет работать как malloc, когда вы передадите значение NULL:
Если ptr является нулевым указателем, realloc() ведет себя как malloc() для указанного размера.
Более C ++-способ сделать это:
Однако вы пометили это как C ++, и более типобезопасно использовать новый оператор C ++.Хотя новый оператор не допускает перераспределения, он будет работать для распределений и для повторного использования существующих буферов (размещение новых).
char *myString = new char[5];
strncpy(myString, "test", 4);
//...
delete[] myString;
или даже:
#include <string>
//...
std::string str = "test";
Это не должно работать. Вы перебираете что-то, что не было изначально неправильным. И нет, он не будет освобожден, когда выйдет из области видимости - когда вы используете malloc или realloc, все зависит от вас.
Обновление: ваша правка ничего не меняет - вы все еще пытаетесь перераспределить то, что изначально не было неправильно. Кроме того, вы не можете игнорировать возвращаемое значение из realloc - если realloc должен переместить память куда-то еще, вы найдете это в возвращении. Другими словами:
char* ptr = malloc(4);
ptr = realloc(ptr, 5);
После realloc, ptr может указывать на совершенно другое место в памяти, и дальнейшее использование исходного значения ptr может оставить вас с памятью, которая была освобождена и которая не так велика, как вы думаете. р>
ЭТО ОПАСНО! Это повредит ваш стек. Если бы вы перераспределили что-то в стеке функции, которая затем вернулась к main (), вы на самом деле закончили бы перезаписью фрейма стека и возвратились бы куда-нибудь, кроме main (). ЭТО ПОТЕНЦИАЛЬНОЕ ОТДЕЛЕНИЕ БЕЗОПАСНОСТИ.
Попробуйте выполнить следующее. Если он падает на Realloc, вам повезло Вы можете нанести серьезный ущерб с помощью чего-то вроде memcpy (& Amp; myString).
int dostuff();
int main()
{
dostuff();
return 0;
}
int dostuff()
{
char myString = NULL;
realloc(&myString, 5);
strncpy((char *)&myString, "test", 5);
return 0;
}
Это то, что вы никогда не должны делать. Попытка освободить () или realloc () переменную стека может привести к неопределенному поведению, включая (но не ограничиваясь этим) поврежденный стек (приводящий к непредсказуемому потоку управления), поврежденные структуры службы кучи, поврежденную пользовательскую память. Вам повезло, если программа просто зависает с AV. В некоторых случаях это может работать, но вы никогда не должны пытаться это делать.
Практическое правило: возвращайте память только тому менеджеру памяти, для которого она была выделена. В этом случае не пытайтесь вернуть переменную стека в кучу времени выполнения.
Ваша программа синтаксически допустима на C ++, но будет вызывать неопределенное поведение, поскольку вы передаете адрес объекта стека распределителю кучи. Обычно это означает, что ваша программа будет аварийно завершена при выполнении.
Стек и куча - это две разные области памяти, выделенные процессу, выполняющему вашу программу. Стек увеличивается по мере того, как вы вводите функцию для хранения ее аргументов и локальных переменных, и автоматически сжимается при возврате из функции. Куча, с другой стороны, является отдельной адресной областью, где память может быть получена по требованию и должна быть освобождена явно, когда она больше не нужна.
Если адрес локальной переменной передается в realloc (), он может попытаться освободить свою память и выделить ее в другом месте. Поскольку адрес не из кучи, а realloc () работает в куче, это не удастся. Скорее всего, realloc () обнаружит адрес не из кучи и прервет работу программы.
<Ч>Кроме того, пример программы содержит несколько логических ошибок.
char myString = NULL;
Вы объявляете переменную для хранения символа, а не строки. Строка в стиле C имеет тип char*
, то есть указатель на символ.
Кроме того, символ назначается NULL
, ноль адреса, который обычно присваивается недействительным указателям. Это компилируется, потому что препроцессор заменяет 0
литералом realloc()
. На самом деле, вы храните нулевой байт в символе, который также по соглашению является терминатором строки в стиле C.
realloc(&myString, 5);
Как упоминалось выше, это недопустимо, потому что вы передаете адрес объекта стека распределителю кучи. Эта проблема остается в вашем втором примере кода.
Кроме того, вы отбрасываете возвращаемое значение. <=> возвращает адрес, на котором была выделена новая память. Это не может быть тот же адрес, что и раньше. Это может быть даже NULL, что является <=> способом сказать вам, что оно вышло из памяти.
strncpy((char *)&myString, "test", 5);
Это правильно, но приведение является избыточным.
<Ч>Вот более правильная версия вашей программы:
#include <stdlib.h>
#include <string.h>
int main()
{
/* allocate space for, say, one character + terminator */
char* myString = (char*) malloc(2);
/* some code using myString omitted */
/* get more space */
myString = (char*) realloc(myString, 5);
/* write to the string */
strncpy(myString, "test", 5);
/* free the memory */
free(myString);
return 0;
}
В C ++ лучше полностью избегать realloc (). Например, вы можете использовать что-то вроде следующего:
#include <string>
int main()
{
std::string myString;
/* some code using myString */
myString = "test";
return 0;
}
Вам не нужно освобождать myString
, поскольку он находится в стеке (который получает " освобождается " при выходе из области видимости).
realloc
здесь недопустимо, адрес должен быть NULL
или адрес, возвращенный ранее при вызове malloc
, calloc
или x
.
Каждая объявленная вами переменная находится в стеке, даже указатель:
int * x;
Переменная pointer
находится в стеке! Он имеет тип <=> и содержит адрес.
x = (int *) malloc (sizeof (int));
назначает адрес, возвращаемый <=>, переменной x! Содержание <=> является адресом памяти!
Проблема с тем, что вы делаете, заключается в том, что вы копаетесь в чем-то, что не является переменной. Вы определили myString как символ и поэтому пытаетесь изменить его адрес. Это плохо.
Функция realloc () не должна изменять что-либо переданное ей. Он берет указатель на некоторую память в куче (или нулевой указатель, если ничего не выделено) и возвращает указатель на некоторую память в куче.
Поэтому вы предоставляете либо нулевой указатель, либо указатель на что-то, выделенное с помощью malloc () или realloc () или calloc (), и сохраняете возвращенный указатель.
Что-то вроде
char * myString = NULL;
myString = realloc(myString, 5);
будет работать, но вы захотите освободить () myString.
В C ++, однако, используйте std :: string.
В ответ на ваш второй пример кода:
Да, это также незаконно. myString не выделяется с помощью malloc (или calloc), поэтому его нельзя перераспределить с помощью realloc или освободить с помощью free.
Кроме того, realloc не принимает указатель на указатель в качестве первого аргумента. Он берет указатель на выделенную память и возвращает другой (возможно, другой) указатель. Напишите звонок вот так:
myString = realloc(myString, strlen(myString)+2);