为什么C和C++中存在多字符字面量?

51
我不知道C和C++允许使用多字符字面量:不是 'c'(在C中为int类型,在C++中为char类型),而是 'tralivali'(属于int类型!)
enum
{
    ActionLeft = 'left',
    ActionRight = 'right',
    ActionForward = 'forward',
    ActionBackward = 'backward'
};

标准规定:

C99 6.4.4.4p10:“包含多个字符(例如,'ab')或包含不映射到单字节执行字符的字符或转义序列的整数字符常量的值是实现定义的。”

我发现它们在C4引擎中被广泛使用。但是我认为在谈论平台无关的序列化时它们并不安全。它们可能会造成混淆,因为它们看起来像字符串。那么多字符字面量的使用范围是什么?它们有什么用处吗?它们在C++中只是为了与C代码兼容吗?它们被认为是像goto操作符一样的不好的特性吗?


25
在C语言中,goto并不是一个坏的特性。它比多字符字面量要更有用。请注意,这只是翻译,没有解释或额外内容。 - Steve M
2
苹果曾经使用它们来识别开发者和应用程序名称。基本上,它们是一种可视化的方式来表示您的开发者ID。int id ='MYCP'; 苹果会将您的开发者ID告诉您作为字符文字而不仅仅是一个无聊的整数。 - Martin York
7
如果你对boost::mpl::string序列感兴趣,多字符字面值会被使用(滥用?)。 - Stuart Berg
1
我们使用多字符字面量来快速填充字符串。为了用“1234”填充一个字符串,我们使用*(int*)sz = '4321'。有时,memcpy(sz, "1234", 4)会被优化为相同的汇编代码,即*(int*)sz = '4321'。优化器并不总是这样做,因此我们使用多字符字面量来强制执行它。 - johnnycrash
1
在C++中,多字符字面值是有条件支持的。因此,您的代码可能无法编译。如果它们得到支持,它们具有实现定义的值。因此,实现可能选择将所有多字符字面值分配为值0,从而破坏您的代码。 - L. F.
这是一个来自B的遗留物 - undefined
6个回答

30

它使得在内存转储中更容易挑选出值。

例如:

enum state { waiting, running, stopped };

对比。

enum state { waiting = 'wait', running = 'run.', stopped = 'stop' };

在以下语句之后的内存转储:
s = stopped;

可能看起来像:

00 00 00 02 . . . .

在第一种情况下,vs:

73 74 6F 70 s t o p

使用多字符文字。 (当然,它是说“stop”还是“pots”取决于字节顺序)

