特定场景下指针解引用和强制转换之间的关系不清楚

Posted

技术标签:

【中文标题】特定场景下指针解引用和强制转换之间的关系不清楚【英文标题】:Unclear relationship between pointer dereferencing and casting in a specific scenario 【发布时间】:2017-12-06 13:28:03 【问题描述】:

我有以下 Cpp 功能:

u_long stringIpV4ToBinary(const char * ipv4)

    u_long * buff[1];
    int ret = InetPton(AF_INET, ipv4, &buff);
    if (ret == -1) return 1;
    if (ret == 0) return 1; // more handling needs to be added

// return ntohl(*buff[0]);           // crash, no warning
// return ntohl((*buff)[0]);         // same meaning above (??) crash, no warning
// return ntohl(**buff);             // crash, no warning
// return ntohl(*(buff[0]));         // crash, no warning
// return ntohl(*(u_long*)buff[0]);  // crash, no warning
// return ntohl((u_long)buff[0]);    // works, but produces warnings:: 'type cast': truncation from 'u_long *' to 'u_long'
// return ntohl(*(u_long*)&buff);    // works
// return ntohl(*(u_long*)&buff[0]); // works
// return ntohl(*(u_long*)buff[0]);  // crash, no warning
// return ntohl(*(u_long*)buff);     // works

我花了一些时间试图提取我需要传递给ntohl() 的 buff 值(所以你可以忽略前几行)。我在玩 *buff[0] *buff 和其他人,没有意识到我需要转换数组第一个元素的值(&buff[0]buff&buff)。

问题:为什么我需要将检索到的值转换为(u_long*),然后引用指针? @runtime 是否模糊或不清楚 buff[0] 中保存的值将是 u_long * 类型?我的期望是ntohl(**buff) - (buff 是数组名称,它衰减为一个指针,并且由于数组本身是 u_long 指针)双解引用就足够了。

【问题讨论】:

InetPton 似乎是一个特定于 Windows 的函数,因此可能值得为这个问题添加相应的标签:) @tkausl 对不起,我没明白为什么它是一个字符指针?我相信这就是我的误解所在。 我看错了变量,抱歉。为什么它是一个大小为 1 的数组呢?只需使用本地 u_long 并获取指向该变量的指针。 InetPton() 返回一个 binary numeric IP address in network byte order. 和 4 个字节(32 位)——在 windows 上 u_long 的大小对于 IPV4 地址来说已经足够了。 buff 声明为正确的类型(IN_ADDR)不是更容易吗? 【参考方案1】:

您已将buff 定义为指向u_long指针 数组(大小为1)。所以InetPton 将生成的 IP 地址直接存储在这个指针变量中。然后您会遇到崩溃,因为此 IP 地址被解释为内存地址并被取消引用。

buff 应该定义为一个简单的u_long,其地址传递给InetPton

u_long buff;
int ret = InetPton(AF_INET, ipv4, &buff);

那么你可以这样做:

return ntohl(buff);

对此进行改进,如果第一个参数的值为AF_INET,则MSDN页面显示如下:

INT WSAAPI InetPton(
  _In_  INT     Family,
  _In_  PCTSTR pszAddrString,
  _Out_ PVOID  pAddrBuf
);

...

指定此参数时,pszAddrString参数必须 指向 IPv4 地址的文本表示和 pAddrBuf 参数返回一个指向IN_ADDR 结构的指针,该结构表示 IPv4 地址。

因此,您应该为第三个参数传递IN_ADDR 的地址:

IN_ADDR addr;
int ret = InetPton(AF_INET, ipv4, &addr);
...
return ntohl(addr.S_addr);

编辑:

详细说明演员阵容产生影响的原因,事实证明并非如此。不同之处在于间接级别和取消引用的次数。

【讨论】:

