Могу ли я передать постоянные указатели, замаскированные под массивы? - PullRequest
12 голосов
/ 27 октября 2011
void foo(const char *s);

эквивалентно:

void foo(const char s[]);

Есть ли аналоги, аналогичные следующим двум?

void foo(char * const s);
void foo(const char * const s);

Ответы [ 4 ]

14 голосов
/ 27 октября 2011

В C ++ компилятор автоматически преобразует параметры функции типа массив из N элементов типа T (где N может быть unknown ) в указатель на T . В том же преобразовании верхний уровень const отбрасываются квалификаторы для аргументов:

void f( const int x ); // => declares: void f( int )
void f( int * const ); // => declares: void f( int* )

Это означает, что в случае указателя квалификатор const верхнего уровня удаляется, а в случае массива он преобразуется в указатель на. Теперь, с другой стороны, вы не можете смешивать оба, только потому, что вы не можете объявить константный массив из N элементов типа T , поскольку массивы всегда постоянны. Это означает, что вы не можете написать:

void f( const int a[] const );  // Invalid type definition

Поскольку тип параметра недействителен. Если это был допустимый тип, то преобразования будут применяться, но, поскольку это не так, компилятор отклонит код перед попыткой выполнить преобразование.

Это рассматривается в §8.3.5 / 3 стандарта C ++ 03 (и, вероятно, где-то близко в C ++ 11)

Одно имя может использоваться для нескольких различных функций в одной области видимости; это перегрузка функции (пункт 13). Все объявления для функции с заданным списком параметров должны точно совпадать как по типу возвращаемого значения, так и по количеству и типу параметров; наличие или отсутствие многоточия считается частью типа функции. Тип функции определяется с использованием следующих правил. Тип каждого параметра определяется его собственным decl-specier-seq и декларатором. После определения типа каждого параметра любой параметр типа «массив из T» или «функция, возвращающая T» настраивается на «указатель на T» или «указатель на функцию, возвращающую T», соответственно. После составления списка типов параметров в этих типах выполняется несколько преобразований для определения типа функции. Любой cv-квалификатор, модифицирующий тип параметра, удаляется. [Пример: тип void(*)(const int) становится void(*)(int) - конец примера] Такие cv-квалификаторы влияют только на определение параметра в теле функции; они не влияют на тип функции. Если спецификатор класса хранения изменяет тип параметра, спецификатор удаляется. [Пример: регистр char * становится char * - конечный пример] Такие спецификаторы класса хранения влияют только на определение параметра в теле функции; они не влияют на тип функции. Результирующий список преобразованных типов параметров является списком типов параметров функции.

Обратите внимание, что, поскольку компилятор выполнит это преобразование, лучше написать фактический тип, который будет использоваться компилятором, следуя принципу наименьшего удивления:

void f( int a[10] ) { a[5] = 7; }

Компилятор не собирается проверять, что переданный массив имеет 10 элементов, он читает объявление как void f( int * ) и с радостью примет вызов с массивом из меньшего количества элементов или даже без массива (указатель на один int). Использование указателя в фактическом коде:

void f( int *a ) { a[5] = 7; }

Скорее всего, вызовет некоторые тревоги при проверке кода: мы гарантируем, что во всех вызовах f аргумент будет иметь размер не менее 6 элементов? Разве мы не должны передать также размер на всякий случай?

13 голосов
/ 27 октября 2011

Вы не можете в C89, но в C99 вы можете объявить эквиваленты как:

void foo(char s[const]);
void foo(const char s[const]);
0 голосов
/ 29 октября 2011

Я думал, что указатель может быть нулевым, в то время как аргумент массива не может быть нулевым (и что компилятору разрешено оптимизировать, зная это; однако на простом примере gcc-4.6 не выполняет такую ​​оптимизацию, даже с -O3).

Я ожидаю, что компилятор по-разному оптимизирует две функции ниже.Это не.У меня нет моего стандарта C под рукой, чтобы проверить, может ли он удалить тест в сс ниже.

int s (int *t)
{
  if (!t)
    return 0;
  return t[0] + t[1];
}


int ss (int t[])
{
  if (!t) // never false, since t is an array!!
    return 0;
  return t[0] + t[1];
}
0 голосов
/ 27 октября 2011

это будет полезно в некоторых случаях:

class AA {
  void foo(char a[]);
  void foo(const char a[]);
};

void AA::foo(char* const a) { … }
void AA::foo(const char* const a) { … }

и в С:

extern void foo(char a[]);
extern void fooc(const char a[]);


void foo(char* const a) { … }
void fooc(const char* const a) { … }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...