2
听说在 Cray 机器上,“sizeof(char) == sizeof(int)”是成立的。我完全不知道 C 编译器在这些机器上会对多字符字面量做什么…… - pmg
2
根据Cray C&C ++参考手册(http://docs.cray.com/books/S-2179-52/html-S-2179-52/zfixedfqsizyiy.html#ZFIXED9ILWPOX2)的说明,多字符字面量的工作方式相同(即使char类型本身更大,也是8位/ char)。 - Ferruccio
@Ferruccio:知道这样的技巧是很好的。通常的编程是很普遍的,当一些语法有争议时,任何人都可以查找标准,而这些技巧只能通过实际工作/经验学习。 - legends2k
请注意,尽管存在"实现定义性", 但是在同一台计算机上运行代码时,“相等性应该保持不变”(int mb = 'test'; if(b == 'test'))。 - bobobobo
@Ferruccio:在第一种情况下,s 的值将为 00 00 00 02,因为 enum 的第一个值从 0 开始,除非被覆盖。 - legends2k

22
我不知道这个用得有多广泛,但"implementation-defined(实现定义)"对我来说是一个很大的警示信号。据我所知,这可能意味着实现可以选择忽略您的字符指定,而只是分配正常递增的值。它可能会做一些更好的事情,但您不能依赖跨编译器(甚至编译器版本)的该行为。至少,“goto”具有可预测(如果不希望)的行为......
那是我的2美分。
编辑:“实现定义”:
来自Bjarne Stroustrup的C++词汇表
“实现定义” - C++语义的一个方面,针对每个实现而不是为每个实现在标准中指定的。例如,int的大小(必须至少为16位,但可以更长)。尽可能避免实现定义行为。另请参见:未定义。TC ++ PL C.2。
还有...
“未定义” - C++语义的一个方面,其中不需要合理的行为。例如,使用值为零的指针进行解引用。尽量避免未定义的行为。另请参见:实现定义。TC ++ PL C.2。
我认为这意味着评论是正确的:它应该至少编译,尽管超出此范围的任何内容都没有指定。请注意定义中的建议。

3
据我所了解,编译失败是不允许的。 - Armen Tsirunyan
只要您不依赖字节顺序或尝试序列化值,您就没问题。 - Ferruccio
3
我完全同意关于红旗的观点。我主要对理论感兴趣。 - topright gamedev
6
这里提到的“未定义行为”与此无关。 “实现定义”和“未定义”是两个具有不同含义的术语。我认为多字符字面量不属于“鼻部恶魔”类别。我认为@Ferruccio是正确的:只要您不关心该功能在幕后如何实现,就可以使用该功能。 - Stuart Berg
@Ferruccio,superbatfish。“如果实现想要的话,它可以选择忽略您的字符指定并只分配正常递增值。”(引自Nick)只有当编译器的文档规定了特定的行为时,您才是正确的。 - ignis

5

我曾经看过和使用过四个字符字面值。它们映射到4个字节=一个32位单词。正如上面所说,它非常有用于调试目的。它们可以与int一起在switch/case语句中使用,这很好。

这种(4 Chars)是相当标准的(即至少由GCC和VC++支持),尽管结果(编译的实际值)可能因不同的实现而异。

但是超过4个字符?我不会使用。

更新:从C4页面得知:“对于我们的简单操作,我们将只提供一些值的枚举,在C4中通过指定四个字符的常量来完成”。所以他们正在使用4个字符字面值,就像我的情况一样。


没有,我没有遇到过这种野兽。我使用的代码是针对x86-32位Windows PC的。 - jv42
@pmg Cray支持POSIX吗? - Damian Yerrick
@tepples:我不确定,但根据维基百科上的一篇文章,我认为它是这样的。 - pmg
@pmg 因为在POSIX中,char类型始终是8位的。详见此链接 - Damian Yerrick

3
多字符字面量允许通过字符的等效表示来指定int值。适用于枚举、FourCC代码和标签以及非类型模板参数。使用多字符字面量,可以直接将FourCC code键入源中,这很方便。
在gcc中的实现描述在https://gcc.gnu.org/onlinedocs/cpp/Implementation-defined-behavior.html。请注意,该值被截断为int类型的大小,因此如果您的整数是4个字符宽,则'efgh' == 'abcdefgh',尽管gcc会发出溢出的警告。
不幸的是,如果传递了-pedantic,则gcc将对所有多字符字面量发出警告,因为它们的行为是实现定义的。如上所述,如果切换实现,则两个多字符字面量的相等性可能会改变。

3
C++14规范草案N4527第2.13.3节的第2条中:
“... 包含多个c-char的普通字符字面量是多字符字面量。多字符字面量或包含单个不可在执行字符集中表示的c-char的普通字符字面量是有条件支持的,具有int类型,并具有实现定义的值。”
之前对你问题的回答主要涉及支持多字符字面量的真实机器。特别是,在int为4个字节的平台上,四字节多字符字面量是可以使用的,可用于方便地进行操作,如Ferrucio的mem dump示例所示。但是,由于不能保证这种方法在其他平台上能够正常工作或以相同的方式工作,因此“应该弃用多字符字面量的使用,以便编写可移植程序”。

那么,通过以字符串存储的方式排列字符,使其具有可移植性如何?我猜最初编写标准的人视力不好,没有看到显而易见的解决方案。 - bit2shift
@bit2shift 哎呀?请解释一下,鉴于int的大小在不同平台上都不是常量,如何将这样的常量变得“显而易见”的可移植性。 - undefined

0

难以置信,我所知道的每个编译器都会将定义为4字符常量的UINT的第一个字符放在低有效字节(小端)中 --- 但Visual C则相反。

// file signature
#define SFKFILE_SIGNATURE 'SFPK' (S=53)

// check header
if (out_FileHdr->Signature != SFKFILE_SIGNATURE)
  • VC 上失败:

Borland:4B504653 4B504653

Watcom:4B504653 4B504653

VisualC:4B504653 5346504B


欢迎来到“实现定义”的行为 :) - Ray
这其实可能更可取,因为当逐字节读取后,字符会以原始顺序恢复。 - Jan Hošek
小端字节序。 - mcabreb

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