Допустимо ли передавать указатель на переменную стека в realloc()?

StackOverflow https://stackoverflow.com/questions/650708

Вопрос

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

Источник топ-2 цитат

Это не должно работать. Вы перебираете что-то, что не было изначально неправильным. И нет, он не будет освобожден, когда выйдет из области видимости - когда вы используете 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);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top