Легкое правило для чтения сложных объявлений const?
Для чтения сложных указателей указателя существует право-левое правило.
Но это правило не упоминает, как читать модификаторы const
.
Например, в простой декларации указателя const
можно применять несколькими способами:
char *buffer; // non-const pointer to non-const memory
const char *buffer; // non-const pointer to const memory
char const *buffer; // equivalent to previous declartion
char * const buffer = {0}; // const pointer to non-const memory
char * buffer const = {0}; // error
const char * const buffer = {0}; // const pointer to const memory
Как насчет использования const
с указателем объявления указателя?
char **x; // no const;
const char **x;
char * const *x;
char * * const x;
const char * const * x;
const char * * const x;
const char * const * const x;
А что такое простое правило для чтения этих объявлений?
Какие декларации имеют смысл?
Используется ли по часовой стрелке/спиральному правилу?
Два примера реального мира
Метод ASTUnit::LoadFromCommandLine
использует const char **
для предоставления аргументов командной строки (в источнике llvm clang).
Параметр параметра аргумента getopt()
объявляется следующим образом:
int getopt(int argc, char * const argv[], const char *optstring);
Где char * const argv[]
эквивалентно char * const * argv
в этом контексте.
Поскольку обе функции используют одну и ту же концепцию (вектор указателей на строки для аргументов), а декларации различаются - очевидные вопросы: почему они отличаются? Делает еще один смысл, чем другой?
Предполагается, что модификатор const
должен указать, что функция не управляет строками этого вектора и не меняет структуру вектора.
Ответы
Ответ 1
Модификатор const
тривиален: он изменяет предшествующее ему значение, если только
ничто не предшествует этому. Итак:
char const* buffer; // const modifies char
char* const buffer; // const modifies *
и т.д. Вообще, лучше избегать форм, где ничего не предшествует
const
, но на практике вы их увидите, так что вам нужно
помните, что когда тип const
не предшествует, вы должны логически
переместите его за первый тип. Итак:
const char** buffer;
на самом деле:
char const** buffer;
то есть указатель на указатель на const char.
Наконец, в объявлении функции a []
после чтения как *
раньше.
(Опять же, вероятно, лучше избегать этой вводящей в заблуждение записи, но
вы увидите это, поэтому вам придется иметь дело с этим.) Итак:
char * const argv[], // As function argument
является:
char *const * argv,
указатель на указатель const на char.
Ответ 2
(Попытка сосредоточиться на других аспектах вопроса)
правило для деклараций const должно читать их справа налево, а const
изменяет следующий токен. Исключение: в начале объявления const
изменяет предыдущий токен.
Существует обоснование этого исключения - для элементарных объявлений const char c
поиск некоторых людей более естественный, чем char const c
- и это сообщил, что предшествующая форма const char c
предшествует окончательному правилу const.
Getopt
int getopt(int argc, char * const argv[], const char *optstring);
или
int getopt(int argc, char * const * argv, const char *optstring);
Это означает, что argv
является указателем на const-вектор указателей на неконстантные строки.
Но можно было бы ожидать следующее объявление:
int getopt(int argc, char const * const * argv, const char *optstring);
(указатель на константный вектор на константные строки)
Потому что getopt()
не должен изменять строки, на которые ссылается через argv.
Не менее char **
(как используется в main()
) автоматически преобразуется в char * const * argv
.
Clang
ASTUnit::LoadFromCommandLine(..., const char **argv, ...);
Это означает, что argv
является указателем на неконстантный массив указателей на константные строки.
Снова можно было бы ожидать const char * const *argv
по той же причине, что и выше.
Но это более заметно, потому что char **
не конвертирует в const char **
, например.
int main(int argc, char **argv) {
const char **x = argv; // Compile error!
return 0;
}
дает ошибку компиляции, где
int main(int argc, char **argv) {
char * const *x = argv;
return 0;
}
и
int main(int argc, char **argv) {
const char * const *x = argv;
return 0;
}
нет.