gcc 似乎对复杂的常量折叠有一些限制。这是一个例子:
static inline unsigned int DJBHash(const char *str)
{
int i;
unsigned int hash = 5381;
for(i = 0; i < strlen(str); i++)
{
hash = ((hash << 5) + hash) + str[i];
}
return hash;
}
int f(void)
{
return DJBHash("01234567890123456");
}
当以 -O3 优化级别 (gcc 4.8) 运行时,它会很好地展开 DJBHash 中的循环并在编译期间计算该字符串的哈希值。
但是,当使字符串长一个字符时 return DJBHash("012345678901234567");
它不再折叠它并生成一个带有条件跳转指令的循环。
我想将任意长度的文字字符串折叠到它的哈希值中作为编译时间常量。
这能做到吗?
澄清
我的问题是关于 gcc 的常量折叠优化(见标题 - 请不要删除 gcc 和 compiler 标签)< br/> 这里的许多答案都试图用模板或 constexpr 来解决问题。很高兴了解这些选项,感谢您为了所有人的利益发布它们。但是,他们没有直接回答我的问题。
实际上,我正在开发 gcc 端口,因此如果需要,我可以更改和构建 gcc 源代码。但我仅限于 C,我想在这个范围内解决这个问题。
最佳答案
这是一个使用 constexpr
的版本。它在一个方面与其他方法略有不同——可以说,由于是递归的,因此最容易将字符串前后散列。例如,它为“abc”提供的值将是您通常期望从“cba”获得的值。我认为这不会对使用产生任何真正的影响,只要您始终如一地使用一个或另一个(但考虑到散列的变幻莫测,我对此可能是错误的)。
虽然它确实在编译时进行评估——例如,我们可以将结果用作 switch
语句中的标签:
#include <iostream>
unsigned constexpr const_hash(char const *input) {
return *input ?
static_cast<unsigned>(*input) + 33 * const_hash(input + 1) :
5381;
}
int main(int argc, char **argv) {
switch (const_hash(argv[1])) {
case const_hash("one"): std::cout << "one"; break;
case const_hash("two"): std::cout << "two"; break;
}
}
显然,可能会发生冲突,因此您通常不想将它用作 case 语句标签——我这样做主要是为了强制出现一种情况,如果结果不是编译结果,它将无法编译-时间常数。
编辑:如果您关心哈希算法是否“正确”,我想这更准确(感谢@Abyx):
unsigned constexpr const_hash(char const *input, unsigned hash = 5381) {
return *input ?
const_hash(input + 1, hash * 33 + static_cast<unsigned>(*input)):
hash;
}
关于c - gcc 复杂常量折叠,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19120326/