【问题标题】:IPV6 address to array of uint64_t is not working as expecteduint64_t 数组的 IPV6 地址未按预期工作
【发布时间】:2017-09-25 15:34:35
【问题描述】:

我正在尝试将字符串格式的 IPv6 地址转换为 uint64_t 数据数组。为此我写了下面的程序

typedef struct
    {
        union
        {struct in6_addr  sa;
        uint64_t addr[2];
        }u;
    } ipv6_addr_t;

    ipv6_addr_t  rnc;
    char rncs[100] = "2000::200a";
    inet_pton(AF_INET6, rncs, &(rnc.u.sa));
    printf("%u\", rnc.u.addr[0]);

预期输出是地址的第一个 64 位,即 2^61 = 2305843009213693952。

但是当我执行程序时,我得到的输出为 32 这是 地址的第一个字节。

我不明白背后的原因,请帮忙。谢谢!

【问题讨论】:

  • A struct in6_addr 在结构的开头具有控制信息,位于保存地址的实际数据字节之前。看看它的大小;它不会是 16 个字节。
  • @JonathanLeffler,我认为您正在考虑struct sockaddr_in6。根据the manualstruct in6_addr 只是unsigned char 数组的包装器。
  • %u 可能是打印uint64_t 的错误 printf 格式说明符。使用#include <inttypes.h> 定义的PRIu64 宏来获取正确的格式说明符,例如printf("%" PRIu64 " %" PRIu64 "\n", rnc.u.addr[0], rnc.u.addr[1]);。如果您没有#include <inttypes.h>,则可以使用printf("%llu %llu\n", (unsigned long long)rnc.u.addr[0], (unsigned long long)rnc.u.addr[1]);
  • @JohnBollinger:用手机接听电话是有原因的。我当然想到了sockaddr_in6 结构。我没有检查过 POSIX 对这个话题有什么要说的。
  • 即使使用PRIu64输出仍然是32

标签: c networking ipv6


【解决方案1】:

这里有几个问题。

  • 您打印结果的方法本身就有缺陷。 printf 指令%u 通常不用于打印无符号整数,而是专门用于打印unsigned int 类型的值。尽管您的int64_t 可能与unsigned int 类型相同,但这是非典型的。如果它们不是同一类型,则指令和实际参数之间的不匹配会导致未定义的行为。 @AndrewHenle 在他的回答中解释了如何通过printf 打印int64_t

  • 1234563您完全可以呈现特定的预期结果表明您正在假设一种特定的表示(并且显然与您的系统不匹配)。
  • 您似乎认为打印的字节数少于应有的字节数,但即使您根据 Andrew 的回答更正了 printf 格式,我倾向于认为输出是相同的。您观察到打印的值对应于地址的第一个字节,但请考虑接下来的几个字节是什么:全为零,直到到达最后两个字节。现在考虑一个位模式,它由一个值为 32(十进制)的字节组成,后跟七个值为 0 的字节。如果您将该模式解释为 64 位、无符号、小端整数,则其值为 32。这是更正后的代码最有可能在基于 Intel 的机器上打印的结果。

显然您希望生成其 逻辑 位模式与地址位匹配的int64_t 值。相反,您生成了 physical 位模式与地址位匹配的值。当您使用 union 而不是算术在字节数组和整数之间进行转换时,这是可以预料的。我建议改用循环:

struct in6_addr sa;
int64_t addr_ints[2] = { 0, 0 };
char rncs[100] = "2000::200a";
inet_pton(AF_INET6, rncs, &sa);

for (int i = 0; i < 16; i++) {
    addr_ints[i / 8] = addr_ints[i / 8] << 8 + sa.s6_addr[i];
}
printf( "%" PRIu64 "\n", addr_ints[ 0 ] );

这也可以避免struct in6_addr 的布局与您预期的不同时出现问题,只要布局符合 POSIX。

【讨论】:

  • 是的,我在英特尔机器上尝试过,现在我在大端的 MIPS 架构机器上尝试了同样的方法,输出为 2305843009213693952。
【解决方案2】:

除了 cmets 中指出的任何问题外,使用错误的 printf() 格式说明符是未定义的行为。

根据 7.21.6 格式化输入/输出函数the C standard 的第 9 段:

如果转换规范无效,则行为是 不明确的。如果任何参数不是正确的类型 相应的转换规范,行为是 未定义。

鉴于此

rnc.u.addr[0]

uint64_tprintf() 格式说明符%u in

printf("%u\n", rnc.u.addr[0]);

不正确。

uint64_t 的正确格式是 PRIu64

printf( "%" PRIu64 "\n", rnc.u.addr[ 0 ] );

另请注意,您发布的代码不正确:

printf("%u\", rnc.u.addr[0]);

这甚至不会编译 - 它是一个未终止的字符串。第二个" 字符转义为\"。我猜你的意思是"%u\n"

【讨论】:

  • 即使使用PRIu64输出仍然是32
猜你喜欢
  • 2023-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-05
相关资源
最近更新 更多