在 C++11 中声明 char 十六进制常量

Posted

技术标签:

【中文标题】在 C++11 中声明 char 十六进制常量【英文标题】:Declaring char hexadecimal constants in C++11 【发布时间】:2013-03-19 17:23:24 【问题描述】:

在我们代码的各种低级部分中,我们需要向设备发送特定字节以使事情发生。因此,我们有很多看起来像这样的代码:

const char magic_bytes[] =  0x01, 0xFA, 0x92 ;

这会导致错误(在 GCC 4.7.2 上)

test_char.cpp:6:51: warning: narrowing conversion of ‘250’ from ‘int’ to ‘const char’ inside   is ill-formed in C++11 [-Wnarrowing]

因为 0xFA 不在 -128 到 127 的范围内。

我能想到两种解决方法:

const char magic_bytes[] =  static_cast<char>(0x01), static_cast<char>(0xFA), static_cast<char>(0x92) ;

或:

const unsigned char magic_bytes[] =  0x01, 0xFA, 0x92 ;

两者要么丑陋(第一种情况),要么有其他缺点(在后一种情况下必须强制转换为 (const char*))

有没有更好的方法来声明这些字符串?

【问题讨论】:

既然您在技术上与unsigned chars 合作,为什么需要转换为char?除了用宏隐藏转换之外,没有更简洁的解决方案。 @Dave:遗憾的是,C 和 C++ 中“字节数组”的通用符号是 char *,而不是 unsigned char *。所以这就是为什么你通常需要演员表。是的,它确实应该是void *,但许多 API 使用 char*s。 【参考方案1】:

C++11 为您提供variadic templates(对GCC 的支持已经存在了一段时间)来解决这个问题。

template <typename... A>                                                                 
constexpr std::array<char, sizeof...(A)> byte_array(A... v)                              
 return std::array<char, sizeof...(A)>static_cast<char>(v)...;                     

constexpr auto arr = byte_array( 0x01, 0xFA, 0x92 );

或者避免重复调用 .data() 将其传递给 C 函数:

template <std::size_t S>
struct byte_array 
  char data_[S];
  char *data()  return data_; 
  operator char*()  return data_; 

  const char *data() const  return data_; 
  operator const char*() const  return data_; 

  constexpr std::size_t size() const  return S; 

  // one could add support for begin/end and things like that
;

template <typename... A>
constexpr byte_array<sizeof...(A)> make_byte_array(A... v)
 return byte_array<sizeof...(A)>static_cast<char>(v)...; 

// beside constexpr, this can be also non-const
auto magic_bytes = make_byte_array( 0x01, 0xFA, 0x92 );
strtok(magic_bytes, "why?");

与普通字符数组相比没有开销。

【讨论】:

【参考方案2】:

你可以做这样的事情来获得一个单一的演员:

const unsigned char magic_bytesUC[] =  0x01, 0xFA, 0x92 ;
enum  NBYTES = sizeof(magic_bytesUC) ;
const char *magic_bytes = reinterpret_cast<const char*>(magic_bytesUC);

【讨论】:

【参考方案3】:

由于您指定了 C++11,我假设您可以使用 variadic macros。在这种情况下,有一个解决方案在使用时很优雅,但在幕后却很丑陋。

所以我将首先向您展示如何使用它:

char myBytes1[] = MAKE_BYTES( 0x00, 0x40, 0x80, 0xC0 );
char myBytes2[] = MAKE_BYTES( 0xFF );

现在,后端代码:这可能会更短,但我使用了标准循环方法,因此您可以从中获得一些重用。它可以支持 1 - 24 字节的列表。通过重复最后几行,它可以变得更大。欢迎来到预处理器元编程的世界。

#define EXPAND(a) a
#define ARGS_COUNT_(\
  _96,_95,_94,_93,_92,_91,_90,_89,_88,_87,_86,_85,_84,_83,_82,_81,\
  _80,_79,_78,_77,_76,_75,_74,_73,_72,_71,_70,_69,_68,_67,_66,_65,\
  _64,_63,_62,_61,_60,_59,_58,_57,_56,_55,_54,_53,_52,_51,_50,_49,\
  _48,_47,_46,_45,_44,_43,_42,_41,_40,_39,_38,_37,_36,_35,_34,_33,\
  _32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21,_20,_19,_18,_17,\
  _16,_15,_14,_13,_12,_11,_10, _9, _8, _7, _6, _5, _4, _3, _2, _1,\
  N,...) N
