在C语言中,字面量和常量是相同的概念吗?

5
在C语言中,字面值和常量是相同的概念吗?它们在使用中有什么区别吗?
3个回答

11

字面值(literals)常量(constants)在C语言中有着明显的区别。可以说,C语言中的字面值代表未命名占用内存的对象(通常是左值),而常量则代表不一定占用内存的(可能被命名的)值(常量是右值)。

C语言中的“经典”版本(C89/90)只有一种字面值:字符串字面值。在那个版本的C语言中没有其他类型的字面值。C99引入了所谓的复合字面值

与此同时,术语常量指代显式的值,如12.5f0xA's'。此外,在C语言中,枚举成员也被认为是常量

再次提醒,由于C语言中的字面值是左值,因此您可以获取并使用它们的地址。

const char *s = "Hello";
char (*p)[6] = &"World";
int (*a)[4] = &(int []) { 1, 2, 3, 4 };

因为常量是右值,所以你不能取它们的地址。

使用const关键字声明的对象在C语言术语中不被视为常量。它们不能用于需要常量值的地方(比如case标签、位域宽度或全局、静态变量初始化)。

P.S. 注意,在C中相关的术语与C++有很大不同。在C++中,术语字面量实际上涵盖了大部分C中所知的常量。而在C++中,const对象可以形成常量表达式。人们有时试图将C++术语强加于C之上,这经常导致混淆。

(另请参见我应该优先选择常量还是#define?


我想知道是谁误解了OP,他们是否将“常量”理解为标准中使用的术语或者是带有const限定符的变量。但我不同意常量和字面值完全是两回事,我认为“字符串字面值!”和‘c’之间存在一些相似之处。 - Daniel Fischer
@Daniel Fischer: OP 问的是 C 中的概念。所以我使用了语言规范中正式定义的术语。在 C 中,带 const 限定符的变量不是常量。而问题是关于常量的。并且,字符串字面值和字符常量之间没有相关的相似之处。这与说马和桌子相似是没有区别的,因为它们都有四条腿。没错,但这与问题无关。 - AnT stands with Russia
我知道const限定的变量并不是常量。我只是想知道OP是否也知道这一点。其他答案将其解释为const限定的变量,那么为什么OP不能这样做呢?至于“没有”相关相似之处,我想这是一个解释问题。字面值可以出现在静态变量的初始化器中,否则只能出现常量。虽然不多,但足以让我说完全不同可能有些言过其实。 - Daniel Fischer
用“significantly”替换 :) - AnT stands with Russia
@Victor S:K&R 显然是非正式的。正式的 C 术语不允许“字符串常量”。 “字符常量”和“字符串字面值”源于语言规范中“常量”和“字面量”的定义。因此,在这个意义上,它不仅仅是一种“惯例”。 - AnT stands with Russia
显示剩余9条评论

5
C标准(具体来说是 ISO/IEC 9899,第二版,1999-12-01)本身并没有定义“字面量”,因此这不是 C 中的一个特定概念。 我发现“字面量”有三种用法,如下所述。
首先,字符串字面量(或正式语法中的“字符串字面量”)是用引号括起来的字符序列,可选地带有“L”前缀(使其成为宽字符串字面量)。毫无疑问称之为字面量,因为字符串中的每个字符都代表它本身:源文本中的“b”在字符串中产生“b”。 (相反,源文本中的“34”作为数字而不是字符串,会在程序中产生值34而不是字符“3”和“4”。)这些字面量的语义在6.4.5的第4和第5段中定义。基本上,相邻的字面量连接起来("abc" "def"变成"abcdef"),附加了零字节,并使用结果初始化具有静态存储期的数组。因此,字符串字面量会导致对象。
其次,复合字面量是用于创建结构值的更复杂的构造。 “复合字面量”是一个不幸的名称,因为它根本不是字面量。也就是说,文字字面上的字符不一定完全代表它们自己。实际上,复合字面量甚至不是常量。复合字面量可以包含在运行时计算的表达式,包括函数调用!
第三,在6.1第1段中,标准指出,“文字和字符集成员”由粗体字表示。 这种“文字”的使用描述的是标准本身,而不是 C 语言中的事物;它意味着标准中的“goto”表示 C 语言中的字符串“goto”。
除了字符串字面量之外,我认为在 C 中没有“字面量”这个特定概念。
然而,“常量”是一个重要概念。
首先,有简单常量,例如“34”、“4.5f”和“'b'”。后者是字符常量;虽然用字符编写,但它具有整数值。常量包括整数常量(十进制、八进制和十六进制)、浮点常量(十进制和十六进制)、字符常量和枚举常量。枚举常量是在“enum”构造中指定的名称。
其次,定义在6.6中的常数表达式是一直可以被求值的(编译时),而不是在运行时;因此,任何需要使用常数的地方都可以使用它。 6.6为常数表达式设置了特定的规则。例如,如果x 是一个静态数组,如static int x[8];所声明的那样,则&x[(int) (6.8 * .5)]是一个常数表达式:它是x的第3个元素的地址。(您很少使用浮点数索引数组,但我在示例中包括它是为了显示允许作为常数表达式的一部分。)
关于 "const":标准似乎没有明确定义带有const限定符类型的对象为常量。相反,它说,如果尝试通过非const限定类型的lvalue(例如取消引用的指针)修改使用const限定类型定义的对象,则行为未定义。这意味着C实现不需要强制实施const对象的常量性。(另外,请注意,具有指向const的指针并不意味着指向的对象是const的,只是取消引用的指针不是可修改的lvalue。可能存在一个指向相同对象的单独指针,它没有const限定符。例如,在int i; int *p = &i; const int *q = p;中,q是指向const-int的指针,但i不是一个const对象,p是指向相同int的指针,尽管它是指向int而不是const。)

6.8 * .5 等于 3.4,小数部分为 0.4,它被截断了吗? - Kindred
1
@ptr_user7813604:实际上,数组下标必须是整数,所以在示例中我应该包含一个强制转换:&x[(int) (6.8 * .5)]。是的,在将浮点数转换为整数时,小数部分会被舍去。 - Eric Postpischil

1

不完全正确。常量变量(与宏定义的常量相对)是具有专用存储空间的实际变量,因此您可以获取它们的地址。您无法获取文字的地址。

编辑:对不起大家,看来我错了。


1
额外说明:编译器可以完全省略 static const 变量的存储空间,因此使用常量变量实际上不会付出任何“惩罚”。 - Dietrich Epp
3
“你不能取字面量的地址” 是完全错误的。事实上,你是可以取字面量的地址的。例如,字符串字面量会自动衰减为其自身的地址。而且你也可以显式地获取复合字面量的地址。实际上,在 C 中,字面量(与常量相对)才是你可以获取地址的对象。你不能取常量的地址。 - AnT stands with Russia
并且要补充一下@AndreyT所说的,你也不能取所有const限定变量的地址,尤其是当你用存储类register声明它们时。 - Jens Gustedt

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