То, что вы говорите в своем посте, абсолютно правильно. Я бы сказал, что каждый разработчик C приходит к одному и тому же открытию и к одному и тому же выводу, когда (если) они достигают определенного уровня владения языком C.
Когда специфика области вашего приложения вызывает массив определенного фиксированного размера (размер массива является константой времени компиляции), единственный правильный способ передать такой массив в функцию - использовать параметр указателя на массив
void foo(char (*p)[10]);
(на языке C ++ это тоже делается со ссылками
void foo(char (&p)[10]);
).
Это включит проверку типов на уровне языка, что обеспечит предоставление массива точно правильного размера в качестве аргумента. Фактически, во многих случаях люди используют эту технику неявно, даже не осознавая этого, скрывая тип массива за именем typedef.
typedef int Vector3d[3];
void transform(Vector3d *vector);
/* equivalent to `void transform(int (*vector)[3])` */
...
Vector3d vec;
...
transform(&vec);
Кроме того, обратите внимание, что приведенный выше код инвариантен по отношению к Vector3d
типу, являющемуся массивом или struct
. Вы можете Vector3d
в любой момент переключить определение с массива на a struct
и обратно, и вам не придется менять объявление функции. В любом случае функции получат агрегированный объект «по ссылке» (есть исключения, но в контексте этого обсуждения это правда).
Однако вы не увидите, что этот метод передачи массива используется явно слишком часто, просто потому, что слишком много людей запутываются из-за довольно запутанного синтаксиса и просто недостаточно знакомы с такими функциями языка C, чтобы использовать их должным образом. По этой причине в обычной реальной жизни передача массива в качестве указателя на его первый элемент является более популярным подходом. Это просто выглядит «проще».
Но на самом деле использование указателя на первый элемент для передачи массива - это очень нишевый прием, трюк, который служит очень конкретной цели: его единственная цель - облегчить передачу массивов разного размера (то есть размера во время выполнения) , Если вам действительно нужно иметь возможность обрабатывать массивы размера во время выполнения, то правильный способ передать такой массив - это указатель на его первый элемент с конкретным размером, предоставленным дополнительным параметром
void foo(char p[], unsigned plen);
На самом деле, во многих случаях очень полезно иметь возможность обрабатывать массивы размера во время выполнения, что также способствует популярности метода. Многие разработчики C просто никогда не сталкиваются (или никогда не осознают) необходимости обрабатывать массив фиксированного размера, поэтому не обращают внимания на правильную технику фиксированного размера.
Тем не менее, если размер массива фиксированный, передача его как указателя на элемент
void foo(char p[])
это серьезная техническая ошибка, которая, к сожалению, в наши дни довольно распространена. В таких случаях гораздо лучше подходит метод указателя на массив.
Другая причина, которая может помешать внедрению техники передачи массивов фиксированного размера, - это преобладание наивного подхода к типизации динамически выделяемых массивов. Например, если программа вызывает фиксированные массивы типа char[10]
(как в вашем примере), средний разработчик будет malloc
такие массивы, как
char *p = malloc(10 * sizeof *p);
Этот массив нельзя передать функции, объявленной как
void foo(char (*p)[10]);
что сбивает с толку рядового разработчика и заставляет отказаться от объявления параметра фиксированного размера, не задумываясь о нем. Однако на самом деле корень проблемы кроется в наивном malloc
подходе. malloc
Формат , показанный выше , должен быть зарезервирован для массивов размера времени выполнения. Если тип массива имеет размер во время компиляции, лучший способ malloc
будет выглядеть следующим образом
char (*p)[10] = malloc(sizeof *p);
Это, конечно, легко передается заявленному выше foo
foo(p);
и компилятор выполнит правильную проверку типа. Но опять же, это слишком сбивает с толку неподготовленного разработчика C, поэтому вы не будете часто видеть это в "типичном" среднем повседневном коде.
10
может быть заменен любой переменной в области видимости