Типы константных массивов в C, недостаток в стандарте? - PullRequest
15 голосов
/ 20 ноября 2008

Пункт 6.7.3.8 спецификации C99 гласит:

Если спецификация типа массива включает какие-либо квалификаторы типа, то тип элемента определяется так, а не тип массива. Если спецификация типа функции включает в себя какие-либо квалификаторы типа, поведение не определено.

В обосновании (логическая страница 87, физическая страница 94) приведен пример приведения плоского указателя к указателю массива (переменной длины).

void g(double *ap, int n)
{
    double (*a)[n] = (double (*)[n]) ap;
    /* ... */ a[1][2] /* ... */
}

Конечно, если массив ap не изменен внутри функции, он должен быть помечен как const, однако приведение в

void g(const double *ap, int n)
{
    const double (*a)[n] = (const double (*)[n]) ap;
    /* ... */
}

не сохраняет квалификатор const, поскольку (согласно 6.7.3.8) он применяется к элементам цели вместо самой цели, которая имеет тип массива double[n]. Это означает, что компиляторы будут справедливо жаловаться, если им будут предоставлены соответствующие флаги (-Wcast-qual для GCC). Не существует способа обозначить тип массива const в C, но это приведение очень полезно и «правильно». Флаг -Wcast-qual полезен для выявления неправильного использования параметров массива, но ложные срабатывания препятствуют его использованию. Обратите внимание, что индексирование a[i][j] является более читабельным и со многими компиляторами дает лучший машинный код, чем ap[i*n+j], поскольку первый позволяет вывести некоторую целочисленную арифметику из внутренних циклов с меньшим количеством анализа.

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

Ответы [ 3 ]

6 голосов
/ 20 ноября 2008

Это известная проблема, которая обсуждалась несколько раз за последние 10 лет на comp.std.c. Суть в том, что конкретный случай, который вы представили, в настоящее время недопустим в стандарте C; вам нужно либо удалить спецификатор, либо воздержаться от использования указателя на массив для ссылки на квалифицированные элементы в массиве.

Если вы считаете, что у вас есть хорошая идея для преодоления этой проблемы, вы можете опубликовать ее на news:comp.std.c для обсуждения. Если другие согласятся с тем, что это хорошая идея, вы или кто-то другой можете подать отчет о дефекте, чтобы изменить поведение (есть несколько членов комитета, которые часто посещают comp.std.c, поэтому отзывы людей, которые потенциально будут рассматривать DR, будут полезно иметь до подачи заявки). Я думаю, что в вашем предложении могут быть некоторые проблемы, связанные с тем, что квалификаторы влияют на сам массив, но я должен подумать об этом.

1 голос
/ 20 ноября 2008

Возможный обходной путь для программиста на C (но не дизайнера компилятора):

gcc с -Wcast-qual не жалуется на это:

void g(const double *ap, int n)
{
    int i;
    struct box 
    {
      double a[n];
    };
    const struct box *s = (const struct box *)ap;

    for (i=0; i<n; ++i)
    {
       doStuffWith(s->a[i]);
       /* ... */
    }
}

Даже если это не очень элегантно. Задний элемент массива a также имеет немного другое значение между C89 и C99, но, по крайней мере, вы получите ожидаемый эффект.

0 голосов
/ 20 ноября 2008

С указателями (т.е. массивами) ситуация неловкая, но вот мои воспоминания о деталях:

const double *ap - указатель на константу double;

double *const ap - постоянный указатель на двойное число;

const double *const ap - постоянный указатель на константу double;

Так что я считаю, что можно делать то, что вы просите, хотя я не пробовал это годами - используемая вами опция gcc была недоступна в последний раз, когда я это делал!

РЕДАКТИРОВАТЬ: Этот ответ не является правильным для вопроса - я оставляю это, чтобы сохранить комментарии ниже, которые проясняют проблему для простых смертных (или ржавых разработчиков C ...)

...