我不知道C和C ++允许multicharacter literal:不是'c'(在C中为int类型,在C ++中为char),而是'tralivali'(为int类型!)

enum
{
    ActionLeft = 'left',
    ActionRight = 'right',
    ActionForward = 'forward',
    ActionBackward = 'backward'
};


标准说:


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


我发现它们被广泛用于C4发动机。但是我想当我们谈论与平台无关的序列化时,它们并不安全。 Thay也会令人困惑,因为看起来像字符串。那么多字符文字的使用范围是什么,它们对某些东西有用吗?它们使用C ++仅仅是为了与C代码兼容吗?作为goto运算符,它们是否被视为不好的功能?

评论

goto并不是一个坏功能;至少在C语言中。它比多字符文字有用得多。

苹果以前使用它们来标识开发人员和应用程序名称。基本上,它们是代表您开发人员ID的直观方式。 int id ='MYCP'; Apple会将您的开发人员ID告诉您为字符文字,而不只是无聊的旧int。

如果您喜欢这种东西,请在boost :: mpl :: string序列中使用(滥用?)多字符文字。

我们使用多字符文字快速填充字符串。要使用“ 1234”填充字符串,我们使用*(int *)sz ='4321'。 memcpy(sz,“ 1234”,4)有时会优化为*(int *)sz ='4321'生成的同一程序集。优化器并不总是这样做,因此我们使用multichar文字强制它。.

在C ++中,有条件地支持多字符文字。因此,您的代码可能无法编译。如果支持,则它们具有实现定义的值。因此,实现有可能选择为所有多字符文字分配值0,从而破坏您的代码。

#1 楼

我不知道这种用法的使用范围,但是“实现定义”对我来说是一个很大的危险信号。据我所知,这可能意味着该实现可以选择忽略您的字符名称,并在需要时仅分配常规的增量值。它可能做些“更小”的事情,但是您不能依赖于整个编译器(甚至编译器版本)的行为。至少“ goto”具有可预测的(如果不希望的话)行为...

那还是我的2c。

编辑:在“实现定义”上:

摘自Bjarne Stroustrup的C ++词汇表:


实现定义-为每个实现而不是
定义的C ++语义的一个方面。在标准中为每个
实现指定。一个示例是int的大小
(必须至少为16
位,但可以更长)。尽可能避免
实现定义的行为
。另请参见:
未定义。 TC ++ PL C.2。


还...


undefined-C ++语义的一个方面,没有需要合理的行为。一个示例是
取消使用值
为零的指针。避免未定义的行为。另请参见
:实现定义。 TC ++ PL
C.2。


我相信这表示注释是正确的:尽管未指定其他内容,但它至少应编译。还要注意定义中的建议。

评论


据我了解,不允许编译失败

– Armen Tsirunyan
10-10-18在16:38



只要您不依赖字节顺序或尝试序列化值就可以了。

–Ferruccio
2010-10-18 17:42

我完全同意危险信号。我的兴趣主要是理论上的。

– topright gamedev
10-10-20在11:11



这里对“未定义行为”的引用是不相关的。 “实施定义”和“未定义”是两个具有两个不同含义的不同术语。我认为多字符字面量不属于鼻恶魔类别。我认为@Ferruccio是正确的:只要您不关心引擎盖下的功能实现方式,就可以使用该功能。

– Stuart Berg
2011年11月21日在22:33

@Ferruccio,超级蝙蝠鱼。 “该实现可以选择忽略您的字符名称,并在需要时分配正常的增量值。” (引自Nick。)只有在编译器的文档规定了特定的行为时,您才可以。

–ignis
2012年8月1日14:16

#2 楼

它使在内存转储中选择值更加容易。

示例:

enum state { waiting, running, stopped };


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


以下语句后的内存转储:

s = stopped;


可能看起来像: >
在第一种情况下,vs:

00 00 00 02 . . . .

使用多字符文字。 (当然,它说的是“停止”还是“电位器”取决于字节顺序)

评论


@pmg:不。我认为会发生不好的事情吗?

–Ferruccio
2010-10-18 17:39

好吧,我听说在Cray机器上,“ sizeof(char)== sizeof(int)”是正确的。我完全不知道C编译器可能会对其中之一上的多字符文字进行处理...

– pmg
2010-10-18 18:00

根据Cray C&C ++参考手册(docs.cray.com/books/S-2179-52/html-S-2179-52/…),多字符文字的工作方式相同(即使char为8位/字符类型本身更大)。

–Ferruccio
2012年8月1日15:34

@Ferruccio:很高兴知道这样的技巧。通常的编程很常见,当某些语法有争议时,任何人都可以查找标准,而这些技巧只能通过实际的工作/经验来学习。

–legends2k
13-10-11在12:37



请注意,尽管有这种“实现定义性”,但只要代码在同一台计算机上运行,​​仍然应保持相等(int mb ='test'; if(b =='test'))。

– bobobobo
2013年12月21日在1:29

#3 楼

我已经看到并使用了四个字符文字。它们映射到4个字节=一个32位字。如上所述,它对于调试非常有用。它们可以用在带有int的switch / case语句中,这很好。

尽管结果(实际值已编译),但这(4个字符)是相当标准的(即,至少受GCC和VC ++支持) )可能因一个实现而异。

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

更新:在C4页面上:“对于我们的简单操作,我们将仅提供一些值的枚举,这是在C4中通过指定四个字符的常量来完成的” 。因此,就像我的情况一样,他们使用的是4个字符的文字。

评论


不,没有遇到这些野兽之一。我使用的代码用于x86-32位Windows PC。

– jv42
2010-10-19 8:50

@pmg Cray是否支持POSIX?

–达米安·耶里克(Damian Yerrick)
15年8月18日在0:13

@tepples:我不知道,但是根据Wikipedia上的一篇文章,我认为是的。

– pmg
15年8月18日在9:19

@pmg因为char在POSIX中始终为8位。

–达米安·耶里克(Damian Yerrick)
15年8月18日在14:16

#4 楼

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

#5 楼

在C ++ 14规范草案N4527第2.13.3节中,条目2:


...包含多个c-char的普通字符文字是多字符文字。多字符文字或包含单个c-char的普通字符文字在执行字符集中不能表示,它是有条件支持的,具有int类型,并且具有实现定义的值。


您对问题的先前答案大多与支持多字符文字的真实计算机有关。特别是,在int为4字节的平台上,按照Ferrucio的内存转储示例,可以使用四字节的多字符,并且可以方便使用。但是,由于不能保证在其他平台上也能正常工作,因此对于可移植程序,不建议使用多字符文字。

评论


应该如何通过将字符存储在字符串中的方式进行排列来使其具有可移植性呢?我猜谁最初写了标准的那部分是近视的,没有看到明显的解决方案。

–bit2shift
17年9月1日21:30



#6 楼

令人难以置信的是,我认识的每个编译器都将定义为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

评论


欢迎来到“实现定义”行为:)

–雷
8月5日8:16

实际上,这可能是更理想的选择,因为在以后逐字节读取字符时,将按原始顺序恢复字符。

– JanHošek
9月3日,11:05