【问题标题】:Initialization of static array from constant string从常量字符串初始化静态数组
【发布时间】:2011-09-13 15:11:44
【问题描述】:

我正在做一些低级编程。为了我的任务,我需要初始化(有时是硬件限制的)数组。它可以只是 char[],也可以是 unsigned short 或其他。

最易读的方法是只使用一些已知长度的常量字符串。为了简化任务,我编写了一个宏来帮助自己。

#define INI( x ) { (x[0] << 8) | 0x00, (x[1] << 8) | 0x00 }

static const unsigned int tab[] = INI("ab");

int main(){
    return 0;
}

当然,上面的宏位于某个#ifdef 块内,并且取决于构建它的架构。我遇到的问题是我收到错误:

initializer element is not constant
main.c:3: error: (near initialization for "tab[0]")
initializer element is not constant
main.c:3: error: (near initialization for "tab[1]")

但上面的代码扩展为:

static const unsigned int tab[] = { ("ab"[0] << 8) | 0x00, ("ab"[1] << 8) | 0x00 };


int main(){
    return 0;
}

每个元素不仅在编译时是常量,而且在预处理器时也是常量。甚至可以创建宏,从字符串中获取每个字符并进行一些操作(如果只有预处理器能够获取字符串的长度并且当然会有一些循环选项)。

那么 - 为什么编译器无法提取这些信息,我的选择是什么?真诚感谢任何帮助。

PS。我知道它在 main() 中作为

const unsigned int tab[] = INI("ab");

但我在任何函数之外都需要它。

【问题讨论】:

  • 我可以使用 DevStudio 2010 来编译它。编辑:实际上,这就是 C++ 编译器的工作原理。
  • @Throwback1986 - 当然是 C99。很抱歉没有指定它。我只是假设会考虑这个版本。
  • | 0x00 中与 0 进行 oring 有什么意义?
  • @Jens - 嘿好地方 :),它只是带有第二个参数的更复杂宏的剩余部分 - 即有时它不是 0x0。 OTOH,我发现它也更具可读性 - 它提醒了包中还有另一个字节。

标签: c arrays string static initialization


【解决方案1】:

您不能在预处理器中进行字符串下标/索引。你可以做的是稍微改变一下宏:

#define ROW( x, y ) { ((x) << 8) | 0x00, ((y) << 8) | 0x00 }

static const unsigned int tab[] = ROW('a' , 'b');

【讨论】:

  • 这与预处理器或数组初始化器无关。 static const char foo = "ab"[0]; 也不起作用。
  • 是的,这很明显。但是您无法通过尝试在预处理器中解决它来解决它。
  • @wildplasser - 你可以在函数内部做这样的事情。感谢您的建议,但不幸的是,这是不可接受的。我在已知长度的 char* 字符串中得到了很多参数(在编译时)。有时 char[] 就足够了,但有时我需要将这些数据放入 uint16 或 uint32 中并进行一些位移。不幸的是,预处理器不允许我遍历字符串中的每个字符,或者以简单的方式将“数据”转换为“d”、“a”、“t”、“a”(因为我以后可以遍历它)。 OTOH 添加一些编译时脚本可能不会被客户构建系统接受。
  • 预处理器中应该有反引号。我认为您可以选择 2 个选项:1)生成稍后包含的代码 2)“按原样”接受字符串作为输入并稍后在初始化期间处理它们。这意味着您的某些定义“unsigned int tab[];”需要更改为超大或动态分配。另外:您需要跟踪它们的大小。
【解决方案2】:

假设它是 C99,标准说:

此复合文字中的表达式必须是常量。

参考具有文件范围的指针的示例。拥有一个带有常量变量的表达式是不够的——结果表达式也必须是常量。

【讨论】:

  • 是的。但是为什么它允许在函数内部呢?这对我来说真的没有意义。这是不一致的。有时我认为只需一点点努力就可以使 C 本身成为一种很好的语言,甚至 C++ 都不是必需的。但是它的部件被严重损坏让我经常真的不使用而是反对这种语言。
  • 如果一个名字的声明在任何块/函数之外,它就具有文件范围。如果它在 main 方法中声明,那么它在一个块内,所以它没有文件范围。
  • 仍然没有意义。表达式本身(顺便说一句)是恒定的。只是比 2+2*2 更具表现力。我想接受它并继续前进是最好的解决方案:)
  • 在 C 中,唯一不变的表达式是文字(如1)和字符串文字(如"ab")。就是这样。对于编译器而言,下标不被视为常量表达式
  • @Foo Bath - 那么为什么它在函数内部使用时对于编译器来说是常量?
猜你喜欢
  • 2016-10-28
  • 2010-11-30
  • 2012-12-25
  • 2010-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多