Подтвердить что ты не робот

Могу ли я передавать постоянные указатели, замаскированные под массивы?

void foo(const char *s);

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

void foo(const char s[]);

Существуют ли аналогичные эквиваленты для следующих двух?

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

Ответ 1

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

void foo(char s[const]);
void foo(const char s[const]);

Ответ 2

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

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

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

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

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

Это рассматривается в разделе 8.3.3/3 стандарта С++ 03 (и, вероятно, где-то рядом с С++ 11)

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

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

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

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

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

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

Ответ 3

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

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

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

и в C:

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


void foo(char* const a) { … }
void fooc(const char* const a) { … }

Ответ 4

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

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

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