стек и вопрос перераспределения C ++ - PullRequest
3 голосов
/ 16 марта 2009
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';

Ответы [ 9 ]

18 голосов
/ 16 марта 2009

Нет, это совершенно неправильно. realloc следует использовать только для перераспределения памяти, выделенной malloc, то, что вы делаете, работает только случайно, и в конечном итоге будет ужасно падать

char *myString = malloc(x);
myString = realloc(myString,y);
free(myString)

Однако лучше использовать new и delete, а еще лучше использовать std :: string.

8 голосов
/ 16 марта 2009

Некоторые проблемы с кодом, который вы разместили:

  • Да, вам нужно освободить все, что вы выделяете, с помощью malloc, realloc и других связанных функций выделения памяти в стиле C.
  • Я думаю, вы имели в виду char * myString, а не char. Передача адреса чего-либо в стеке (вашего символа) совершенно неверна.
  • Вам необходимо инициализировать указатель на символ myString в NULL, прежде чем использовать его в realloc.
  • Вы должны передавать 4 в strncpy, а не 5, если бы у вас была строка большего размера, вы бы перезаписывали память.
  • Вы должны освободить буфер, который вы создали в своем примере
  • Вы должны проверять возвращаемое значение вашего вызова realloc. realloc ()

[Относительно возвращаемого значения realloc:] После успешного завершения с размером не равно 0, realloc () возвращает указатель на (возможно, перемещен) выделенное пространство. Если размер равен 0, либо нулевой указатель или уникальный указатель которые могут быть успешно переданы free () возвращается. Если нет достаточно доступной памяти, realloc () возвращает нулевой указатель и устанавливает errno [ENOMEM].

  • re-alloc будет работать как 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 цитат

3 голосов
/ 16 марта 2009

Это не должно работать. Вы перебираете что-то, что не было изначально неправильным. И нет, он не будет освобожден, когда выйдет из области видимости - когда вы используете malloc или realloc, все зависит от вас.

Обновление: ваша правка ничего не меняет - вы все еще пытаетесь перераспределить что-то, что не было изначально неправильно. Кроме того, вы не можете игнорировать возвращаемое значение из realloc - если realloc должен переместить память куда-то еще, вы найдете это в возвращении. Другими словами:

char* ptr = malloc(4);
ptr = realloc(ptr, 5);

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

2 голосов
/ 16 марта 2009

Это то, что вы никогда не должны делать. Попытка освободить () или realloc () переменную стека может привести к неопределенному поведению, включая (но не ограничиваясь) поврежденный стек (приводящий к непредсказуемому потоку управления), поврежденные структуры службы кучи, поврежденную пользовательскую память. Вам повезло, если программа просто зависает с AV. В некоторых случаях это может сработать, но вы никогда не должны пытаться это делать.

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

2 голосов
/ 16 марта 2009

ЭТО ОПАСНО! Это повредит ваш стек. Если бы вы перераспределили что-то в стеке функции, которая затем вернулась к main (), вы бы в действительности перезаписали фрейм стека и возвратились куда-то, кроме main (). ЭТО ПОТЕНЦИАЛЬНОЕ ОТЛОЖЕНИЕ БЕЗОПАСНОСТИ.

Попробуйте выполнить следующее. Если он падает на Realloc, вам повезло. Вы можете нанести серьезный урон с помощью чего-нибудь вроде memcpy (& myString).

int dostuff();

int main()
{
        dostuff();
        return 0;
}

int dostuff()
{
        char myString = NULL;
        realloc(&myString, 5);
        strncpy((char *)&myString, "test", 5);
        return 0;
}
1 голос
/ 26 марта 2009

Ваша программа синтаксически допустима на C ++, но будет вызывать неопределенное поведение, потому что вы передаете адрес объекта стека распределителю кучи. Как правило, это означает, что ваша программа будет аварийно завершена при выполнении.

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

Если адрес локальной переменной передается в realloc (), он может попытаться освободить свою память и выделить ее в другом месте. Поскольку адрес не из кучи, а realloc () работает в куче, это не удастся. Скорее всего, realloc () обнаружит адрес не из кучи и прервет работу программы.


Помимо этого, пример программы содержит несколько логических ошибок.


char myString = NULL;

Вы объявляете переменную для хранения символа, а не строки. Строка в стиле C имеет тип char*, то есть указатель на символ.

Кроме того, символу присваивается NULL, нулевой адрес, который обычно присваивается недействительным указателям. Это компилируется, потому что препроцессор заменяет NULL на литерал 0. На самом деле, вы храните нулевой байт в символе, который также, по соглашению, является терминатором строки в стиле C.


realloc(&myString, 5);

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

Кроме того, вы отбрасываете возвращаемое значение. realloc() возвращает адрес, на котором была выделена новая память. Это не может быть тот же адрес, что и раньше. Это может быть даже NULL, что является realloc() способом сказать вам, что оно вышло из памяти.


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;
}
0 голосов
/ 16 марта 2009

В ответ на ваш второй пример кода:

Да, это также незаконно. myString не выделяется с помощью malloc (или calloc), поэтому его нельзя перераспределить с помощью realloc или освободить с помощью free.

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

myString = realloc(myString, strlen(myString)+2);
0 голосов
/ 16 марта 2009

Проблема с тем, что вы делаете, заключается в том, что вы копаетесь в чем-то, что не является переменной. Вы определили myString как символ и поэтому пытаетесь изменить его адрес. Это плохо.

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

Следовательно, вы предоставляете либо нулевой указатель, либо указатель на что-то, выделенное malloc () или realloc () или calloc (), и сохраняете возвращенный указатель.

Что-то вроде

char * myString = NULL;
myString = realloc(myString, 5);

будет работать, но вы захотите освободить () myString.

Однако в C ++ используйте std :: string.

0 голосов
/ 16 марта 2009

Вам не нужно освобождать myString, поскольку он находится в стеке (который «освобождается» при выходе из области действия).

realloc здесь недопустимо, адрес должен быть NULL или адрес, возвращенный ранее при вызове realloc, malloc или calloc.

Каждая объявленная вами переменная находится в стеке, даже указатель:

int * x;

Переменная x находится в стеке! Он имеет тип pointer и содержит адрес.

x = (int *) malloc (sizeof (int));

назначает адрес, возвращаемый malloc, переменной x! Содержимое x является адресом памяти!

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