我有这些小函数,它们接受一个小的char数组,并将其重新转换为int或longlong:
#define HASH4(x) (*((int*)x))
#define HASH8(x) (*((longlong*)x))
int aValue=HASH4("FOOD");
longlong aBigValue=HASH8("SEXYCOOL");
我想知道在C/C++语法中是否有什么方法可以让我不需要#define就能完成这个任务?原因是,我想在case结构中使用这些,就像这样:
switch(something)
{
case HASH4("FOOD"): printf("Food!");break;
case HASH8("SEXYCOOL"): printf("Sexycool!");break;
}
......它不会让我这么做的。
那么,在c语法中有没有什么方法可以告诉它把这个4字节的char* 解释为int型的呢?或者,有没有什么方法可以写一个define来转换它呢?
澄清一下:我想知道在C/C++语法中是否有一种方法可以接受四个字节,这样这两个语句就等价了:
int something=Magic("\0\0\0A");
int something=65;
switch(stuff)
{
case Magic("FOOD"): <-- becomes valid
}
...因为我们都知道const char“FOOD”只有四个字节,包含70,79,79,68...有没有什么方法可以 Package 一个漂亮的MAKEINT(4个字节),它完全由预处理器处理,或者与int或long long没有区别?
3条答案
按热度按时间watbbzwu1#
更新:@chtz的破解完全奏效了,它欺骗了编译器,让编译器没有意识到它正在从一个char数组构建一个int。
溶液1/4:使用宏通过手动计算4个字节来组合
uint32_t
更新2:考虑字节序。x86-64系统是little-endian。我最初错误地使用了big-endian散列:
测试cpp:
运行命令:
输出:
使用
#define
时 * 输出 *:这样做很好,因为((('F'*256+'O')*256+'O')*256+'D')
是一个常量表达式!--它在编译时被完全计算为常量值。溶液2/4(更好):使用常量表达式函数hack代替上面的宏hack
@Pepijn克雷默说得对,
constexpr
函数可以用来代替那些只做编译前计算的宏。换句话说,constexpr
函数可以代替 * 一些 * 宏。constexpr
函数可能是首选,因为它们具有类型安全和检查功能,并且避免了宏在传递表达式或赋值语句时的双重求值问题。constexpr
函数将在编译时 *(如果可能)计算为constexpr
结果,否则在运行时 * 计算为常规结果。因此,它们就像是 * 一些 * 宏+常规函数的功能混合体。这里有一个解决方案,将4个字符的
std::array
传入constexpr
函数:运行并输出,显示
FOOD
中的4个字节变成了1146048326
的uint32_t
数字,而在我的x86-64 Linux系统上,这个数字又变成了4个字符FOOD
(小字节序):溶液3/4:(目前为止最好的)
constexpr
函数黑客使用std::string_view
作为输入,而不是上面的std::array
更好的是,使用
std::string_view
作为输入参数,这样您仍然可以将原始的C字符串传递给它。运行并输出(与之前完全相同):
溶液4/4:不要将4字节转换为整数;我只需要使用内置的C++散列函数直接散列字符串,作为字符串视图
基于您在问题中调用宏
HASH4()
和HASH8()
的事实,您似乎真的只需要输入字符串的唯一或接近唯一的哈希值?即:你实际上不需要转换它的等价空间整数表示相反,你只需要它的一部分。在这种情况下,你也可以使用C++内置的
std::hash<>{}()
函数。std::string_view
专门化的文档operator""sv()
函数的含义,用作"my_string"sv
,以从上述示例中的C字符串"my_string"
生成std::string_view
std::hash<>{}()
不是一个constexpr
函数,所以你也不能在switch的情况下使用它!相反,你必须使用if
else
风格的检查。如何阅读
std::hash<>{}()
:std
是名称空间<>
指定模板类型{}
构造此类类型的默认对象()
调用operator()
(类似于括号函数的[或“函子”]运算符;参见here和here),在本例中,该对象是对那些括号内的参数执行散列的函数。注:下面的代码工作得很好,可能是许多C人最喜欢的,但我发现它相当复杂,也许太“C"了。你的电话。它也不是一个
constexpr
表达式。我很高兴我终于达到了一个点,经过3年的日常C使用,我甚至可以阅读和编写这个自己,然而,并且能够访问C字符串的快速散列(解释为std::string_view
s)实际上是 C 的一部分。运行和输出:
std::hash
函子非常难看,所以如果愿意,可以用一个宏来 Package 它:示例:
输出与上面的相同。
更进一步
如果你想了解更多关于内存blob和字节数组之间的转换,也可以参考我在这里的其他回答:
1.如何在C中将
struct
变量转换为uint8_t
数组:需要考虑和理解的其他信息
要使4字节被解释为常量4字节int(
const int32_t
),只需使用即:在指针转换之前添加
const
。但是,这会得到一个
const int32_t
,它与constexpr int32_t
常量表达式int32_t * 不 * 相同。一个 * 常量表达式 * 告诉编译器,这段内存不会被忽视、编辑或重新解释-转换为另一种类型。事实上,通过宏将4个字节重新解释-转换为一个int已经违反了这一点。所以,不,在C++中,我不知道有什么预处理器宏方法可以强制将4个字节解释为
constexpr int
。你可以将4个字节重新解释为
const int
,但这不是一回事,只有constexpr
类型可以在switch语句中用作case,所以@dbush的答案是正确的,使用if
else
来检查const int
的值。注意:如果你声明了一个
const int
,编译器可能会看到它也可能是一个constexpr int
,并为你做出决定。......还有这个:
xtupzzrd2#
使用constexpr函数代替宏。例如
yshpjwxd3#
case
标签的值必须是常量表达式,而像(*((int*)x))
这样的表达式是不符合条件的。使用#define
的事实并不重要。为此,您需要使用
if
...else
链条。