Как функция free () знает, сколько байтов освободить и как получить доступ к этой информации в нашей программе? [Дубликат]

Это одно из самых запутанных сообщений об ошибках, которые каждый программист VC ++ видел снова и снова. Давайте сначала сделаем чёткость.

A. Что такое символ? Короче говоря, символ - это имя. Это может быть имя переменной, имя функции, имя класса, имя typedef или что-либо кроме тех имен и знаков, которые принадлежат языку C ++. Он определяется пользователем или вводится библиотекой зависимостей (другой пользовательский).

B. Что является внешним? В VC ++ каждый исходный файл (.cpp, .c и т. Д.) Рассматривается как единица перевода, компилятор компилирует по одному модулю за раз и генерирует один объектный файл (.obj) для текущей единицы перевода. (Обратите внимание, что каждый заголовочный файл, включенный в этот исходный файл, будет предварительно обработан и будет рассматриваться как часть этой единицы перевода). Все внутри единицы перевода считается внутренним, все остальное считается внешним. В C ++ вы можете ссылаться на внешний символ, используя ключевые слова, такие как extern, __declspec (dllimport) и т. Д.

C. Что такое «решимость»? Resolve - термин времени связывания. Во время компоновки линкер пытается найти внешнее определение для каждого символа в объектных файлах, которые не могут найти свое определение внутри. Объем этого процесса поиска, включая:

  • Все объектные файлы, сгенерированные во время компиляции
  • Все библиотеки (.lib), которые явно или неявно указаны как дополнительные зависимости это строительное приложение.

Этот процесс поиска называется разрешением.

D. Наконец, почему Unresolved External Symbol? Если компоновщик не может найти внешнее определение для символа, который не имеет определения внутри, он сообщает об ошибке неразрешенного внешнего символа.

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

  1. Определение существует

Например, если у нас есть функция foo, определенная в a.cpp:

int foo()
{
    return 0;
}

В b.cpp мы хотим вызвать функцию foo, поэтому добавим

void foo();

, чтобы объявить функцию foo () и вызвать ее в другом теле функции, скажем bar():

void bar()
{
    foo();
}

Теперь, когда вы создаете этот код, вы получите ошибку LNK2019, жалуясь, что foo является неразрешенным символом , В этом случае мы знаем, что foo () имеет свое определение в a.cpp, но отличается от того, которое мы вызываем (другое возвращаемое значение). Это означает, что определение существует.

  1. Определение не существует

Если мы хотим вызвать некоторые функции в библиотеке, но библиотека импорта не добавлен в дополнительный список зависимостей (установленный из: Project | Properties | Configuration Properties | Linker | Input | Additional Dependency) вашего проекта. Теперь компоновщик сообщит LNK2019, поскольку определение не существует в текущей области поиска.

296
задан Prashant Kumar 28 November 2013 в 00:25
поделиться

10 ответов

278
ответ дан inspector-g 26 August 2018 в 19:00
поделиться

malloc() и free() зависят от системы / компилятора, поэтому трудно дать конкретный ответ.

Дополнительная информация по этому другому вопросу .

6
ответ дан Community 26 August 2018 в 19:00
поделиться
1
ответ дан DigitalRoss 26 August 2018 в 19:00
поделиться

В соответствующей записке библиотека GLib имеет функции распределения памяти, которые не сохраняют неявный размер, - и затем вы просто передаете параметр размера бесплатно. Это может исключить часть служебных данных.

4
ответ дан EFraim 26 August 2018 в 19:00
поделиться

память, выделенная с помощью malloc () или calloc () или realloc ().

void free (void * ptr);

свободная функция не принимает размер в качестве параметра , Как функция free () знает, сколько памяти освобождается от заданного только указателя?

Ниже приведен наиболее распространенный способ хранения размера памяти, чтобы free () знал, что размер памяти будет освобожден.

