如何使用十六进制数字初始化字符数组?

21

我使用utf8并需要将一个常量保存在字符数组中:

const char s[] = {0xE2,0x82,0xAC, 0}; //the euro sign

然而它给了我一个错误:

test.cpp:15:40: error: narrowing conversion of ‘226’ from ‘int’ to ‘const char’ inside { } [-fpermissive]

我必须将所有十六进制数字转换为字符,这让我感到繁琐而不舒服。是否有其他适当的方法可以做到这一点?


@AaronMcDaid 看看我的第一句话? - SwiftMango
3
为什么不用const char s[] = u8"\u20AC"; - Kerrek SB
正如@KerrekSB所提到的,但这是C++11的一个特性。 - πάντα ῥεῖ
4个回答

37

char 可能是 signedunsigned(默认取决于实现)。你可能想要:

  const unsigned char s[] = {0xE2,0x82,0xAC, 0}; 

或者
  const char s[] = "\xe2\x82\xac";

或使用许多最近的编译器(包括GCC

  const char s[] = "€";

一个 字符串字面量 是一个char数组,除非你给它加上一些前缀。

请参见 GCC 的 -funsigned-char (或-fsigned-char)选项。

在某些实现中,char是无符号的,而CHAR_MAX为255(CHAR_MIN为0)。在其他情况下,char是有符号的,因此CHAR_MIN为-128,CHAR_MAX为127(例如,在Linux/PowerPC/32位和Linux/x86/32位上情况不同)。据我所知,标准没有禁止19位有符号字符。


1
@John 如果您没有指定char的有符号性,那么您正在使用编译器的默认设置...这可能会在不同的编译器供应商(甚至是同一编译器的不同版本)之间发生变化。当您需要一个char作为一个byte时,您应该声明它,并且不要对编译器可能会做什么或不会做什么做出任何假设。 - Zac Howland
2
@BasileStarynkevitch:是的,就在几天前,我花了很长时间深入研究标准,以找出为什么我的代码不起作用,然后我发现了这个宝石,从中我意识到我需要三个重载,而不是两个。参考C++03:3.9.1基本类型“1 / [...]普通字符、有符号字符和无符号字符是三种不同的类型。[...]” - John Dibling
1
@ZacHowland:同样的条款还指出,“在任何特定的实现中,一个普通的char对象可以取得与signed char或unsigned char相同的值;哪一个是实现定义的。”因此,char不同于signed charunsigned char,但它们在基本层面上非常接近,以至于在我15年的C++专业编程生涯中,我只需要区分它们一次 - John Dibling
1
仅代表个人意见,从风格的角度来看,如果是文本,请使用char。我以前尝试过使用unsigned char(因为我经常处理带有重音符号的字符):它根本不起作用(因为很多函数都需要char*std::string,并且字符串字面值是char[]),这会让读者感到困惑。 - James Kanze
1
@ZacHowland:我预测两年后你会为某个东西写第三个重载。但是之后你就又可以再用15年了。 :) - John Dibling
显示剩余18条评论

0
你的问题的简短答案是你正在溢出一个char。char的范围为[-128, 127]。0xE2 = 226 > 127。你需要使用的是unsigned char,它的范围为[0, 255]。
unsigned char s = {0xE2,0x82,0xAC, 0};

那么默认情况下,如果没有指定符号,char 就是有符号的吗? - SwiftMango
2
不是所有的实现中,char 都是带符号的,而且 CHAR_MAX 为 255(CHAR_MIN 为 0)。在其他一些实现中,char 是有符号的,所以 CHAR_MIN 为 -128,CHAR_MAX 为 127(例如,在 Linux/PowerPC/32 位和 Linux/x86/32 位上情况可能会有所不同)。 - Basile Starynkevitch
2
@texasbruce 这取决于编译器。在许多编译器上,默认值为“signed”。如果您需要一个“unsigned”,则应始终明确指定它。 - Zac Howland

0

虽然在代码中频繁使用强制类型转换可能会让人感到乏味,但对我来说,尽可能使用强类型是非常好的做法。

如上所述,当您指定类型“char”时,您正在邀请编译器选择编译器作者首选的内容(有符号或无符号)。我不是UTF-8的专家,但如果您不需要,就没有理由使您的代码不可移植。

至于您的常量,我曾经使用过默认将这种方式编写的常量转换为有符号整数的编译器,以及考虑上下文并相应解释它们的编译器。请注意,有符号和无符号之间的转换可能会溢出。对于相同数量的位,负数会溢出无符号数(显然),而带有设置了最高位的无符号数会溢出有符号数,因为最高位表示负数。

在这种情况下,您的编译器将您的常量视为无符号8位或更大,这意味着它们不适合作为有符号8位。我们都很感激编译器发出警告(至少我是)。

我的观点是,强制类型转换以显示您打算发生的事情是完全没有问题的。如果编译器允许您在有符号和无符号之间进行分配,那么无论变量还是常量都应该要求您进行强制类型转换。例如:

const int8_t a = (int8_t) 0xFF; // 将会是 -1

虽然在我的例子中,最好赋值为 -1。当你需要添加额外的转换时,它们要么有意义,要么你应该编写适合所分配类型的常量。


虽然更强的类型检查可能有助于捕获错误,但对于必须处理旧代码的项目而言,它会造成很多困扰。从跨越“0x00-0xFF”的十六进制常量初始化“char”数组是非常常见的,例如:X位图(XBM)文件格式(实际上就是一个包含此类初始化的C源代码片段),以及许多期望“char”数组而不是“unsigned char”数组的X库函数,如渐变、颜色映射等。 - ack

0

有没有一种方法可以混合这些内容?我想要一个宏定义 FX_RGB(R,G,B),将其变为一个常量字符串“\x01\xRR\xGG\xBB”,以便我可以执行以下操作: const char* LED_text = "Hello " FX_RGB(0xff, 0xff, 0x80) "World"; 并获得一个字符串:const char* LED_text = "Hello \x01\xff\xff\x80World";


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