#define ARGS_COUNT(...) ARGS_COUNT_(__VA_ARGS__,\
  96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,\
  80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,\
  64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,\
  48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,\
  32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,\
  16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define ARGS_HEAD(a,...) (a)
#define ARGS_TAIL(a,...) (__VA_ARGS__)

#define FOREACH(macro,lmacro,list) FOREACH_(ARGS_COUNT list,macro,lmacro,list)
#define FOREACH_(n,macro,lmacro,list) FOREACH__(n,macro,lmacro,list)
#define FOREACH__(n,macro,lmacro,list) FOREACH_##n(macro,lmacro,list)
#define FOREACH_1(macro,lmacro,list) lmacro list
#define FOREACH_2(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_1(macro,lmacro,ARGS_TAIL list)
#define FOREACH_3(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_2(macro,lmacro,ARGS_TAIL list)
#define FOREACH_4(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_3(macro,lmacro,ARGS_TAIL list)
#define FOREACH_5(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_4(macro,lmacro,ARGS_TAIL list)
#define FOREACH_6(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_5(macro,lmacro,ARGS_TAIL list)
#define FOREACH_7(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_6(macro,lmacro,ARGS_TAIL list)
#define FOREACH_8(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_7(macro,lmacro,ARGS_TAIL list)
#define FOREACH_9(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_8(macro,lmacro,ARGS_TAIL list)
#define FOREACH_10(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_9(macro,lmacro,ARGS_TAIL list)
#define FOREACH_11(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_10(macro,lmacro,ARGS_TAIL list)
#define FOREACH_12(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_11(macro,lmacro,ARGS_TAIL list)
#define FOREACH_13(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_12(macro,lmacro,ARGS_TAIL list)
#define FOREACH_14(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_13(macro,lmacro,ARGS_TAIL list)
#define FOREACH_15(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_14(macro,lmacro,ARGS_TAIL list)
#define FOREACH_16(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_15(macro,lmacro,ARGS_TAIL list)
#define FOREACH_17(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_16(macro,lmacro,ARGS_TAIL list)
#define FOREACH_18(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_17(macro,lmacro,ARGS_TAIL list)
#define FOREACH_19(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_18(macro,lmacro,ARGS_TAIL list)
#define FOREACH_20(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_19(macro,lmacro,ARGS_TAIL list)
#define FOREACH_21(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_20(macro,lmacro,ARGS_TAIL list)
#define FOREACH_22(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_21(macro,lmacro,ARGS_TAIL list)
#define FOREACH_23(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_22(macro,lmacro,ARGS_TAIL list)
#define FOREACH_24(macro,lmacro,list) EXPAND(macro ARGS_HEAD list) FOREACH_23(macro,lmacro,ARGS_TAIL list)

#define MAKE_BYTE(x) static_cast<char>(x),
#define MAKE_LAST_BYTE(x) static_cast<char>(x)
//#define MAKE_BYTES(x)  FOREACH(MAKE_BYTE,MAKE_LAST_BYTE,x)  // uncomment this if you would rather use double bracket ((0x00,0x40,...)) syntax
#define MAKE_BYTES(...)  FOREACH(MAKE_BYTE,MAKE_LAST_BYTE,(__VA_ARGS__)) 

【讨论】:

可变参数宏是否向后兼容 C++03?或者,我们可以接受的是,它们是在早期版本(例如 GCC 4.2)中作为 GCC 扩展引入的吗? @Damien:是的(一个 GCC 扩展),尽管如果你回溯到足够远的地方,命名参数的语法会更改为 nameOfArgs...。在我的项目中,我有检查以交换可变参数宏(并且我使用最后几行中的注释形式将所有更改保留在公共位中),但这显然意味着更多的代码。问题是直到 C99 和 C++11 才标准化。我不知道符合标准的表格在 GCC 中已经存在了多久,但我很确定 4.2 有它。可能比这更早的版本。

以上是关于在 C++11 中声明 char 十六进制常量的主要内容,如果未能解决你的问题,请参考以下文章

C和C指针小记(十六)-动态内存分配

C中的无符号十六进制常量?

C中的十六进制浮点常量

java中如何声明一个十六进制的字符串,或者说,怎么发送一个十六进制数据?

C中的无符号十六进制常量?

在 C 中将 ascii char[] 转换为十六进制 char[]