Постоянные типы массивов в C, изъяны в стандарте?
Пункт 6.7.3.8 состояний спецификации C99
Если спецификация типа массива включает в себя квалификаторы любого типа, тип элемента является так качественно, а не типом массива. Если спецификация типа функции включает в себя квалификаторы любого типа, поведение не определено.
В rationale (логическая страница 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?
Ответы
Ответ 1
Это известная проблема, которая несколько раз обсуждалась за последние 10 лет на comp.std.c. Суть в том, что конкретный случай, который вы представили, в настоящее время не является законным в стандарте C; вам нужно либо удалить квалификатор, либо воздержаться от использования указателя на массив, чтобы ссылаться на квалифицированные элементы в массиве.
Если вы думаете, что у вас есть хорошая идея, чтобы преодолеть эту проблему, вы можете отправить ее в news:comp.std.c
для обсуждения. Если другие согласны с тем, что это хорошая идея, вы или кто-то другой можете подать отчет о дефекте, чтобы изменить поведение (есть несколько членов комитета, которые часто бывают comp.std.c, поэтому отзывы людей, которые потенциально могли бы пересматривать DR, быть полезными, прежде чем подавать его). Я думаю, что могут возникнуть некоторые проблемы с вашим предложением о том, чтобы квалификаторы влияли на массив, но я должен был бы подумать о нем.
Ответ 2
Возможное обходное решение для программиста 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]);
/* ... */
}
}
Даже если это не очень элегантно. Элемент trailing array a
также имеет немного другое значение между C89 и C99, но по крайней мере вы получаете предполагаемый эффект.
Ответ 3
Ситуация неудобна с указателями (т.е. массивами), но здесь мое воспоминание о деталях:
const double *ap
- указатель на константу double;
double *const ap
- постоянный указатель на double;
const double *const ap
- постоянный указатель на константу double;
Итак, я считаю, что можно делать то, что вы просите, хотя я не пробовал это в течение многих лет - опция gcc
, которую вы используете, была недоступна в последний раз, когда я это сделал!
EDIT: этот ответ неверен для вопроса - я оставляю его, чтобы сохранить комментарии ниже, которые уточняют проблему для простых смертных (или ржавых разработчиков C...)