C: указатель на местоположение кучи malloc'а плюс 4 - PullRequest
0 голосов
/ 11 марта 2019

Я пытаюсь реализовать новый malloc, который хранит размер в начале области malloc, а затем возвращает указатель на увеличенное местоположение (что следует после сохраненного целого без знака).

void* malloc_new(unsigned size) {
    void* result = malloc(size + sizeof(unsigned));
    ((unsigned*)result)[0] = size;
    result += sizeof(unsigned);
    return result;
}

У меня есть сомнения относительно правильности строки

result += sizeof(unsigned);

(делает то, что я хочу).Скажем, исходный адрес в куче для malloc - X, а размер без знака - 4, я хочу, чтобы указатель «result» указывал на X + 4, верно?Это означает, что область памяти в стеке, в которой хранится указатель «результата», должна содержать (исходное расположение кучи + 4).

Ответы [ 4 ]

2 голосов
/ 11 марта 2019

В дополнение к проблемам, отмеченным в других ответах при выполнении арифметики с указателями на void * указателях, вы также, вероятно, нарушаете одно из ограничений, которые стандартные места C в памяти возвращают из функций, таких как malloc().

7.22.3 Функции управления памятью , параграф 1 стандарта C гласит:

Порядок и непрерывность хранилища, выделенного последовательными вызовами функций aligned_alloc, calloc, malloc и realloc, не определены. Указатель, возвращаемый в случае успешного выделения, выравнивается соответствующим образом, чтобы его можно было присвоить указателю на любой тип объекта с фундаментальным требованием выравнивания, а затем использовать для доступа к такому объекту или массиву таких объектов в выделенном пространстве (пока пространство не будет явно освобождено). Время жизни выделенного объекта простирается от выделения до освобождения. Каждое такое распределение должно давать указатель на объект, не пересекающийся с любым другим объектом. Возвращенный указатель указывает на начало (младший байтовый адрес) выделенного пространства. Если пространство не может быть выделено, возвращается нулевой указатель. Если размер запрошенного пространства равен нулю, поведение определяется реализацией: либо возвращается нулевой указатель, либо поведение такое, как если бы размер был некоторым ненулевым значением, за исключением того, что возвращенный указатель не должен использоваться для доступа к объекту ,

Обратите внимание на жирную часть.

Если ваша система не имеет фундаментального выравнивания, которое составляет всего четыре байта (8 или 16 гораздо более типично), вы нарушаете это ограничение и будете вызывать неопределенное поведение для 6.3.2.3 Указатели , пункт 7 для любого типа объекта с основным требованием выравнивания больше четырех байтов:

... Если результирующий указатель неправильно выровнен для ссылочного типа, поведение не определено. ...

2 голосов
/ 11 марта 2019

result += sizeof(unsigned); должен дать вам хотя бы предупреждение (арифметика указателя на void * ведет к неопределенному поведению).

unsigned *result = malloc(size + sizeof size);
result[0] = size;
return result + 1;

должен быть более простым способом.

Обратите внимание, чтовозвращенная память не очень хорошо выровнена для всех возможных типов данных.Вы столкнетесь с проблемами, если будете использовать эту память для double или других 64-битных типов данных.Вам следует использовать 8-байтовый тип данных uint64_t для хранения размера, после чего блок памяти впоследствии будет выровнен.

0 голосов
/ 11 марта 2019

Вы можете сделать void * арифметику , если вы, например, сначала приведете тип к char *.А затем бросить обратно в пустоту *.Чтобы получить лучшее выравнивание, используйте 64-битный тип для размера, например uint64_t.

#define W_REF_VOID_PTR(ptr,offset) \
    ((void*)((char*) (ptr) + (offset)))
0 голосов
/ 11 марта 2019

Как происходит арифметика пустого указателя в GCC

C не допускает арифметику указателей с типом указателя void *.

GNU C позволяет это, учитывая, что размер void равен 1.

result - это void*, поэтому result += sizeof(unsigned); просто работает на совместимых компиляторах.

Вы можете изменить свою функцию на:

void *malloc_new(unsigned size) {
    void* result = malloc(size + sizeof(unsigned));
    ((unsigned*)result)[0] = size;
    result = (char*)result + sizeof(unsigned);
    return result;
}

Примечание: до того, как на языке C существовали тип void и универсальный указатель void*, программисты использовали char* для представления универсального указателя.

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