我正在制作一个用于测试SDK的pong克隆...这些年来我没有C.
无论如何,我试图在const设置阶段这样做:
const int SCREEN_W = 480;
const int SCREEN_H = 480;
const int PLAYER_H_WIDTH = 50;
const int PLAYER_H_HEIGHT = 12;
const int BUFFER = 14;
const int LEFT_BUFFER = PLAYER_H_WIDTH+BUFFER;
const int RIGHT_BUFFER = SCREEN_W-LEFT_BUFFER;
除了编译器在LEFT_BUFFER
和RIGHT_BUFFER
行引发错误,因此让我想知道,为什么?
如果答案是“因为标准是这样说的”,我仍然想知道为什么(为什么标准这样说?)
编辑因为评论:
haccks注意到函数内部的那些行(比如,main(){})会编译,而在文件范围内它们却没有。我也问,为什么?
特定的GCC(实际上是MingW)错误是:initializer element is not constant
答案 0 :(得分:6)
使用静态存储持续时间声明的对象的初始化程序(在任何函数之外,或在具有static
关键字的函数内)必须是常量表达式,或者必须只包含常量表达式。
此声明:
const int PLAYER_H_WIDTH = 50;
不使PLAYER_H_WIDTH
成为常量表达式。关键字const
实际上并不意味着“常量”(即能够在编译时进行评估);它的意思是“只读”。
所以当你宣布:
const int LEFT_BUFFER = PLAYER_H_WIDTH+BUFFER;
您尝试使用非常量表达式初始化LEFT_BUFFER
。
参考:ISO C标准,2011年版,第6.7.9节第4段:
初始化程序中具有静态或。的对象的所有表达式 线程存储持续时间应为常量表达式或字符串 文字。
为了说明const
和constant
之间的区别,请执行以下操作:
const int r = rand();
是一个完全有效的声明,只要它不在文件范围内。对象(我不想将其称为“变量”)r
是 const (即只读),但不是常量(即,它的值无法在编译时确定。)
由于C的执行模型,该声明不能出现在任何函数之外,因为它不允许在输入main
之前执行用户代码。
如果初始化程序是常量并且是整数类型,那么C ++会使这些对象保持为常量; C没有。 C ++中的规则需要编译器多做一些工作;它确定对象的名称是否是一个常量表达式,取决于其初始化程序的形式。我不确定为什么C没有采用C ++规则;可能委员会认为这不值得(请记住,任何新功能都会给每个编译器实施者带来负担)。
作为解决方法,您可以使用预处理器:
#define PLAYER_H_WIDTH 50
/* ... */
#define LEFT_BUFFER (PLAYER_H_WIDTH+BUFFER)
或者你可以使用enum
:
enum { PLAYER_H_WIDTH = 50 };
enum { LEFT_BUFFER = PLAYER_H_WIDTH+BUFFER };
后者仅适用于int
类型的常量。诀窍是enum
声明创建了一个枚举类型,但枚举数仍然是int
类型。
答案 1 :(得分:1)
你的问题标题显示你被一些混乱误导了。它与a + b
或a - b
无关。如果你只是做
const int LEFT_BUFFER = BUFFER;
没有任何+
或-
。
问题是你定义的名字都不是“常量”,这意味着它们不能形成常量表达式。例如,SCREEN_W
不是常量。
在C语言中,术语“常量”适用于文字值(如42
)和枚举常量。声明为const
的变量不是C语言中的“常量”。如果需要,可以将它们称为“const变量”,但如果需要真正的常量,则永远无法使用它们。
这就是它在C中的方式。这就是它一直在C中的方式。如果你想用C语言声明一个清单常量,请使用#define
或enum
。请勿尝试使用const
。 const
具有一些令人愉快的属性(与#define
相比),但const
在C中不生成真常量的事实严重限制了const
用于声明清单常量的可用性。
您的原始示例实际上证明了这个问题。具有静态存储持续时间的对象需要在C中使用常量初始值设定项。由于PLAYER_H_WIDTH
,BUFFER
等不是常量,因此不能将它们用作具有静态存储持续时间的对象的初始值设定项。而且,问题再次与+
或-
无关,即使在
const int LEFT_BUFFER = BUFFER;
声明。
如果将这些行传输到本地范围,则对象将不再具有静态存储持续时间。它们将成为局部变量。上述限制不适用于局部变量,这就是您的声明编译没有任何问题的原因。但是,如果将关键字static
添加到上述声明中,即使在本地范围内,错误也会重新出现。
答案 2 :(得分:0)
C的常数实际上并不像我们预期的那样“恒定”。它是编译器的一个指标,表明变量的内容无法更改。初始化需要是常量表达式或字符串文字。
与C中常量最接近的是使用枚举或#define,例如
enum {
SCREEN_W = 480,
SCREEN_H = 480,
PLAYER_H_WIDTH = 50,
PLAYER_H_HEIGHT = 12,
BUFFER = 14,
LEFT_BUFFER = PLAYER_H_WIDTH+BUFFER,
RIGHT_BUFFER = SCREEN_W-LEFT_BUFFER
};
我尽可能使用枚举。
答案 3 :(得分:0)
如果要避免此错误,请使用#define指令预处理器!
const int SCREEN_W = 480;
const int SCREEN_H = 480;
const int PLAYER_H_WIDTH = 50;
const int PLAYER_H_HEIGHT = 12;
const int BUFFER = 14;
#define LEFT_BUFFER PLAYER_H_WIDTH + BUFFER
#define RIGHT_BUFFER SCREEN_W - LEFT_BUFFER
#define指令是一个预处理程序指令;预编译器在编译器甚至看到之前用它们的主体替换这些宏。可以将其视为自动搜索和替换源代码。
但是使用const限定符定义的常量最好被认为是一个不可修改的变量。它具有变量的所有属性:它有一个类型,它有一个大小,它有链接,你可以取其地址。
为此,编译器无法将const变量的赋值编译为另一个,因为赋值的变量不是常量表达式。