storing the resulting IP address directly in this pointer variable. - 让我试着阐述一下我是如何理解你所说的。我希望你能纠正我,以防我遗漏了什么。我将&buff 传递给&buff[0] 或简单地将buff 传递给InetPton()。它将值作为数组的第一个元素推送。该函数的输出参数定义为_Out_ PVOID pAddrBuf,因此我应该能够use u_long * a [] 没有问题。函数ntohl() 然后接受 u_long - 应该从 **buff 获得 - 第一个 * 延迟数组,第二个 * - 获取值? @MindaugasBernatavičius 问题在于 IP 地址存储在 buff[0] 中,其类型为 u_long *。例如,如果您将“127.0.0.1”传递给InetPtoN,那么buff[0] 将(假设您的系统是小端)包含值0x0100007f 作为类型u_long *。所以buff[0] 包含你想要的值,但它是错误的类型。这就是(u_long)buff[0] 为您提供预期值的原因。这也是*buff[0] segfaults 的原因,因为它的意思是“获取地址 0x0100007f 的值”,这不是一个有效的地址。 好的,太好了。我想我现在明白了。 PVOID 不是InetPtoN() 的返回类型(不知道为什么会这样,因为返回值是在函数签名的开头指定的),它是它所期望的值的类型——在我的正在使用buff 的地址运算符 (&) 检索案例。由于buff[0]u_long * IP 被存储在那里并取消引用它,我会得到一个段错误。我犯的一个奇怪的错误是认为 PVOID 在某种程度上是一种返回类型 - 也许我因为最近在 C# 中编程而犯了这个错误:D 谁知道。【参考方案2】:

传递给ntohl 的参数不需要强制转换。

在解析 IPv4 字符串地址时,InetPton 只需要内存中可以写入 4 个字节的地址。

u_long addr; // 4 bytes on Windows
int ret = InetPton(AF_INET, ipv4, &addr);
if (ret == -1) return 1;
if (ret == 0) return 1; // more handling needs to be added

return ntohl(addr);

理想情况下,您应该使用 IN_ADDR 类型,但如果您只关心 Windows,则可以直接使用基于 long 的类型。

当您需要在所述变量中存储数据时,将堆栈上的变量声明为指针 (*) 是没有意义的,这是 C 101。

【讨论】:

这是我在 Internet 上找到的一个示例,但感谢它在某种程度上是 C 101。不应该使用指针我同意,只是想了解为什么我看到我看到的行为。 buff 不是存储位置。您不能询问如何检索 buff[0] 中的值,因为它不存在。你的 buff 变量指向一个未定义的位置,当传递给 InetPton 作为写入它的位置时,理论上可能会覆盖你的主引导记录。【参考方案3】:

InetPton 的调用会将一个32 位IPv4 地址写入buff 中的第一个u_long *

所有双重取消引用的变体都将该值视为指针。不是,这就是它们崩溃的原因。

那些工作的人设法只做一次取消引用,并将结果视为长,这就是他们工作的原因。 (尽管我怀疑其中许多实际上具有未指定或未定义的行为,因为您对值是指针还是整数非常随意。)

如果您将buff 声明为IN_ADDR,您的代码可能会更简单且清晰正确。

【讨论】:

我同意最后一点,我不会留下问题中提供的代码。谢谢你帮助我,我很感激。所以你是说buff 不是指向u_long * 指针的指针(数组)?输出参数是_Out_ PVOID pAddrBuf for InetPton() PVOID,我认为是 void * - 一个通用指针,所以我应该能够使用 u_long * ? ... 好像我错过了什么。 正如您声明的那样,buff 确实是一个指针数组。但是函数接受指针的原因是告诉它把结果写到哪里;结果本身不是指针。您只需要传递合适类型的变量的地址 - 例如参见@Anders 答案。 (请注意,_Out_ 不是标准 C++;这里本质上是一个注释。) 精确识别,那是我弄错了 - 我虽然 out 关键字指定了返回类型。很高兴我在脑海中澄清了这一点。非常感谢您的帮助,您让我更容易理解问题:)

以上是关于特定场景下指针解引用和强制转换之间的关系不清楚的主要内容,如果未能解决你的问题,请参考以下文章

深入理解C++中五种强制类型转换的使用场景

深入理解C++中五种强制类型转换的使用场景

关于继承中的强制类型转换

instanceof的使用以及与引用类型的强制类型转换的关系

C++强制类型转换

向上强制转换和向下强制转换