为什么const a + const b不是const本身?

时间:2014-01-14 22:39:13

标签: c

我正在制作一个用于测试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_BUFFERRIGHT_BUFFER行引发错误,因此让我想知道,为什么?

如果答案是“因为标准是这样说的”,我仍然想知道为什么(为什么标准这样说?)

编辑因为评论:

haccks注意到函数内部的那些行(比如,main(){})会编译,而在文件范围内它们却没有。我也问,为什么?

特定的GCC(实际上是MingW)错误是:initializer element is not constant

4 个答案:

答案 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段:

  

初始化程序中具有静态或。的对象的所有表达式   线程存储持续时间应为常量表达式或字符串   文字。

为了说明constconstant之间的区别,请执行以下操作:

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 + ba - b无关。如果你只是做

,你会得到同样的错误
const int LEFT_BUFFER = BUFFER;

没有任何+-

问题是你定义的名字都不是“常量”,这意味着它们不能形成常量表达式。例如,SCREEN_W不是常量。

在C语言中,术语“常量”适用于文字值(如42)和枚举常量。声明为const的变量不是C语言中的“常量”。如果需要,可以将它们称为“const变量”,但如果需要真正的常量,则永远无法使用它们。

这就是它在C中的方式。这就是它一直在C中的方式。如果你想用C语言声明一个清单常量,请使用#defineenum。请勿尝试使用constconst具有一些令人愉快的属性(与#define相比),但const在C中不生成真常量的事实严重限制了const用于声明清单常量的可用性。

您的原始示例实际上证明了这个问题。具有静态存储持续时间的对象需要在C中使用常量初始值设定项。由于PLAYER_H_WIDTHBUFFER不是常量,因此不能将它们用作具有静态存储持续时间的对象的初始值设定项。而且,问题再次与+-无关,即使在

中也存在
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变量的赋值编译为另一个,因为赋值的变量不是常量表达式。