如何将长无符号转换为无符号字符*?

3

我正在尝试对一个unsigned long值进行哈希,但是哈希函数需要一个unsigned char *,如下面的实现所示:

unsigned long djb2(unsigned char *key, int n)
{
    unsigned long hash = 5381;
    int i = 0;
    while (i < n-8) {
        hash = hash * 33 + key[i++];
        hash = hash * 33 + key[i++];
        hash = hash * 33 + key[i++];
        hash = hash * 33 + key[i++];
        hash = hash * 33 + key[i++];
        hash = hash * 33 + key[i++];
        hash = hash * 33 + key[i++];
        hash = hash * 33 + key[i++];
    }
    while (i < n)
        hash = hash * 33 + key[i++];
    return hash;
}

有没有办法可以实现我的目标,也许可以使用两者之间的转换吗?

3
@Pubby,reinterpret_cast是C++中的语法,这个问题是关于C语言的。 - Drew Chapin
可能只是我,但我认为这个问题应该有更多的上下文(即更多的代码)。 - tay10r
@druciferre 我评论时它没有被标记 ;) - Pubby
@Pubby,抱歉这个问题没有显示“已编辑”的通知(很奇怪)。 - Drew Chapin
2
从函数的角度来看,你最好希望你的 long 类型是 8 字节长。否则请使用 long long。否则你可能会遇到分段错误。 - tay10r
显示剩余5条评论
6个回答

13
unsigned long x;

unsigned char * p = (unsigned char*)&x;

确保通过使用 p (或在您的系统上的 unsigned long 长度)使用全部 4 个字节。


1
你说的“通过p使用所有4个字节”是什么意思?抱歉,我是C语言新手。 - James
3
还有,不要忘记字节序问题!如果您没有适当地交换字节序,则在不同的计算机上对“相同”的数据进行哈希可能会得到不同的结果。 - tangrs
3
p是一个指针。它指向你的无符号长整型变量'x'的第一个字节。如果你有一个接受无符号字符指针和长度的函数,那么长度应该是sizeof(unsigned long)或者sizeof(x)。 - user1764961
2
@MattPhillips 假设哈希函数以空字符结尾的字符串作为输入,这是在假设条件下。通常,哈希函数采用长度参数和缓冲区指针而非以空字符结尾的字符串作为输入。 - tangrs
1
@MattPhillips 我不知道是否需要空终止符,因为我们还没有看到这个函数。char*并不总是表示字符串。 - tay10r
显示剩余8条评论

1
技术上,您可以通过以下方式实现它:

unsigned long value = 58281;
djb2((unsigned char *) &value, sizeof(value));

不过要注意一些常见的陷阱:

  • 该哈希函数最初是为字符串设计的(因此有原型),因此请确保它适合您的需要(冲突数,雪崩效应等)
  • 如果您在某个时刻想要为非常大的对象进行哈希,其 sizeof(object) >(int) sizeof(object) (如果适用于您的架构),请注意可能会导致越界访问(未定义行为)或只对您对象的一部分进行哈希。

我认为使用sizeof x而不是sizeof(x)更加罕见,就像使用return x而不是return(x)一样罕见 :) - SomeWittyUsername
我觉得这真是让人耳目一新 :) - SomeWittyUsername

1

正如其他人所说,您可以轻松地将int或任何其他对象读取为char数组:

unsigned char value = 0xde;
unsigned short value = 0xdead;
unsigned long value = 0xdeadbeef;
double value = 1./3;

djb2((unsigned char*)&value, sizeof value);

但请注意,存储在short或long中的0xdead将不具有相同的哈希值。
另外,请注意,您的哈希函数可以更好地展开,使用Duff's device
unsigned long djb2(unsigned char *k, int size)
{
    unsigned long h = 5381;
    int i = 0;
    switch(size % 8) {
      case 0: while(i < size) { 
                  h = h*33 + k[i++];
      case 7:     h = h*33 + k[i++];
      case 6:     h = h*33 + k[i++];
      case 5:     h = h*33 + k[i++];
      case 4:     h = h*33 + k[i++];
      case 3:     h = h*33 + k[i++];
      case 2:     h = h*33 + k[i++];
      case 1:     h = h*33 + k[i++];
              }
    }
    return h;
}

0

这展示了一个转换的工作方式。请注意,在此情况下,“ABC”字符串将以空字符结尾,但在实际应用中可能需要更多的注意。

#include <stdio.h>

int main() {
    unsigned long x=0x414243;  #0x414243 is ABC
    unsigned char *s=(unsigned char *)&x;
    printf("%s", s);
}

在你的例子中,在大端系统上它将会是ABC。编辑:实际上,在大端系统上,它只是一个空字符串。 - tangrs
是的,在典型的英特尔Linux系统上,这里使用的是"CBA" :) - Vorsprung

0

既然你现在已经发布了你的代码,那么你应该使用类似于这样的东西:

#include <stdio.h>


int main() {
    unsigned long result, x = 0xdeadbeef;
    x = convert_endian(x);

    result = djb2((unsigned char*)&x, sizeof(x));
    do_something(result);
    return 0;
}

@TaylorFlores 仔细看,如果 size < 8,它不会循环。 - SomeWittyUsername
@TaylorFlores 怎么做?它将循环8的倍数,直到 n-8,然后将以单步循环剩余部分。 - SomeWittyUsername
1
嗯,是的,你说得对。我想我应该更经常地测试我的建议。不过我仍然认为第一个while循环是不必要的。 - tay10r
@TaylorFlores 这可能是为了性能优化(一种循环展开) - SomeWittyUsername
你本可以同意我的看法并恢复我的尊严 :P 不过很有趣,我从未听说过循环展开。 - tay10r
显示剩余3条评论

-2

你应该使用ultoa_s进行转换。


他从未说过字符串。问题是关于数据类型转换的。 - tay10r
你不能将ulong转换为以null结尾的字符字符串并使用指针吗? - dizzer
@dizzer:你觉得0x10000010作为一个以0结尾的字符串会是什么样子? - glglgl
@dizzer,您假设char *是一个字符串,但它可能不是。您还向用户展示了将ulong转换为数字的字符串表示形式的函数-这不是强制转换。 - tay10r
@TaylorFlores 你说得对,char* 并不意味着需要字符串。 - dizzer
由于问题提出者添加的代码片段,我之前给出的答案现在已经不合适了。 - dizzer

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