避免散列函数中的严格别名冲突
Avoiding strict aliasing violation in hash function
如何避免违反严格的别名规则,尝试修改 char*
sha256 函数的结果。
计算哈希值:
std::string sha = sha256("some text");
const char* sha_result = sha.c_str();
unsigned long* mod_args = reinterpret_cast<unsigned long*>(sha_result);
比获得 2 块 64 位:
unsigned long a = mod_args[1] ^ mod_args[3] ^ mod_args[5] ^ mod_args[7];
unsigned long b = mod_args[0] ^ mod_args[2] ^ mod_args[4] ^ mod_args[6];
比通过连接那两部分得到结果:
unsigned long long result = (((unsigned long long)a) << 32) | b;
虽然听起来令人沮丧,但 只有 真正便携、standard-conforming 且有效的方法是 memcpy()
。使用 reinterpret_cast
违反了严格的别名规则,使用 union
(通常建议)会在您从未写入的成员处读取时触发未定义的行为。
然而,由于大多数编译器会优化掉 memcpy()
调用,这并不像听起来那么令人沮丧。
例如,以下带有两个 memcpy()
的代码:
char* foo() {
char* sha = sha256("some text");
unsigned int mod_args[8];
memcpy(mod_args, sha, sizeof(mod_args));
mod_args[5] = 0;
memcpy(sha, mod_args, sizeof(mod_args));
return sha;
}
生成以下优化程序集:
foo(): # @foo()
pushq %rax
movl $.L.str, %edi
callq sha256(char const*)
movl [=11=], 20(%rax)
popq %rdx
retq
很容易看出,没有memcpy()
- 修改值'in place'.
如何避免违反严格的别名规则,尝试修改 char*
sha256 函数的结果。
计算哈希值:
std::string sha = sha256("some text");
const char* sha_result = sha.c_str();
unsigned long* mod_args = reinterpret_cast<unsigned long*>(sha_result);
比获得 2 块 64 位:
unsigned long a = mod_args[1] ^ mod_args[3] ^ mod_args[5] ^ mod_args[7];
unsigned long b = mod_args[0] ^ mod_args[2] ^ mod_args[4] ^ mod_args[6];
比通过连接那两部分得到结果:
unsigned long long result = (((unsigned long long)a) << 32) | b;
虽然听起来令人沮丧,但 只有 真正便携、standard-conforming 且有效的方法是 memcpy()
。使用 reinterpret_cast
违反了严格的别名规则,使用 union
(通常建议)会在您从未写入的成员处读取时触发未定义的行为。
然而,由于大多数编译器会优化掉 memcpy()
调用,这并不像听起来那么令人沮丧。
例如,以下带有两个 memcpy()
的代码:
char* foo() {
char* sha = sha256("some text");
unsigned int mod_args[8];
memcpy(mod_args, sha, sizeof(mod_args));
mod_args[5] = 0;
memcpy(sha, mod_args, sizeof(mod_args));
return sha;
}
生成以下优化程序集:
foo(): # @foo()
pushq %rax
movl $.L.str, %edi
callq sha256(char const*)
movl [=11=], 20(%rax)
popq %rdx
retq
很容易看出,没有memcpy()
- 修改值'in place'.