Вопрос выделения памяти в C ++ - PullRequest
0 голосов
/ 03 июля 2011
int main() {

  char** k;

  k = new char*;

  char* k1 = "abc";
  char* k2 = "def";

  *k = k1;
  *(k + 1) = k2;
  delete [] (k + 1);


}

Ошибка: ошибка сегментации

Может ли кто-нибудь объяснить, почему при освобождении возникает ошибка сегментации (k + 1)?Я смог освободить k без проблем.

ADD: В ответах было сказано, что я не могу delete [] (k + 1), так как я не использовал новое на нем;Но как объяснить тот факт, что cout<<*(k + 1)<<endl; напечатан правильно?

Ответы [ 4 ]

3 голосов
/ 03 июля 2011
k = new char*;

Это выделенное хранилище только для одного char*.

*(k + 1) = k2;

Это пытается сделать вид, что выделено два char*. Это может быть не сайт segfault, но это ошибка.

delete [] (k + 1);

Здесь вы пытаетесь delete[] что-то, что вы не сделали new[], еще одна ошибка.

РЕДАКТИРОВАТЬ: В глубине, память выделяется большими кусками, такими как страницы. Поэтому, когда вы выделяете небольшой объем памяти, очень вероятно, что память вокруг него также является действительной. Однако доступ к нему все еще очень недействителен.

Более того, когда вы говорите что-то вроде new char*, это превращается в вызов operator new(sizeof(char*)). Допустим, ОС выделяет новую страницу 4 КБ физической памяти для этого по адресу 0x12340000. Менеджеру памяти нужна небольшая структура, чтобы отслеживать блок, что-то вроде:

struct mem_block_info {
    void* next_block;
    size_t block_size;
};

Таким образом, эта структура помещается в 0x12340000. Сразу после этого он помещает запрошенное вами хранилище, поэтому (при условии, что это 32-разрядный компьютер) он возвращает указатель 0x12340008, начиная с sizeof(void*) == sizeof(size_t) == 4. Затем он должен поместить заголовок после вашего хранилища, чтобы отследить неиспользуемую часть этой страницы 4K, чтобы не тратить память, выделяя еще одну страницу 4K, когда вам нужно еще char*. Этот заголовок идет по адресу сразу после конца выделенного блока, 0x1234000C. Как только пыль осядет, new char* записал это в память:

Address    Data
0x12340000 0x00000000
0x12340004 0x00000001
0x12340008 uninitialized; could be anything
0x1234000C 0x00000000
0x12340010 0x00000FF4

Нулевые указатели указывают на конец выделенных и связанных списков свободных блоков.

Итак, когда вы делаете:

*(k + 1) = k2;

k + 1 == 0x1234000C - это указатель next_block для свободного блока, и вы просто перезаписали его недопустимым значением (скорее всего, адрес строки в постоянной памяти). Это не сразу вызывает ошибку сегментации, но когда диспетчер памяти пытается просмотреть список свободных блоков, он в конечном итоге смотрит на эту строку и неправильно интерпретирует ее как заголовок блока, а затем переходит к next_block, который является неверный адрес и бум, segfault.

2 голосов
/ 03 июля 2011

Можно только delete точно то, что возвращается от new, и то же самое относится к new[] и delete[].

Это Неопределенное поведение передать любой адрес для удаления, который не был возвращен новым.Вот цитата из Стандарта C ++.

§ 3.7.4.2-3

Если функция освобождения завершается с помощью исключения,поведение не определено.Значение первого аргумента, переданного функции освобождения, может быть значением нулевого указателя;если так, и если функция освобождения предоставлена ​​в стандартной библиотеке, вызов не имеет никакого эффекта.В противном случае значение, предоставляемое оператору delete(void*) в стандартной библиотеке, должно быть одним из значений, возвращаемых предыдущим вызовом оператора new(std::size_t) или оператора new(std::size_t, const std::nothrow_-t&) в стандартной библиотеке, и значением, предоставляемым оператору delete[](void*)в стандартной библиотеке должно быть одно из значений, возвращаемых предыдущим вызовом оператора new[](std::size_t) или оператора new[](std::size_t, const std::nothrow_t&) в стандартной библиотеке.

1 голос
/ 03 июля 2011

Вы можете delete только точные указатели, возвращаемые new. Все остальное незаконно.

1 голос
/ 03 июля 2011

k + 1 не создается с использованием new[].Как ты тогда можешь delete[] это?

Они всегда идут парами.

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