Какие гарантии дает malloc для выравнивания памяти?

Я наткнулся на следующий код:

int main()
{
    char *A=(char *)malloc(20);
    char *B=(char *)malloc(10);
    char *C=(char *)malloc(10);
    printf("\n%d",A);
    printf("\t%d",B);
    printf("\t%d\n",C);
    return 0;
}  
//output--   152928264     152928288    152928304

Я хочу знать, как выполняется распределение и заполнение malloc(), Глядя на вывод, я вижу, что начальный адрес кратен 8. Есть еще какие-нибудь правила?

3 ответа

Решение

Согласно этой странице документации,

адрес блока, возвращаемого функцией malloc или realloc в системе GNU, всегда кратен восьми (или шестнадцати в 64-битных системах).

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

Стандарт C говорит, что результат malloc()должен иметь возможность приведения к любому допустимому типу указателя. Так

... = (DataType *)malloc(...);

должно быть возможно, независимо от типа DataType является.

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

Исходная формулировка стандарта:

Указатель, возвращаемый в случае успешного выделения, выравнивается соответствующим образом, чтобы его можно было назначить указателю на любой тип объекта с фундаментальным требованием выравнивания, а затем использовать для доступа к такому объекту или массиву таких объектов в выделенном пространстве (до пространство явно освобождено).

Источник: ISO / IEC 9899: 201x (также известный как ISO C11)

Например, если система требует int быть выровненным по 4 байта и long быть выровненным по 8 байт, malloc() должен возвращать память, выровненную по 8 байт, потому что он не может знать, собираетесь ли вы преобразовать результат в int или чтобы long.

Теоретически, если вы запрашиваете меньше sizeof(long) байтов, приведение к long * недействителен как longдаже не влезет в эту память. Можно подумать, что в таком случаеmalloc()можно было выбрать меньшее выравнивание, но это не то, что сказано в стандарте. Требование выравнивания в стандарте не зависит от размера выделения!

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

Если вы зависите от определенного выравнивания, вы можете использовать aligned_alloc(), который определен в стандарте ISO C11 и, следовательно, переносится на все системы, для которых существует компилятор C11, или вы можете использоватьposix_memalign(), который определен в IEEE Std 1003.1-2001 (также известный как POSIX 2001) и доступен во всех системах, совместимых с POSIX, а также в системах, которые стараются быть максимально совместимыми с POSIX (например, Linux).

Интересный факт:
malloc()в macOS всегда возвращает память, выровненную по 16 байтам, несмотря на то, что ни один тип данных в macOS не требует выравнивания памяти, превышающего 8. Причина этого - SSE. Некоторые инструкции SSE требуют выравнивания по 16 байт и гарантируют, чтоmalloc() всегда возвращает память, выровненную по 16 байтам, Apple очень часто может использовать оптимизацию SSE в своей стандартной библиотеке.

Единственное стандартное правило заключается в том, что адрес, возвращаемый malloc будет соответствующим образом выровнен для хранения любых переменных. Что именно это означает, зависит от платформы (поскольку требования к выравниванию варьируются от платформы к платформе).

Для 32-битной системы Linux:
Когда malloc () выделяет память, она выделяет память кратно 8 (заполнение 8) и выделяет дополнительные 8 байт для учета.

Например:

malloc (10) и malloc (12) выделят 24 байт памяти (16 байт после заполнения + 8 байт для учета).

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

Другие вопросы по тегам