C 和 C++ 中的多字符文字
Posted
技术标签:
【中文标题】C 和 C++ 中的多字符文字【英文标题】:Multicharacter literal in C and C++ 【发布时间】:2011-04-27 00:47:53 【问题描述】:我不知道 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:“一个 整数字符常量包含 多个字符(例如,'ab'), 或包含字符或转义 不映射到 a 的序列 单字节执行字符,是 实现定义。”
我发现它们在C4 engine 中被广泛使用。但是当我们谈论平台无关的序列化时,我想它们并不安全。 Thay 也可能令人困惑,因为它看起来像字符串。那么多字符文字的使用范围是什么,它们对某些东西有用吗?它们在 C++ 中只是为了与 C 代码兼容吗?作为 goto 运算符,它们是否被认为是一个不好的功能?
【问题讨论】:
goto
不是一个坏功能;至少在 C 中。它比多字符文字有用得多。
Apple 过去使用它们来识别开发者和应用程序名称。基本上,它们是代表您的开发者 ID 的视觉方式。 int id='MYCP';
Apple 会将您的开发者 ID 作为字符文字告诉您,而不仅仅是一个无聊的旧 int。
在boost::mpl::string 序列中使用(滥用?)多字符文字,如果你喜欢这种事情的话。
我们使用多字符文字来快速填充字符串。要使用“1234”填充字符串,我们使用 *(int*)sz = '4321'。 memcpy(sz, "1234", 4) 有时会针对 *(int*)sz = '4321' 产生的相同程序集进行优化。优化器并不总是这样做,所以我们使用多字符文字强制它..
在 C++ 中,多字符文字是有条件支持的。因此您的代码可能无法编译。如果它们受支持,则它们具有 实现定义 值。因此,实现可能会选择为所有多字符文字分配值0
,从而破坏您的代码。
【参考方案1】:
这使得在内存转储中挑选值变得更加容易。
例子:
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”取决于字节顺序)
【讨论】:
@pmg:不。我认为会发生不好的事情? 嗯,我听说在 Cray 机器上,“sizeof (char) == sizeof (int)
”是真的。我完全不知道 C 编译器可能会对其中一个上的多字符文字做什么......
根据 Cray C & C++ 参考手册 (docs.cray.com/books/S-2179-52/html-S-2179-52/…),多字符文字的工作方式相同(8 位/字符,即使 char 类型本身更大)。
@Ferruccio:很高兴知道这些技巧。通常的编程很常见,当某些语法有争议时,任何人都可以查找标准,而这些技巧只能通过实际工作/经验来学习。
请注意,尽管有这种“实现定义性”,仍然应该保持平等 (int mb = 'test' ; if( b == 'test' )
) 应该保持不变,只要代码在同一台机器上运行。跨度>
【参考方案2】:
我不知道它的使用范围有多广,但“实现定义”对我来说是一个很大的危险信号。据我所知,这可能意味着实现可以选择忽略您的角色名称,并在需要时分配正常的递增值。它可能会做一些“更好”的事情,但你不能依赖跨编译器(甚至编译器版本)的行为。至少“goto”具有可预测的(如果不需要的话)行为......
无论如何,那是我的 2c。
编辑:关于“实现定义”:
来自Bjarne Stroustrup's C++ Glossary:
实现定义 - C++ 语义的一个方面,它是为每个实现定义的,而不是在标准中为每个实现指定的。一个示例是 int 的大小(必须至少为 16 位,但可以更长)。尽可能避免实现定义的行为。另请参阅:未定义。 TC++PL C.2.
还有……
undefined - C++ 语义的一个方面,不需要合理的行为。一个例子是取消引用一个值为 0 的指针。避免未定义的行为。另见:实现定义。 TC++PL C.2.
我相信这意味着评论是正确的:它至少应该编译,尽管没有指定任何其他内容。还要注意定义中的建议。
【讨论】:
据我了解不允许编译失败 只要您不依赖字节顺序或尝试序列化值就可以了。 我完全同意红旗。我的兴趣主要是理论上的。 这里对“未定义行为”的引用是无关紧要的。 “实现定义”和“未定义”是两个不同的术语,具有两种不同的含义。我不认为多字符文字属于nasal demons 类别。我认为@Ferruccio 是正确的:只要您不关心该功能是如何在后台实现的,您就可以使用该功能。 @Ferruccio,超级鱼。 “实现可以选择忽略您的角色指定,如果需要,只需分配正常的递增值。” (cit. Nick) 只有当你的编译器的文档要求特定的行为时你才可以。【参考方案3】:四个字符文字,我见过并使用过。它们映射到 4 个字节 = 一个 32 位字。如上所述,它对于调试目的非常有用。它们可以在带有整数的 switch/case 语句中使用,这很好。
这(4 个字符)非常标准(即至少受 GCC 和 VC++ 支持),尽管结果(编译的实际值)可能因一种实现而异。
但是超过 4 个字符?我不会用。
更新:来自 C4 页面:“对于我们的简单操作,我们将只提供一些值的枚举,这在 C4 中通过指定四字符常量来完成”。所以他们使用 4 个字符的文字,就像我的情况一样。
【讨论】:
不,没有遇到这些野兽。我使用的代码用于 x86-32 位 Windows PC。 @pmg Cray 是否支持 POSIX? @tepples:我不知道,但根据an article on Wikipedia 我认为是的。 @pmg 因为char
是always 8-bit in POSIX。【参考方案4】:
多字符文字允许通过字符中的等效表示来指定int
值。对枚举、FourCC 代码和标签以及非类型模板参数很有用。使用多字符文字,FourCC code 可以直接输入到源代码中,这很方便。
在 gcc 中的实现在 https://gcc.gnu.org/onlinedocs/cpp/Implementation-defined-behavior.html 中描述。请注意,该值将被截断为 int
类型的大小,因此如果您的整数为 4 个字符宽,则为 'efgh' == 'abcdefgh'
,尽管 gcc 会在溢出的文字上发出警告。
不幸的是,如果传递了-pedantic
,gcc 将对 all 多字符文字发出警告,因为它们的行为是实现定义的。正如您在上面看到的,如果您切换实现,两个多字符文字的相等性可能会发生变化。
【讨论】:
【参考方案5】:在C++14 specification draft N4527 部分 2.13.3,条目 2:
... 包含多个 c-char 的普通字符文字是多字符文字。多字符文字或包含无法在执行字符集中表示的单个 c-char 的普通字符文字是有条件支持的,具有 int 类型,并具有实现定义的值。
之前对您的问题的回答主要涉及支持多字符文字的真实机器。具体来说,在int
是 4 字节的平台上,四字节多字符很好并且可以方便地使用,如 Ferrucio 的内存转储示例。但是,由于不能保证这在其他平台上会起作用或以相同的方式起作用,应该不推荐在可移植程序中使用多字符文字。
【讨论】:
如何通过以与存储在字符串中相同的方式排列字符来使其可移植?我猜最初编写该标准部分的人是近视的,没有看到明显的解决方案。【参考方案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
【讨论】:
欢迎使用“实现定义”行为 :) 这实际上可能更可取,因为当之后逐字节读取时,字符会按原始顺序恢复。 小“端”。以上是关于C 和 C++ 中的多字符文字的主要内容,如果未能解决你的问题,请参考以下文章