Этот вопрос уходит к гуру Си:
В C можно объявить указатель следующим образом:
char (* p)[10];
.. что в основном означает, что этот указатель указывает на массив из 10 символов. При объявлении подобного указателя замечательно то, что вы получите ошибку времени компиляции, если попытаетесь назначить указатель на массив другого размера для p. Это также даст вам ошибку времени компиляции, если вы попытаетесь присвоить значение простого указателя char для p. Я пробовал это с помощью gcc, и, похоже, он работает с ANSI, C89 и C99.
Мне кажется, что объявление такого указателя было бы очень полезно, особенно при передаче указателя на функцию. Обычно люди пишут прототип такой функции вот так:
void foo(char * p, int plen);
Если бы вы ожидали буфер определенного размера, вы бы просто проверили значение plen. Однако вы не можете гарантировать, что человек, который передаст вам p, действительно предоставит вам много действительных ячеек памяти в этом буфере. Вы должны верить, что человек, вызвавший эту функцию, поступает правильно. С другой стороны:
void foo(char (*p)[10]);
.. заставит вызывающую сторону предоставить вам буфер указанного размера.
Это кажется очень полезным, но я никогда не видел указателя, объявленного таким образом, ни в одном коде, с которым я когда-либо сталкивался.
Мой вопрос: есть ли причина, по которой люди не объявляют такие указатели? Я не вижу очевидной ловушки?
10
может быть заменен любой переменной в области видимостиОтветы:
То, что вы говорите в своем посте, абсолютно правильно. Я бы сказал, что каждый разработчик C приходит к одному и тому же открытию и к одному и тому же выводу, когда (если) они достигают определенного уровня владения языком C.
Когда специфика области вашего приложения вызывает массив определенного фиксированного размера (размер массива является константой времени компиляции), единственный правильный способ передать такой массив в функцию - использовать параметр указателя на массив
(на языке C ++ это тоже делается со ссылками
).
Это включит проверку типов на уровне языка, что обеспечит предоставление массива точно правильного размера в качестве аргумента. Фактически, во многих случаях люди используют эту технику неявно, даже не осознавая этого, скрывая тип массива за именем typedef.
Кроме того, обратите внимание, что приведенный выше код инвариантен по отношению к
Vector3d
типу, являющемуся массивом илиstruct
. Вы можетеVector3d
в любой момент переключить определение с массива на astruct
и обратно, и вам не придется менять объявление функции. В любом случае функции получат агрегированный объект «по ссылке» (есть исключения, но в контексте этого обсуждения это правда).Однако вы не увидите, что этот метод передачи массива используется явно слишком часто, просто потому, что слишком много людей запутываются из-за довольно запутанного синтаксиса и просто недостаточно знакомы с такими функциями языка C, чтобы использовать их должным образом. По этой причине в обычной реальной жизни передача массива в качестве указателя на его первый элемент является более популярным подходом. Это просто выглядит «проще».
Но на самом деле использование указателя на первый элемент для передачи массива - это очень нишевый прием, трюк, который служит очень конкретной цели: его единственная цель - облегчить передачу массивов разного размера (то есть размера во время выполнения) , Если вам действительно нужно иметь возможность обрабатывать массивы размера во время выполнения, то правильный способ передать такой массив - это указатель на его первый элемент с конкретным размером, предоставленным дополнительным параметром
На самом деле, во многих случаях очень полезно иметь возможность обрабатывать массивы размера во время выполнения, что также способствует популярности метода. Многие разработчики C просто никогда не сталкиваются (или никогда не осознают) необходимости обрабатывать массив фиксированного размера, поэтому не обращают внимания на правильную технику фиксированного размера.
Тем не менее, если размер массива фиксированный, передача его как указателя на элемент
это серьезная техническая ошибка, которая, к сожалению, в наши дни довольно распространена. В таких случаях гораздо лучше подходит метод указателя на массив.
Другая причина, которая может помешать внедрению техники передачи массивов фиксированного размера, - это преобладание наивного подхода к типизации динамически выделяемых массивов. Например, если программа вызывает фиксированные массивы типа
char[10]
(как в вашем примере), средний разработчик будетmalloc
такие массивы, какЭтот массив нельзя передать функции, объявленной как
что сбивает с толку рядового разработчика и заставляет отказаться от объявления параметра фиксированного размера, не задумываясь о нем. Однако на самом деле корень проблемы кроется в наивном
malloc
подходе.malloc
Формат , показанный выше , должен быть зарезервирован для массивов размера времени выполнения. Если тип массива имеет размер во время компиляции, лучший способmalloc
будет выглядеть следующим образомЭто, конечно, легко передается заявленному выше
foo
и компилятор выполнит правильную проверку типа. Но опять же, это слишком сбивает с толку неподготовленного разработчика C, поэтому вы не будете часто видеть это в "типичном" среднем повседневном коде.
источник
const
собственность с помощью этой техники.const char (*p)[N]
Аргумент не кажется совместимым с указателемchar table[N];
В противоположность этому , простойchar*
PTR остаются совместимыми сconst char*
аргументом.(*p)[i]
а не делать*p[i]
. Последний будет прыгать на размер массива, что почти наверняка не то, что вам нужно. По крайней мере, для меня изучение этого синтаксиса вызвало, а не предотвратило ошибку; Я бы быстрее получил правильный код, просто передав ему float *.const
указатель на массив изменяемых элементов. И да, это полностью отличается от указателя на массив неизменяемых элементов.Я хотел бы добавить к ответу AndreyT (если кто-то наткнется на эту страницу в поисках дополнительной информации по этой теме):
По мере того как я начинаю больше играть с этими объявлениями, я понимаю, что с ними связаны серьезные препятствия в C (очевидно, не в C ++). Довольно часто возникает ситуация, когда вы хотите дать вызывающей стороне константный указатель на буфер, в который вы записали. К сожалению, это невозможно при объявлении такого указателя в C. Другими словами, стандарт C (6.7.3 - Параграф 8) расходится с чем-то вроде этого:
Это ограничение, похоже, отсутствует в C ++, что делает такие объявления гораздо более полезными. Но в случае C необходимо вернуться к объявлению обычного указателя всякий раз, когда вам нужен указатель const на буфер фиксированного размера (если сам буфер не был объявлен как const с самого начала). Вы можете найти больше информации в этой цепочке писем: текст ссылки
На мой взгляд, это серьезное ограничение, и это может быть одной из основных причин, по которой люди обычно не объявляют такие указатели в C. Другая проблема заключается в том, что большинство людей даже не знают, что вы можете объявить такой указатель как AndreyT указал.
источник
Очевидная причина в том, что этот код не компилируется:
По умолчанию для массива используется неквалифицированный указатель.
Также см. Этот вопрос , использование
foo(&p)
должно работать.источник
foo(&p)
.Я также хочу использовать этот синтаксис, чтобы включить дополнительную проверку типов.
Но я также согласен с тем, что синтаксис и ментальная модель использования указателей проще и легче запомнить.
Вот еще несколько препятствий, с которыми я столкнулся.
Для доступа к массиву необходимо использовать
(*p)[]
:Заманчиво использовать вместо этого локальный указатель на символ:
Но это частично лишило бы цели использования правильного типа.
Следует не забывать использовать оператор адресации при присвоении адреса массива указателю на массив:
Оператор address-of получает адрес всего массива
&a
с правильным типом для его присвоенияp
. Без оператораa
автоматически преобразуется в адрес первого элемента массива, такого же, как в&a[0]
, но имеющего другой тип.Поскольку это автоматическое преобразование уже происходит, я всегда озадачен тем, что
&
это необходимо. Это согласуется с использованием&
переменных других типов, но я должен помнить, что массив является особенным и мне нужен,&
чтобы получить правильный тип адреса, даже если значение адреса такое же.Одна из причин моей проблемы может заключаться в том, что я изучил K&R C еще в 80-х годах, который еще не позволял использовать
&
оператор для целых массивов (хотя некоторые компиляторы игнорировали это или допускали синтаксис). Что, кстати, может быть еще одной причиной того, почему указатели на массивы трудно внедрить: они работают должным образом только после ANSI C, и&
ограничение оператора могло быть еще одной причиной, по которой они считались слишком неудобными.Когда
typedef
это не используется для создания типа для массива указателей на-(в общем заголовочном файле), то глобальный массив указатель на потребности более сложноеextern
заявление , чтобы разделить его по файлам:источник
Проще говоря, C так не поступает. Массив типа
T
передается как указатель на первыйT
в массиве, и это все, что вы получаете.Это позволяет использовать некоторые классные и элегантные алгоритмы, такие как цикл по массиву с такими выражениями, как
Обратной стороной является то, что управление размером зависит от вас. К сожалению, неспособность сделать это сознательно также привело к миллионам ошибок в коде C и / или возможностям злонамеренного использования.
Что близко к тому, что вы просите в C, так это передать
struct
(по значению) или указатель на него (по ссылке). Пока один и тот же тип структуры используется на обеих сторонах этой операции, и код, передающий ссылку, и код, который ее использует, согласны в отношении размера обрабатываемых данных.Ваша структура может содержать любые данные, которые вы хотите; он может содержать ваш массив четко определенного размера.
Тем не менее, ничто не мешает вам или некомпетентному или злонамеренному кодировщику использовать приведение типов, чтобы обмануть компилятор и заставить его рассматривать вашу структуру как структуру другого размера. Практически неограниченная способность делать такие вещи - часть дизайна C.
источник
Вы можете объявить массив символов несколькими способами:
Прототип функции, которая принимает массив по значению:
или по ссылке:
или по синтаксису массива:
источник
char (*p)[10] = malloc(sizeof *p)
.Я бы не рекомендовал это решение
поскольку он скрывает тот факт, что Vector3D имеет тип, о котором вы должны знать. Программисты обычно не ожидают, что переменные одного и того же типа будут иметь разные размеры. Рассматривать :
где sizeof a! = sizeof b
источник
sizeof(a)
не то же самоеsizeof(b)
?Может быть, я что-то упускаю, но ... поскольку массивы являются постоянными указателями, в основном это означает, что нет смысла передавать на них указатели.
Не могли бы вы просто использовать
void foo(char p[10], int plen);
?источник
В моем компиляторе (vs2008) он рассматривается
char (*p)[10]
как массив символьных указателей, как если бы не было скобок, даже если я компилирую как файл C. Поддерживает ли компилятор эту «переменную»? Если это так, то это основная причина не использовать его.источник