【问题标题】:How can one store a uint64_t in an array?如何将 uint64_t 存储在数组中?
【发布时间】:2020-11-30 07:19:01
【问题描述】:

我正在尝试将 crc64 校验和的 uint64_t 表示形式存储为数组。

校验和总是像uint64_t res = 0x72e3daa0aa188782,所以我希望将它存储为一个数组char digest[8],其中digest[0] 是72,digest[1] 是e3...digest[7] 是82。

我尝试循环/除法来分解数字,但如果它是一个较小的整数,那会更合适,如果起点是 Base-10,因为起点是 Base-16,输出应该但是如上所述。


更新:我删除了无意义的代码,并希望我能接受所有三个答案,因为他们都按照我的要求做了。位移是我希望得到的答案,所以这就是它被接受的原因。

【问题讨论】:

  • 这个问题很混乱。看起来您正在尝试将 64 位值转换为十进制,但您正在使用浮点运算来执行此操作。你很可能有错误。此外,您有一个包含 19 位数字的数组,但您正在迭代其中的 20 多个数字。如果我从您的输出中删除最后一位数字(这是未定义的行为)并转换为十六进制,我会得到 72E3DAA0AA188CEC,这很接近但在最后一位数字中显然不正确。这是意料之中的,因为使用double 计算会丢失精度。
  • 建议 1:不要对循环长度进行硬编码。如果你的长度是digit,那么你应该做for (i = 0; i < digit; ++i)
  • 建议 2:仅使用整数运算将您的值转换为十进制,或者简单地使用sprintf 或类似的方法将其转换为字符串。有很多成熟的方法可以做到这一点,只需搜索一个 Stack Overflow 即可。
  • 建议 3:为您的数字使用一个固定的缓冲区大小,在您尝试转换的任何基数中占可能最长的 uint64_t 值。在转换数字时数数。通常你不需要通过计算日志等来聪明,但如果你需要这样做,它也必须用整数函数来完成。否则你会遇到类似的问题(很可能是少数极端情况),其中精度问题把你搞砸了。而且您甚至可能不知道您正在获得未定义的行为。这可能会导致难以调试的灾难性错误。
  • 我并没有尝试在任何基础上进行转换,实际上,它只是这样做了,因为它是不受欢迎的行为。我正在尝试将 crc64 校验和(作为 uint64_t 返回)存储到数组中。校验和总是和上面的例子一样,所以我想将 uint64_t res = x72e3daa0aa188782 存储为一个数组 char digest[8],其中 digest[0] 为 72,digest[1] 为 e3..digest[7]是 82..

标签: arrays c data-conversion unsigned-long-long-int bit-representation


【解决方案1】:

移位和按位 AND 也可以满足您的需求。比如

unsigned char digest[8];
int shift = 56;
for (int i = 0; i < 8; ++i)
{
    digest[i] = (res >> shift) & 0xff;
    shift -= 8;
}

如果可以更改res 的值,另一种方法是:

for (int i = 7; i >= 0; --i)
{
    digest[i] = res & 0xff;
    res >>= 8;
}

【讨论】:

  • 你能进一步解释第二种方法吗?位移是我预期会得到的方法,但无法弄清楚,但我正在做一个更基本的方法,但它可能更适合六位整数,但尽管如此,我喜欢按位与移位。
  • @jon 第二种方法首先保存 res 的 8 个最低有效位。当它向左移动res 8 位时。因此,如果您以0x72e3daa0aa188782 开头,它将保存0x82,然后在轮班之后您将拥有0x72e3daa0aa1887。所以在下一个循环中,它将保存0x87,然后在轮班之后你有0x72e3daa0aa18。所以在下一个循环中,它将保存0x18,然后在轮班之后你有0x72e3daa0aa。以此类推。
【解决方案2】:

您应该使用除以 256,而不是除以 10:

unsigned char digest[sizeof(uint64_t) / sizeof(char)];

for (int i = sizeof(digest) - 1; i >= 0; i--) {
   digest[i] = res % 256; // get the last byte
   res /= 256;            // get the remainder
}

// for demo purposes
for (int i = 0; i < sizeof(digest); i++) {
   printf("%x ", digest[i]); 
}

// 72 e3 da a0 aa 18 87 82 

【讨论】:

  • 是的,我认为 base-10 是一个起点,这让我回到了我做这件事的 CS 时代(实际上这是我的第一个作业,将一个三位数的整数分成各自的组成部分) ..但我离题了,请问为什么当摘要总是8时,数组大小的划分大小?其余的除法/模数是我至少试图做的……我猜AM编码中的2就是这样做的……
  • sizeof(char) 总是 1,所以 sizeof(uint64_t) / sizeof(char) => sizeof(uint64_t)
【解决方案3】:

显然,如果您除以 10,您将得到十进制数字,而不是您预期的数字。在这种情况下,您只想获取uint64_t 的底层位,这可以通过简单的memcpy 来完成。但是您希望字节以大端顺序排列,因此您还需要先转换为大端序

uint64_t number = 0x72e3daa0aa188782ull;

#ifdef __unix__
uint64_t number_be = htonll(number);
#else
uint64_t number_be = htobe64(number);
#endif

char digest[8];
memcpy(&digest, &number_be , sizeof number);

在 C 中也可以通过联合来完成

union Digest
{
    uint64_t res;
    char bytes[8];
} digest;
digest.res = htonll(0x72e3daa0aa188782ull); // or htobe64
// now just use digest.bytes[]

这些只需要几条机器指令,因此比为了得到 8 个字节而循环 8 次要快得多

【讨论】:

    【解决方案4】:

    我想你可以在这里使用union

    union foo {
        uint64_t u64;
        uint8_t u8[8];
    };
    

    这使您不必进行任何转换。您可以使用foo.u64 访问64 位值或使用foo.u8[0]foo.u8[7] 访问8 位值。

    【讨论】:

    • 由于字节序问题,这不会将字节放在正确的位置。
    • 什么字节序问题?最可能的情况是将8-i 应用于数组索引,这很简单。
    • 我可以反转它,其中 u8[0] 是最后一个值,u8[7] 是第一个值..
    • 当然,你可以这样做,但是如果你在不理解为什么的情况下这样做,那么你做错的可能性就很大原因,你可能有错误的解决方案。
    • 它只会在小端机器上被反转。如果代码是为一些大端目标编译的,那么字节当然已经处于预期的顺序
    【解决方案5】:

    试试这个:

    #define INIT_LIST           \
    201234567890123456,       \
    12345678901234567890,     \
    98765432109876543,        \
    65432109887,              \
    12345234512345,           \
    217631276371261627,       \
    12354123512453124,        \
    2163521442531,            \
    2341232142132321,         \
    1233432112
    
    #define STR_(...) #__VA_ARGS__
    #define STR(x) STR_(x)
    
    int main (void)
    {
      uint64_t numbers[10] = { INIT_LIST };
      char array[] = STR(INIT_LIST);
      puts(array);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-18
      • 1970-01-01
      • 1970-01-01
      • 2015-07-11
      • 1970-01-01
      相关资源
      最近更新 更多