4

前几天我玩了一下指定的初始化程序,令我惊讶的是,多次使用同一个索引是有效的。更重要的是,当我这样做时,它甚至没有产生编译器警告、错误甚至信息性声明,甚至 PC-Lint 似乎也不在乎(我认为这最让我感到惊讶)。

我想知道在这种情况下编译器是否有原因甚至不提供信息消息,或者是否有额外的编译器/lint/等。可用于捕获或标记此问题的选项。

使用的工具:Renesas RX Standard Toolchain v1.2.0.0 (C99)、gcc 版本 4.4.3 (Ubuntu 4.4.3-4ubuntu5.1)(在 VM 中)、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 Designated Inits页面记录了一个 GNU 扩展,它允许您在初始化程序中使用范围,我可以看到它对于定义整个范围然后覆盖某些部分的能力很有用(例如int arr[] = {[0 ... 99] = -1, [42] = 1},但我不明白为什么它仍然至少在某种程度上没有被标记...

在同一个 GCC 页面的更下方,它确实解决了多个字段的主题,但没有解释它为什么会这样:“如果同一个字段被多次初始化,它具有上次初始化的值。如果有的话这种重写的初始化有副作用,不确定是否发生副作用。目前,GCC 会丢弃它们并发出警告。

这个IBM页面也展示了一个有趣的例子,但仍然没有回答(至少对我来说)为什么它至少不是某种构建消息......

最后,这个2000 年 12 月的gcc 补丁页面表明重复检查已被明确删除,但没有(根据我简要阅读的内容)解释原因。

那么,为什么这(似乎)被掩盖了?有没有办法让编译器(甚至 lint)标记这个(以提供更多安全性)(在 c/c99 中;不要只说“使用 c++”或其他东西:p)?

4

2 回答 2

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
于 2013-05-24T19:52:19.520 回答
2

该标准预见到可能存在重复并施加解决此问题的策略,最后一个初始化程序获胜。因此,对标准明确验证的代码的警告只会令人讨厌。(例如,一些编译器为“零”初始化程序给出的警告{0}

如果您的问题更多是关于动机,我猜想枚举是主要原因。枚举具有重复值的情况并不少见,因此使用这些值初始化数组会引起很大的麻烦。

其他动机可能更多。看一下

bool const usedSizes[] = { [sizeof(int)] = true, [sizeof(long)] = true, [sizeof(long long)] = true };

这里很容易重复初始化器,但具体值高度依赖于平台。

于 2013-05-24T19:53:41.090 回答