C99 Обозначенный дубликат индекса инициализатора вообще не помечен в выходных данных сборки или lint
На днях я немного поиграл с назначенными инициализаторами и, к своему удивлению, заметил, что один и тот же индекс допустимо использовать более одного раза. Более того, он даже не выдавал предупреждение компилятора, ошибку или даже информационное сообщение, когда я делал это, и даже PC-Lint, казалось, не заботился (что, я думаю, удивило меня больше всего).
Мне интересно, есть ли причина, по которой компиляторы даже не предоставляют информационное сообщение в этом случае, или есть дополнительный компилятор /lint/etc. варианты, которые могут быть доступны, чтобы поймать или пометить это.
Используемые инструменты: Renesas RX Standard Toolchain v1.2.0.0 (C99), gcc версия 4.4.3 (Ubuntu 4.4.3-4ubuntu5.1) (в виртуальной машине), Lint-NT 9.00i
Например, какой-то старый код, над которым я работаю, #defines связывает команды, а затем создает массив структур команд (существенно упрощенных здесь), которые нужно циклически просмотреть, чтобы найти и использовать эту конкретную команду:
#define CMD_RMEM 0
#define CMD_WMEM 1
#define CMD_XCRC 2
#define CMD_NULL 3
typedef struct
{
const char cmdID;
const char* cmdStr;
} CMD;
const CMD commands[] = {
{CMD_RMEM,"RMEM"},
{CMD_WMEM,"WMEM"},
{CMD_XCRC,"XCRC"},
{CMD_NULL,"NULL"},
};
Затем я вспомнил назначенный синтаксис инициализатора, который я видел где-то, и подумал, что он может обеспечить большую гибкость при расположении элементов в массиве в дополнение к обнаружению дублирующих индексов команд, например:
//(same #def's & typedef as above)
const CMD commands[] = {
[CMD_RMEM] = {CMD_RMEM,"RMEM"},
[CMD_NULL] = {CMD_NULL,"NULL"}, //different order in ititializer list,
// but designation keeps it in the same array/memory position, so
// this will still be the 'last' element
[CMD_CMEM] = {CMD_CMEM,"CMEM"},
[CMD_WMEM] = {CMD_WMEM,"WMEM"},
[CMD_XCRC] = {CMD_XCRC,"XCRC"},
};
будет производить тот же эффект, что и исходный код выше, но с гибкостью в расположении элементов в объявлении массива (что он делает) и (я думал / надеюсь), что
#define CMD_RMEM 0
#define CMD_WMEM 1
#define CMD_XCRC 1 // obvious dupe in a short list, but not so obvious
// if part of a much longer list or if split among multiple files
#define CMD_NULL 2
// (Same designated initializer section as above)
выдаст по крайней мере предупреждение, поскольку мы используем один и тот же назначенный индекс более одного раза (чего он не делает) (этот пример может легко возникнуть при добавлении команды без смещения последнего заполнителя NULL).
На странице " Назначенные элементы GCC" указано расширение GNU, которое позволяет вам использовать диапазоны в инициализаторах, которые, как я вижу, могут быть полезны с возможностью определения всего диапазона, а затем переопределять определенные части (например, int arr[] = {[0 ... 99] = -1, [42] = 1}
, но я не понимаю, почему он все еще не помечен на каком-то уровне...
Далее на той же странице GCC, он затрагивает тему нескольких полей, но не объясняет, почему он ведет себя так, как он делает: "Если одно и то же поле инициализируется несколько раз, оно имеет значение из последней инициализации. Если есть такая переопределенная инициализация имеет побочный эффект, неизвестно, происходит ли побочный эффект или нет. В настоящее время GCC отбрасывает их и выдает предупреждение ".
Эта страница IBM также показывает интересный пример, но все еще не отвечает (по крайней мере для меня), почему это не по крайней мере какое-то сообщение о сборке...
И, наконец, эта страница исправлений gcc от декабря 2000 года указывает, что дубликаты проверок были явно удалены, но не объясняет (из того, что я кратко прочитал) почему.
Итак, почему это (на первый взгляд) затуманено? И есть ли способ заставить компилятор (или даже lint) пометить это (для обеспечения большей безопасности) (в c/c99; не говорите просто "используйте C++" или что-то в этом роде:p)?
2 ответа
У меня нет объяснения, почему - возможно, просто потому, что назначенные инициализаторы все еще являются новыми и мало используются. Производители компиляторов должны учитывать количество программистов, которым будут полезны новые функции.
Clang предупреждает о вашей конструкции:
$ clang -std=c99 -Wall -c t.c
t.c:24:17: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
[CMD_XCRC] = {CMD_XCRC,"XCRC"},
^~~~~~~~
t.c:4:18: note: expanded from macro 'CMD_XCRC'
#define CMD_XCRC 1 // obvious dupe in a short list, but not so obvious
^
t.c:23:17: note: previous initialization is here
[CMD_WMEM] = {CMD_WMEM,"WMEM"},
^~~~~~~~
t.c:2:18: note: expanded from macro 'CMD_WMEM'
#define CMD_WMEM 1
^
(Я должен был внести незначительные изменения для его компиляции, но вы поняли.)
$ clang -v
Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn)
Target: x86_64-apple-darwin12.3.0
Thread model: posix
Стандарт предусматривает наличие дубликатов и навязывает стратегию для решения этой проблемы, выигрывает последний инициализатор. Так что предупреждения для кода, который явно проверен стандартом, будут просто раздражающими. (как, например, предупреждение, которое некоторые компиляторы дают для "нулевого" инициализатора {0}
)
Если ваш вопрос больше касается мотивации, я бы предположил, что перечисления являются основной причиной этого. Нередко перечисления имеют повторяющиеся значения, поэтому инициализация массива с ними может вызвать серьезные головные боли.
Другие мотивы для этого могут быть более сложными. Посмотри на
bool const usedSizes[] = { [sizeof(int)] = true, [sizeof(long)] = true, [sizeof(long long)] = true };
Здесь легко могут возникать дубликаты инициализаторов, но конкретные значения сильно зависят от платформы.