【问题标题】:Error in string array initializer: initializer element is not constant字符串数组初始化程序中的错误:初始化程序元素不是常量
【发布时间】:2019-01-10 09:42:28
【问题描述】:

我需要定义一些字符串和一个数组,并使用这些字符串进行初始化,以供软件的不同部分使用。我想在这样的头文件中定义它们:

//.h file
const char *serviceStateKindNormal = "Normal";
const char *serviceStateKindUnmanned = "Unmanned";
const char *serviceStateKindScheduledMaintenanceDown = "ScheduledMaintenance (down)";
const char *serviceStateKindScheduledMaintenanceAvailable = "ScheduledMaintenance (available)";
const char *serviceStateKindMajorIncidentInProgress = "MajorIncidentInProgress";
const char *serviceStateKindPartialService = "PartialService";
const char *serviceStateKindOverloaded = "Overloaded";
const char *serviceStateKindGoingDown = "GoingDown";
const char *serviceStateKindDown = "Down";

const char *serviceStateKind[9] = {
    serviceStateKindNormal,
    serviceStateKindUnmanned,
    serviceStateKindScheduledMaintenanceDown,
    serviceStateKindScheduledMaintenanceAvailable,
    serviceStateKindMajorIncidentInProgress,
    serviceStateKindPartialService,
    serviceStateKindOverloaded,
    serviceStateKindGoingDown,
    serviceStateKindDown
};

但编译器显示

error: initializer element is not constant
     serviceStateKindNormal

这里到底有什么问题,我有什么选择来定义我的变量?

【问题讨论】:

标签: c compile-time-constant


【解决方案1】:

在 C 语言中,常量是指文字常量,例如(3.14、2.718 等)。 const 限定的对象(任何类型)在 C 语言术语中不是常量。

要在 C 语言术语中创建常量,请使用 #define 指令,例如:

#define ServiceStateKindNormal "Normal"

等等

【讨论】:

  • 我的意思是 C 中的关键字 const 不等同于 C++ 或其他语言中的 constant。字常量用于数字和符号字面量。
【解决方案2】:

您在文件范围内声明的变量初始化中的所有内容都必须是constant expression 或string literal,来自initialization。有一个列表什么是常量表达式,变量值不在其中。所以:

 // there is an array of char[2]{'a',\0'} somewhere in the memory
 // we store the pointer value to that array inside the variable a
 static const char *a = "a";
 // now we want to statically initialize variable b with the same value
 static const char *b = a;

不会起作用,因为b是用指针avalue初始化的,它不是一个常量表达式。初始化具有静态存储持续时间的变量时,您需要一个常量表达式。

你能做什么?以下:

  1. 好方法:为什么要存储指向字符串文字"Normal" 的指针?为什么不将数据本身存储在数组中?幸运的是,变量地址是一个常量表达式,所以我们可以在初始化时使用它!请注意,(几乎)所有用例和语义都保持不变,除了一些角落用法,如 sizeof(serviceStateKindNormal) 运算符结果。

    const char serviceStateKindNormal[] = "Normal";
    const char serviceStateKindUnmanned[] = "Unmanned";
    
    const char *serviceStateKind[] = {
        serviceStateKindNormal,
        serviceStateKindUnmanned,
    };
    
  2. 一种奇怪的方式:将指向字符串指针的指针存储在数组中。由于变量地址是常量表达式,这将起作用。请注意,这使 serviceStateKind 成为三星变量。您需要在使用时对数组进行双重取消引用。 serviceStateKind 是一个指向字符串的指针数组。请注意,如何使用这样的数组非常令人困惑,因此我建议您使用结构。

    const char *serviceStateKindNormal = "Normal";
    const char *serviceStateKindUnmanned = "Unmanned";
    
    const char **serviceStateKind[] = {
        &serviceStateKindNormal,
        &serviceStateKindUnmanned,
    };
    
    int main() {
        // question which one is correct?
        printf("%s\n", *serviceStateKind[1]);
        printf("%s\n", (*serviceStateKind)[1]);
        printf("%s\n", serviceStateKind[0][1]);
    }
    

但由于我不认为自己是一个三星级程序员,我会尽量做到至少两星:

    const char *serviceStateKindNormal = "Normal";
    const char *serviceStateKindUnmanned = "Unmanned";

    struct {
        // points to a string
        const char **pnt;
    // an array of pointers to string
    } serviceStateKind[] = {
        &serviceStateKindNormal,
        &serviceStateKindUnmanned,
    };

    int main() {
        // more verbose
        printf("%s\n", *serviceStateKind[0].pnt);
    }
  1. 老式的方式 - 宏。不要这样做。它的坏处是,您可以滥用字符串文字的预处理器连接,将它们连接在一起。

    #define serviceStateKindNormal  "Normal"
    #define serviceStateKindUnmanned  "Unmanned"
    
    const char *serviceStateKind[] = {
        serviceStateKindNormal,
        serviceStateKindUnmanned,
    };
    
    int main() { 
       # magic
       printf(serviceStateKindNormal " " serviceStateKindUnmanned "\n");
    }
    

我想在这样的头文件中定义它们:

如果您在头文件中定义变量,您希望用static 标记它们,这样在链接到使用该.h 文件的不同.c 文件时不会出现重复符号错误。此外,最好将变量标记为 const,以提示其他程序员它们在某种程度上是不可更改的。

【讨论】:

  • 您能解释一下为什么const char serviceStateKindNormal[] = "Normal"; 比char const * const serviceStateKindNormal = "Normal"; 更受欢迎吗? (我在回答中提供的只是收集了一堆反对票)
  • 嗯,因为它没有任何意义,除非你真的需要一个指向数组的指针。我不会将其命名为“首选”,它是不同的内存布局。在第一种情况下,您有一个变量,它本身就是一个数组并存储 7 个字符长度的字符串。在第二种情况下,您有一个指向数组的指针。我在这里称其为“好方法(TM)”,因为我认为这是最易读和最可靠的做事方式。您收集了反对票,因为将变量声明为 const 不允许用它的值初始化其他变量,所以它不能解决问题。
  • 所以你是说char const * const a = "a"; const char * b = a; 不应该编译?即使使用-std=c90 -pedantic -Wall 编译,clang 和 gcc 也会在没有警告的情况下编译。 ideone.com/TYhjX9
  • @Lanting:你确定 ideone 使用的是 C 编译器而不是 C++ 编译器吗?我的 gcc 为这个“char const * const a = "a"; const char * b = a;”提供了“error: initializer element is not constant”
  • @Lanting 因此,对于未来的读者来说,使用 const 限定变量初始化元素是编译器扩展。我很好奇你的评论,所以我asked about it here onstackoverflow。
猜你喜欢
  • 2020-08-14
  • 1970-01-01
  • 1970-01-01
  • 2012-04-10
  • 1970-01-01
  • 2011-06-08
  • 2015-06-25
  • 2017-09-01
相关资源
最近更新 更多