Проблема распределения памяти

Этот вопрос был задан в письменном раунде собеседования:

#include #define MAXROW 3 #define MAXCOL 4 main() { int (*p)[MAXCOL]; p = (int (*)[MAXCOL]) malloc(MAXROW*(sizeof(*p))); } 

Сколько байтов выделено в этом процессе?

Честно говоря, я не ответил на этот вопрос. Я не понял задания на p .

Может ли кто-нибудь объяснить мне, что будет ответом и как его можно вывести?

Это зависит от платформы.

int (*p)[MAXCOL]; объявляет указатель на массив целых чисел MAXCOL элементов широкий (MAXCOL, конечно, 4 в этом случае). Поэтому одним из элементов этого указателя является 4*sizeof(int) на целевой платформе.

Оператор malloc выделяет буфер памяти MAXROW, умноженный на размер типа, содержащегося в P. Поэтому в общей сложности выделяются целые числа MAXROW * MAXCOL. Фактическое количество байтов будет зависеть от целевой платформы.

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

p – указатель на массив элементов MAXCOL типа int , поэтому sizeof *p (скобки были избыточными) – это размер такого массива, то есть MAXCOL*sizeof(int) .

Бросок на возвращаемое значение malloc является необходимым, уродливым и считается вредным . В этом случае он скрывает серьезную ошибку: из-за отсутствия прототипа malloc предполагается неявно возвращать int , что несовместимо с его правильным типом возврата ( void * ), что приводит к неопределенному поведению.

sizeof(*p) будет MAXCOL*sizeof(int) . Таким образом, полностью MAXROW*MAXCOL*sizeof(int) количество байтов alloctaed.

Возможно, вы захотите проверить cdecl на помощь в переводе объявлений C на английский язык. В этом случае int (*p)[4]; становится declare p as pointer to array 4 of int .

 #include #define MAXROW 3 #define MAXCOL 4 main() { int (*p)[MAXCOL]; p = (int (*)[MAXCOL]) malloc(MAXROW*(sizeof(*p)); } 

Сколько байтов выделено в этом процессе?

p – указатель, поэтому он будет занимать sizeof(int(*)[MAXCOL]) в стеке, что может показаться сложным, но оно почти всегда совпадает с sizeof(void*) , sizeof(int*) или любым другим указателем. Очевидно, размеры указателей – это то, что дает приложениям их classификация как 16-, 32-, 64-и т.д. бит, и этот указатель будет соответствующим размером.

Затем p указывает на некоторую память, полученную из malloc

 malloc( MAXROW * sizeof(*p) ) 

sizeof(*p) – размер массива int который указывает p , а именно sizeof(int) * MAXCOL , поэтому мы получаем

 malloc( MAXROW * (sizeof(int) * MAXCOL) ) 

запрошенный из кучи. Для иллюстративных целей, если принять общий 32-битный размер int , мы рассмотрим 48 байтов. Фактическое использование может быть округлено до того, что чувствует себя подсистемами кучи (для подпрограмм кучи часто используются «ведра фиксированного размера» для ускорения их операций).

Чтобы подтвердить это ожидание, просто замените функцию каротажа для malloc() :

 #include  #define MAXROW 3 #define MAXCOL 4 void* our_malloc(size_t n) { printf("malloc(%ld)\n", n); return 0; } int main() { int (*p)[MAXCOL]; p = (int (*)[MAXCOL]) our_malloc(MAXROW*(sizeof(*p))); } 

Вывод на моем ящике Linux:

 malloc(48) 

Тот факт, что возвращаемый указатель malloc применяется к типу p, не влияет на объем выделенного выделения памяти.

Как замечает R, отсутствие прототипа malloc заставит компилятор ожидать, что malloc вернет int а не фактически возвращенную void* . На практике вероятно, что наименьшие sizeof (int) байты от указателя будут пережить преобразование, и если sizeof (void *) оказался равным sizeof (int), или – еще более слабый, – произойдет куча памяти на адрес, представляемый в int несмотря на то, что размер указателей больше (т.е. все усеченные биты равны 0s), тогда последующее разыменование указателя может работать. Дешевый подключаемый модуль: C ++ не будет компилироваться, если не будет показан прототип.

Тем не менее, возможно, ваш alloc.h содержит прототип malloc … У меня нет alloc.h, поэтому я предполагаю, что он нестандартный.

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

int (*p)[MAXCOL] == int (*p)[4] == “указатель на массив 4 из int” (см. примечание ниже)

sizeof(*p) будет тогда соответствовать точке p, т. е. 4 * sizeof(int) . Умножьте это на MAXROW и ваш окончательный ответ:

 12 * sizeof(int) 

Примечание: Это контрастирует с:

int *p[MAXCOL] == int *p[4] == “массив 4 указателя на int”
Скобки имеют очень большую разницу!

Это должно быть MAXROW * MAXCOL * sizeof (int) количество байтов

Мне очень не нравятся такие вопросы, потому что я считаю, что лучше работать инженером, чтобы экспериментировать, а не предполагать, что вы знаете, что вы делаете, особенно если есть подозрение, например, что программа работает не так, как ожидалось, или кто-то делает трюк вопросы.

 #include  #include  #define MAXROW 3 #define MAXCOL 4 main() { int (*p)[MAXCOL]; int bytes = MAXROW * (sizeof(*p)); p = (int (*)[MAXCOL]) malloc(bytes); printf("malloc called for %d bytes\n", bytes); } 

В 32-битной Linux-системе:

gcc test.c
./a.out
malloc вызвал 48 байт

(отредактировано для удаления приклеивания аварии умножения на maxrow дважды, что дает ошибочный размер 144 байта)

Выполнение следующего в codepad.org:

 //#include #define MAXROW 3 #define MAXCOL 4 int main() { int (*p)[MAXCOL]; p = (int (*)[MAXCOL]) malloc(MAXROW*(sizeof(*p))); int x = MAXROW*(sizeof(*p)); printf("%d", x); return 0; } 

распечатывается 48.

Зачем? Поскольку MAXROW равно 3, а sizeof(*p) равно 16, мы получаем 3 * 16.

Почему sizeof(*p) 16? Поскольку MAXCOL равен 4, то p является указателем на массив из 4 целых чисел. Каждый int 32 бит = 4 байта. 4 ints в массиве * 4 байта = 16.

Почему sizeof(*p) не 4? Потому что это размер, на который указывает p, а не размер p. Чтобы быть размером p, он должен быть sizeof(p) , который будет равен 4, поскольку p является указателем.

Педантично вы можете добавить:

  1. Если машина 64 бит (скажем), ответ будет равен 96.
  2. Поскольку в вопросе говорится: «Сколько байтов выделено в процессе?», Вам нужно добавить 4 байта для указателя p.
  3. malloc может выделять больше, чем вы просите (но не менее), поэтому на вопрос не может быть дан ответ.
  4. В том же духе, что и в 2, вы можете утверждать, что по мере того, как процесс также загружает системные DLL, такие как DLL C runtime, для запуска, он также выделяет пространство для них. Затем вы можете утверждать, что пространство, выделенное dll, которое впрыскивается в процесс другими (не системными) процессами, такими как те, которые были введены Actual Window Manager и его ilk. Но как педантично мы хотим получить?

Но я думаю, что вопрос действительно просят 48, с возможным дополнительным кредитом для объяснения 96.

Как насчет нулевых байтов, потому что он даже не будет компилироваться без нестандартного alloc.h . Если вы не можете скомпилировать его, вы не сможете его запустить, и если вы не можете его запустить, он не сможет выделить какую-либо память.