Именно со звездой - демонические конструкции могут принимать вид
const char* const* blah_blah,
char const* blah
int const* const integer,
int* const* const и так далее в самых разных комбинациях. Как расшифровать сии начертания и зачем вообще может кому-то понадобиться оборачиваться в const, будто шелкопряду?
Ответ
Такие записи проще всего рассматривать справа налево. Например,
Данное объявление
const char * const * blah_blah;
объявляет указатель с именем blah_blah, который указывает на константный указатель (так как квалификатор const предшествует *, если смотреть справа налево), который указывает на константный объект типа char.
Вот пример простой программы, который облегчает понимание этого объявления.
#include
int main(void)
{
const char * const * blah_blah;
const char *p = "Hello";
blah_blah = &p;
puts( *blah_blah );
}
На консоль будет выведено
Hello
Можно ввести typedef имена для наглядности
typedef const char CONST_CHAR_TYPE;
typedef CONST_CHAR_TYPE *POINTER_TO_CONST_CHAR_TYPE;
const POINTER_TO_CONST_CHAR_TYPE *blah_blah;
в этом случае переменная blah_blah является указателем на константный указатель на константный объект типа char
Я думаю будет легче понять эти объявления, если следовать грамматике определения указателя.
Указатель в стандарте C определяется следующим образом
pointer:
* type-qualifier-listopt
* type-qualifier-listopt pointer
то есть за знаком * может следовать список квалификации, который к нему относится.
Поэтому предыдущее объявление можно записать как
const char ( * const ( *blah_blah ) );
Можно сделать и сам указатель blah_blah константным. Но тогда он должен быть явно инициализирован при объявлении, если имеет автоматическую продолжительность памяти. Например
const char ( * const ( * const blah_blah ) ) = /* некоторое значение */;
или
const char * const * const blah_blah = /* некоторое значение */;
Вот демонстрационная программа, в которой сам указатель blah_blah также объявлен как константный.
#include
int main(void)
{
const char *p = "Hello";
const char ( * const ( * const blah_blah ) ) = &p;
puts( *blah_blah );
}
Объявление этого указателя читается как константный указатель на константный указатель на константный объект типа char
В объявления функций, имеющих прототип, можно опускать идентификатор параметра.
Ниже представлена такая программа
#include
void display( const char ( * const ( * const ) ) );
void display( const char * const * const );
void display( const char ( * const ( * ) ) );
void display( const char * const * );
int main(void)
{
const char *p = "Hello";
display( &p );
}
void display( const char * const * const blah_blah )
{
puts( *blah_blah );
}
Так как объявлений одной и той же функции (без ее определения) может быть несколько, то все выше указанные функции объявляют одну и ту же функцию. Так называемый квалификатор верхнего уровня, который относится непосредственно к параметру, можно убрать из объявления функции. То есть (еще один пример) эти пары функций объявляют одни и те же функции
void f( const int x );
void f( int x );
и
void g( int * const p );
void g( int * p );
Комментариев нет:
Отправить комментарий