文件范围内的可变修改数组

91
我想在我的Objective-C实现文件中创建一个常量静态数组,类似于在我的".m"文件的顶层添加以下内容:
static const int NUM_TYPES = 4;
static int types[NUM_TYPES] = {
  1,
  2,
  3,
  4 };

我计划在文件的后面使用`NUM_TYPES`,所以我想把它放在一个变量中。
然而,当我这样做时,我会得到错误信息:
“文件范围内的可变修改的'types'”
我猜这可能与数组大小是一个变量有关(当我在那里放置一个整数字面值时,比如`static int types[4]`,我就不会收到这个消息)。
我想修复这个问题,但也许我做错了...我在这里有两个目标:
1. 有一个可以在整个文件中访问的数组 2. 封装`NUM_TYPES`到一个变量中,这样我就不必在文件的不同地方散布相同的字面值
我该怎么办?
我在C FAQ(11.8)中找到了这个问题的解答:我不明白为什么不能在初始化程序和数组维度中使用const值

2
如果您将其定义为 #define kNUM_TYPES 4,会发生什么? - Jorge Israel Peña
那个可以运行...出于某种原因,我一直试图避免使用预处理器,因为我记得在某个地方读到过,但是我做了更多的研究,发现没有什么好理由不在这种情况下使用它。如果我在预处理器中创建对象(比如@"An NSString literal"),可能会不太理想。你代码唯一的问题就是不需要分号。 - Sam
1
C的相应规范问题是:*C中文件作用域下的可变大小数组* - Peter Mortensen
6个回答

66

4
改变const定义的对象(例如通过取消指针上的const并存储值来进行强制转换)是未定义行为。因此,这种对象的值是编译时或运行时常量(取决于存储持续时间)。该值不能仅因为C标准没有规定它可以被用作常量表达式而在常量表达式中使用。(如果目标对象没有定义const或者动态分配,则允许取消const并存储值;字符串字面值不是const但不能被写入。) - jilles
3
“@jilles ‘could potentially be changed by machine code’”并不意味着回答者的意思是“可能会被C语言代码改变”。此外,这句话还有另一个很好的原因:在编译当前TU时,可能存在不同TUs中的‘extern’常量其值未知的情况。 - user529758
18
改善这个回答的方式是展示如何解决这个问题。 - George Stocker
尝试解决这个问题的方法是CygnusX1的回答 - Peter Mortensen
1
但是这个问题是关于Objective-C而不是C的:“...在我的Objective-C实现文件中”。 - Peter Mortensen

34

如果你无论如何都要使用预处理器,就像其他答案中所述,那么你可以让编译器自动确定NUM_TYPES的值:

#define NUM_TYPES (sizeof types / sizeof types[0])
static int types[] = { 
  1,
  2, 
  3, 
  4 };

哇,这真的很酷……我不知道那是可能的。我认为这个计算的成本可以忽略不计。我能否假设编译器可以将其优化为静态值? - Sam
2
是的,对于像那样的对象,sizeof 的结果是编译时常量。 - caf

22
#define NUM_TYPES 4

11

还可以使用枚举。

typedef enum {
    typeNo1 = 1,
    typeNo2,
    typeNo3,
    typeNo4,
    NumOfTypes = typeNo4
}  TypeOfSomething;

8

正如其他答案中已经解释的那样,在C语言中,const仅仅意味着一个变量是只读的,仍然是一个运行时的值。然而,您可以在C语言中使用enum作为真正的常量:

enum { NUM_TYPES = 4 };
static int types[NUM_TYPES] = { 
  1, 2, 3, 4
};

是的,这是对许多来自C++(或类似C++的,如Arduino)的人隐含问题的真实答案。 - Peter Mortensen
1
虽然这个问题是关于Objective-C而不是C语言的,但是在我的Objective-C实现文件中... - Peter Mortensen

3
在我看来,这是许多C编译器中的一个缺陷。我可以确定,我使用过的编译器并不会将"static const"变量存储在地址上,而是直接在代码中替换为常量。这可以通过使用预处理器的#define指令和使用静态const变量时产生相同的代码校验和来验证。
无论哪种方式,尽可能使用静态const变量而不是#define指令,因为静态const是类型安全的。

1
这听起来相当糟糕,因为您可以获取static const变量的地址。您所描述的行为可能是有效的优化,但肯定不是总能奏效的东西。 - unwind
1
这其实没问题。C编译器可以在可能的情况下将const全局变量的单个使用替换为常量值,这是可以接受的。如果所有对变量的引用都转换为常量,则编译器可以完全删除它。如果您在任何地方使用地址,则不会被删除。但无论变量是否为const,根据语言标准,C都不允许具有变量作为大小的全局数组。 - Evan
这些都与关于Objective-C的问题有什么相关性呢?毕竟Objective-C和C是两种不同的编程语言。 - Toby Speight

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接