Когда выделение памяти выполняется, выделенное пространство кучи на одно слово больше, чем запрошенная память. Дополнительное слово используется для хранения размера выделения и позже используется функцией free ()

1
ответ дан geetha 26 August 2018 в 19:00
поделиться

Из списка comp.lang.c часто задаваемых вопросов: Как бесплатно узнать, сколько байтов освободится?

Реализация malloc / free запоминает размер каждого блока по мере его выделения , поэтому нет необходимости напоминать о размере при освобождении. (Как правило, размер хранится рядом с выделенным блоком, поэтому обычно обычно плохо ломаются, если границы выделенного блока даже немного перешагнуты)

43
ответ дан jdehaan 26 August 2018 в 19:00
поделиться

Чтобы ответить на вторую половину вопроса: да, вы можете, и довольно общий шаблон в C следующий:

typedef struct {
    size_t numElements
    int elements[1]; /* but enough space malloced for numElements at runtime */
} IntArray_t;

#define SIZE 10
IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
myArray->numElements = SIZE;
1
ответ дан MSalters 26 August 2018 в 19:00
поделиться

Большинство реализаций функций распределения памяти C будут хранить учетную информацию для каждого блока, как в строке, так и отдельно.

Один типичный способ (в строке) состоит в том, чтобы фактически распределять как заголовок, так и память вы просили, добрались до минимального размера. Например, если вы запросили 20 байтов, система может выделить 48-байтовый блок:

  • 16-байтовый заголовок, содержащий размер, специальный маркер, контрольную сумму, указатели на следующий / предыдущий блок и и т. д.
  • 32 байта области данных (ваши 20 байт заполнены до кратного 16).

Адрес, указанный вами, является адресом данных площадь. Затем, когда вы освободите блок, free просто возьмет адрес, который вы ему даете, и, предположив, что вы не наполнили этот адрес или память вокруг него, проверьте учетную информацию непосредственно перед этим. Графически это будет выглядеть так:

 ____ The allocated block ____
/                             \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
          ^
          |
          +-- The address you are given

Имейте в виду, что размер заголовка и отступов полностью определяется реализацией (на самом деле, все это определяется реализацией (a), но встроенный учетный параметр является общим).

Контрольные суммы и специальные маркеры, которые существуют в учетной информации, часто являются причиной ошибок, таких как «Memory arena corrupted» или «Double free», если вы перезаписываете их или освободить их дважды.

Отступы (чтобы сделать выделение более эффективным) - это то, почему вы иногда можете писать немного за пределы запрашиваемого пространства, не вызывая проблем (все же, не делайте этого, это undefined behavior и, как раз потому, что он работает иногда, не означает, что это нормально).


(a) Я написал реализации malloc во встроенных системах, где вы получили 128 байты независимо от того, что вы просили (это был размер самой большой структуры в системе), если вы попросили 128 байт или меньше (запросы на большее должно быть выполнено с возвратным значением NULL). Очень простая битовая маска (т. Е. Не в строке) использовалась для определения того, был ли выделен 128-байтовый кусок.

Другие, которые я разработал, имели разные пулы для 16-байтовых фрагментов, 64-байтовые фрагменты, 256-байтовые фрагменты и 1K-фрагменты, снова используя битовую маску для определения того, какие блоки были использованы или доступны.

Обеим этим опциям удалось уменьшить накладные расходы бухгалтерской информации и увеличить скорость malloc и free (нет необходимости объединять соседние блоки при освобождении), что особенно важно в среде, в которой мы работали.

112
ответ дан paxdiablo 26 August 2018 в 19:00
поделиться

Менеджер кучи сохранил объем памяти, принадлежащий выделенному блоку, где-то, когда вы вызвали malloc.

Я никогда не реализовал его сам, но я думаю, что память прямо перед выделенным блоком может содержат метаинформацию.

2
ответ дан Timbo 26 August 2018 в 19:00
поделиться

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

0
ответ дан Varun Chhangani 26 August 2018 в 19:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: