#c #malloc
Вопрос:
Когда мы используем malloc
, он возвращает указатель на начало адреса памяти фиксированного размера, который был передан в malloc. Например, malloc(40)
бросит мне какой-нибудь неинициализированный фрагмент памяти длиной 40 байт. Дело в том, что я видел примеры кода, в котором люди индексируют этот фрагмент памяти. Мой вопрос в том, как malloc
определить размер индекса?
Например, возьмем этот фрагмент кода,
#include <stdio.h>
#include <stdlib.h>
int main()
{
char **array;
array = malloc(3 * sizeof(char *));
for (int i=0; i < 3; i ) {
array[i] = malloc(10);
}
for (int i=0; i<10; i ) {
free(array[i]);
}
free(array);
return 0;
}
Сначала я хотел бы объяснить, что, по моему мнению, происходит, и надеюсь, что кто-нибудь сможет поправить меня в отношении любых неправильных идей, которые у меня есть.
char **array
создает переменную «массив», где она станет указателем на указатель на символ. Это означает, что если мы разыменуем это значение, оно даст нам адрес памяти, в котором char
оно хранится.
array = malloc(3 * sizeof(char *))
. Давайте предположим, что здесь sizeof(char *)
всегда будет возвращено 8. Продолжая, это создаст неинициализированный фрагмент памяти длиной 32 байта. Ключевым моментом здесь является то, что он имеет длину 32 байта, как он обрабатывает индексируемый размер?
array[i] = malloc(10)
это часть моего замешательства здесь. У нас есть неинициализированный фрагмент памяти длиной 32 байта, как мы его индексируем?
У меня есть идея, которую я хотел бы изложить, и надеюсь, что кто-нибудь сможет исправить любое мое недоразумение.
0x02 0x0A 0x12
[ 0x90 | 0x91 | 0x92 ]
<-- sizeof(char*) -> <-- sizeof(char*) -> <-- sizeof(char*) ->
^
|
|
0x01 (memory address of variable array) (array - points to 0x02)
-- Random memory locations
0x90 -- | Starting from the memory address location 0x90, the next sizeof(char) bytes will representing the value in this memory address location.
['c']
0x91
['a']
0x92
['t']
Насколько я понимаю, мэллок будет знать индексируемый размер по приведению, которое мы сделали для нашего начального указателя, т. Е. char*
внутри char** array
. Это означает, что наш указатель, возвращенный из malloc(40)
, будет указывать в данном примере на адресное пространство памяти, расположенное в 0x02
(начале массива).
Каждый раз, когда мы выполняем действие array[i]
, которое мы на самом деле выполняем 0x02 sizeof(char*) * i
, оно будет перемещать указатель в начало нового местоположения. Это означает, например, что, когда мы это делаем array[1]
, мы на самом деле делаем 0x02 sizeof(char*) * 1
то, что подтолкнуло бы нас к 0x02 8 (0x0A)
этому . Это означает, что из расположения адреса памяти 0x0A
следующие sizeof(char *)
байты будут считываться как индекс, хранящийся в этом месте в памяти. В этом примере это будет a char *
, в моем примере я написал 0x90
, что означает , что в другом месте в памяти 0x90
следующее sizeof(char)
, т. е. 1 байт будет иметь фактическое значение. Фактическое значение, представляющее » c » (например), но это может быть расположено где-то еще в памяти, не связанное с malloc.
Используя эту формулу, мы можем, например, получить массив целых чисел, возвращенный из malloc, имея int* ten_int_array = malloc(10 *sizeof(10))
. Теперь формула будет скорректирована ten_int_array sizeof(int) * i
. Что сделало бы malloc не индексируемым с фиксированным размером.
Спасибо за любые ответы, я пытаюсь проверить свои предположения здесь.
Комментарии:
1. Это не имеет к этому никакого отношения
malloc
. Это неmalloc
то, что делает индексацию. Это компилятор. Когда код выполняет индексацию, компилятор генерирует правильные смещения в зависимости от типа указателя. Всеmalloc
, что нужно, — это выделить тот объем памяти, который вы указываете (ну, возможно, немного больше для ведения учета и выравнивания). Так что не совсем понятно, в чем заключается ваш вопрос.2. когда у вас есть a
T *
и вы индексируете его, вы получаете доступ к ячейкам памяти, как если бы они содержали массив смежных элементовT
.3. Размер указателя не имеет значения. Указатель — это просто адрес памяти. Важно то, что делает компилятор, когда он вычисляет смещение в массиве. Это делается путем умножения индекса массива на размер элемента массива. Полученное число представляет собой смещение (в байтах) от адреса памяти, хранящегося в указателе на индексированный элемент в массиве.
4. Все, что делает malloc, — это выделяет блок памяти запрошенного вами размера, а затем возвращает адрес памяти в первый байт этого блока.
5. Читая эти комментарии, если бы я поместил это в формулу, это было бы что-то вроде,
T* example = malloc(10 * sizeof(T))
, тоT[i]
, следовательно, на самом деле было бы,example sizeof(T) * i
.
Ответ №1:
Вот что происходит. Предположительно, вы запускаете его в 64-разрядной системе, где для адреса памяти требуется 8 байт (64 бита).
char *str;
объявляет переменную указателя, обозначаемую, *
которая может указывать на место в памяти. По вышеупомянутому соглашению он должен быть размером 8 байт. Компилятор знает, что объект, на который он должен указывать, является a char
. Он абсолютно не знает, должны ли быть другие символы, которые следуют за этим местоположением или предшествуют ему, только программист знает.
Таким образом, str = malloc(10);
выделяется достаточно места в памяти для хранения 10 символов, включая окончание «0». Адрес памяти присваивается переменной указателя str
.
char **array;
объявляет указатель *
на указатель *
. Это еще одна 8-байтовая переменная, которая, как известно компилятору, указывает на другой указатель, который сам указывает на символ. Как и в предыдущем случае, он понятия не имеет, есть ли другие указатели рядом с тем, на который он должен указывать.
array = malloc(3 * sizeof(char*));
выделяет достаточно места в памяти, чтобы сохранить ровно 3 указателя на символ. Результат распределения будет присвоен array
.
В » c » оператор []
, применяемый к указателю, аналогичен оператору, применяемому к переменной массива. Таким array[1]
образом, возвращает указатель #2 из памяти, выделенной выше.
array[i] = malloc(10);
выделяет память для строки из 10 символов и присваивает результат указателю, на который указывает массив[i]. free(array[i])
освобождает это воспоминание.
В результате вы получаете двухуровневую динамическую структуру.
|-malloc(3 * sizeof (char*)) == 24 bytes
|
V
array --> [0] --> malloc(10) == 10 bytes
[1] --> malloc(10)
[2] --> malloc(10)
Итак, когда вы освобождаетесь, вам нужно free(array[i]) //0..2
сначала это free(array)
сделать, потому что после освобождения массива память, на которую он указывает, тоже становится недействительной, и вы не можете ее использовать.