Какие гарантии дает 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 байтов используется, когда мы вызываем свободную функцию. Байты бухгалтерии хранят длину выделенной памяти.