Простое правило для чтения сложных константных объявлений? - PullRequest
41 голосов
/ 23 сентября 2011

Для чтения объявлений сложного указателя существует правило влево-вправо .

Но в этом правиле не упоминается, как читать 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).

Параметр вектора аргумента getopt() объявлен так:

int getopt(int argc, char * const argv[], const char *optstring);

Где char * const argv[] эквивалентно char * const * argv в этом контексте.

Поскольку обе функции используют одну и ту же концепцию (вектор указателей на строки для предоставления аргументов) и объявления различаются - очевидные вопросы: почему они отличаются? Имеет ли один смысл больше, чем другой?

Намерение должно быть таким: Модификатор const должен указывать, что функция не манипулирует строками этого вектора и не изменяет структуру вектора.

Ответы [ 2 ]

61 голосов
/ 23 сентября 2011

Модификатор const тривиален: он изменяет то, что ему предшествует, если только ничто не предшествует этому. Итак:

char const* buffer;  // const modifies char
char* const buffer;  // const modifies *

и т. Д. Как правило, лучше избегать форм, где ничто не предшествует const, но на практике вы их увидите, поэтому вам придется помните, что когда ни один тип не предшествует const, вы должны логически переместите это позади первого типа. Итак:

const char** buffer;

на самом деле:

char const** buffer;

, то есть указатель на указатель на const char.

Наконец, в объявлении функции [] after читается как * before. (Опять же, вероятно, лучше избегать этой вводящей в заблуждение записи, но вы увидите это, так что вам придется с этим справиться.) Итак:

char * const argv[],  //  As function argument

есть:

char *const * argv,

указатель на константный указатель на символ.

6 голосов
/ 28 сентября 2011

(пытаясь сосредоточиться на других аспектах вопроса)

Практическое правило для объявлений const заключается в чтении их справа налево, а const изменяет следующий токен. Исключение: в начале объявления const изменяет предыдущий токен.

За этим исключением стоит обоснование - для элементарных объявлений const char c выглядит для некоторых людей более естественных, чем char const c - и сообщается, что форма предшественника const char c предшествует окончательному константному править.

Getopt

int getopt(int argc, char * const argv[], const char *optstring);

или

int getopt(int argc, char * const * argv, const char *optstring);

Это означает, что argv является указателем на константный вектор указателей на неконстантные строки.

Но можно ожидать следующего объявления:

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;
}

нет.

...