const и typedef массивов в C

в C, это возможно typedef массив, используя эту конструкцию :

typedef int table_t[N];

здесь table_t теперь определяется как массив N int. Любая переменная, объявленная как table_t t; теперь будет вести себя как обычный массив int.

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

int doSomething(table_t t);

сопоставимое прототип функции может быть :

int doSomething(int* t);

заслуга первой конструкции в том, что она обеспечивает N как размер таблицы. Во многих случаях безопаснее применять это свойство, а не полагаться на программиста, чтобы правильно выяснить это условие.

теперь все хорошо, за исключением того, что, чтобы гарантировать, что содержимое таблицы не будет изменено, необходимо использовать const квалификатор.

следующее утверждение относительно просто понять:

int doSomething(const int* t);

теперь doSomething гарантируйте, что он не будет изменять содержимое таблицы, переданной в качестве указателя. А как насчет этой почти эквивалентной конструкции ? :

int doSomething(const table_t t);

что это const здесь ? содержимое таблицы, или указатель на таблицу ? Если это указатель, который является const, есть ли другой способ (совместимый с C90) сохранить возможность определить размер таблицы и скажите, что его содержание будет const ?

обратите внимание, что иногда также необходимо изменить содержимое таблицы, поэтому const свойство не может быть встроено в определение typedef.

[редактировать] Спасибо за отличные ответы получили до сих пор. Подводя итог:

  • первоначальное предположение о размере typedef N совершенно неправильно. Он в основном ведет себя так же, как обычный указатель.
  • на const собственность также будет вести себя так же, как если бы это был указатель (в отличие от typedef для типа указателя, как подчеркнуто @random ниже)
  • для обеспечения размера (что не было первоначальным вопросом, но в конечном итоге очень важно сейчас...), см. Джонатан

5 ответов


содержимое таблицы будет постоянным. Легко проверено с этим кодом.

#include<stdio.h>

typedef int table_t[3];
void doSomething(const table_t t)
{
    t++;    //No error, it's a non-const pointer.
    t[1]=3; //Error, it's a pointer to const.

}

int main()
{
    table_t t={1,2,3};
    printf("%d %d %d %ld",t[0],t[1],t[2],sizeof(t));
    t[1]=5;
    doSomething(t);
    return 0;
}

во-первых, вы ошибаетесь, прототипы функций

int doSomething(table_t t);
int doSomething(int* t);

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

const-квалификация по массивам всегда применяется к базовому типу массива, поэтому два объявления

const table_t a;
int const a[N];

эквивалентны, а для параметров функций имеем

int doSomething(const table_t t);
int doSomething(int const* t);

типы массивов и типы указателей не эквивалентны на 100%, даже в этом контексте, когда вы в конечном итоге получаете a тип указателя для параметра функции. Ваша ошибка в предположении, что const поступил бы так же, если бы это были тип указателя.

чтобы расширить пример Арби:

typedef int table_t[3];
typedef int *pointer_t;

void doSomething(const table_t t)
{
    t++;    //No error, it's a non-const pointer.
    t[1]=3; //Error, it's a pointer to const.
}

void doSomethingElse(const pointer_t t)
{
    t++;    //Error, it's a const pointer.
    t[1]=3; //No error, it's pointer to plain int
}

Он действует аналогично const int *, а const pointer_t вместо эквивалентно int * const.

(также, отказ от ответственности, определяемый пользователем имена, заканчивающиеся на _t, не допускаются POSIX, они зарезервированы для будущего расширения)


достоинством первой конструкции является то, что она обеспечивает N как размер таблицы.

я не уверен, что вы имеете в виду здесь. В каких контекстах он будет" навязывать " его? Если вы объявляете функцию как

int doSomething(table_t t);

размер массива не быть исполнено. Я приказываю, чтобы обеспечить размер, вам придется идти по другому маршруту

int doSomething(table_t *t); // equivalent to 'int (*t)[N]'

что такое const здесь ?

как const... Когда const применяется к типу массива, он "падает" до всех элементов массива. Это значит, что const table_t - это массив констант ints, т. е. это эквивалентно const int [N] тип. Конечным результатом этого является то, что массив становится немодифицируемые. В контексте объявления параметра функции const table_t будет преобразован в const int *.

однако обратите внимание на одну особенность, которая не сразу очевидна в этом случае: сам тип массива остается неконст-квалифицированным. Это отдельные элементы становятся const. Фактически, невозможно const-квалифицировать сам тип массива в C. любые попытки сделать это сделают const-квалификацию "просеять" до отдельных элементов.

эта особенность приводит к довольно неприятным последствиям в массиве const-корректности. Например, этот код не будет компилироваться в C

table_t t;
const table_t *pt = &t;

хотя это выглядит довольно невинно с точки зрения const-правильности и будет компилироваться для любого типа объектов без массива. Язык C++ обновил свои правила const-корректности, чтобы решить эту проблему, в то время как C продолжает придерживаться своих старых способов.


стандарт 6.7.6.3 гласит:

объявление параметра как ‘массив типа’ должно быть заменено на ‘квалифицированных указатель на тип’

Это означает, что при объявлении параметра функции как const int тип массива, он распадается на указатель на const int (первый элемент массива). Эквивалентно const int* в этом случае.

также обратите внимание, что из-за вышеупомянутого правила указанный размер массива не добавляет дополнительной безопасности типов! Это один большой недостаток в языке C, но это так.

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