Потому что вы не выделяете 2D массив. Вы выделяете набор одномерных массивов, и эти выделения не обязательно должны быть смежными (большинство реализаций malloc
резервируют несколько байтов для хранения размера выделенного блока).
Для динамического выделения "true" 2D-массив, в котором количество строк и столбцов не известно до времени выполнения, вы должны сделать что-то вроде этого:
stat (*arr)[c] = malloc( sizeof *arr * r );
, которое будет смежным, как любой "нормальный" 2D-массив.
Но ...
Строго говоря, это поведение не определено - поскольку arr
указывает на VLA, выражение sizeof *arr
должен оцениваться в время выполнения , а не во время компиляции, и arr
не является допустимым значением указателя в этой точке. Я никогда не видел такого сбоя ни в одной из реализаций, которые я использовал, но это не значит, что где-то не получится. Если бы вместо этого c
были постоянными, как
stat (*arr)[3] = malloc( sizeof *arr * r );
, то проблем не было бы, и это было бы предпочтительным способом динамического выделения массива Nx3.
Если вам нужно, чтобы все элементы массива были смежными (чтобы вы могли перемещаться по всему массиву с помощью указателя или чего-то в этом роде), тогда самый безопасный вариант - выделить вашу память как одномерный массив:
stat *arr = malloc( sizeof *arr * r * c );
и вычислите смещения вручную:
x = arr[ i * r + j ];
Если вы хотите удобство двумерной нотации, вы можете попробовать создать указатель и параметр, указывающий на начало массива, что-то вроде
stat (*ptr)[c] = (stat (*)[c]) arr;
но такой тип псевдонимов указателей также не определен, если типы указателей несовместимы, и у нас нет оснований ожидать, что указатель на T
совместим с указателем на массив T
.