【问题标题】:What does " if(*((void **) &(_ptr)) != (void *) NULL) " check?“ if(*((void **) &(_ptr)) != (void *) NULL) ” 检查什么?
【发布时间】:2013-05-30 13:57:48
【问题描述】:

我遇到了如下一段代码:

/* Allocate memory for _ptr */

if(*((void **) &(_ptr)) != (void *) NULL)
{
   /* free _ptr */
}

和下面的有什么不同?

/* Allocate memory for _ptr */

if (_ptr != NULL )
{
   /* free _ptr */
}

编辑:​_ptr 可以是任何类型,实际上,这是一个宏,如下所示:

#define RETURN_MEM_CHK(_ptr)   \
    {if(*((void **) &(_ptr)) != (void *) NULL){/* free _ptr */}

很抱歉造成混乱。

【问题讨论】:

  • 你的意思是if (_ptr != NULL)?
  • 看起来像是来自混淆 C 编程竞赛的一句话
  • 也没必要;在指针上调用 free 之前不需要检查 NULL。
  • _ptr 的类型是什么?
  • @pinkpanther: r 不是空指针...

标签: c pointers casting


【解决方案1】:

物有所值:

我自己无法解决这个问题,所以我和我的编译器讨论了这个问题,他说条件等同于*if (_ptr != NULL):

% gcc -Wall -O2 -g -c convoluted.c; objdump -d -M intel -S convoluted.o

convoluted.o:     file format elf32-i386
Disassembly of section .text.startup:

00000000 <main>:
#include <stdlib.h>

int main(void)
{
   0:   55                      push   ebp
   1:   89 e5                   mov    ebp,esp
   3:   83 e4 f0                and    esp,0xfffffff0
   6:   83 ec 10                sub    esp,0x10
   void* _ptr=malloc(1024);
   9:   c7 04 24 00 04 00 00    mov    DWORD PTR [esp],0x400
  10:   e8 fc ff ff ff          call   11 <main+0x11>

   if(*((void **) &(_ptr)) != (void *) NULL)
  15:   85 c0                   test   eax,eax
  17:   74 08                   je     21 <main+0x21>
   {
      free(_ptr);
  19:   89 04 24                mov    DWORD PTR [esp],eax
  1c:   e8 fc ff ff ff          call   1d <main+0x1d>
   }

   return 0;
}
  21:   31 c0                   xor    eax,eax
  23:   c9                      leave  
  24:   c3                      ret    

% gcc -Wall -O2 -g -c kindanormal.c; objdump -d -M intel -S kindanormal.o

kindanormal.o:     file format elf32-i386
Disassembly of section .text.startup:

00000000 <main>:
#include <stdlib.h>

int main(void)
{
   0:   55                      push   ebp
   1:   89 e5                   mov    ebp,esp
   3:   83 e4 f0                and    esp,0xfffffff0
   6:   83 ec 10                sub    esp,0x10
   void* _ptr=malloc(1024);
   9:   c7 04 24 00 04 00 00    mov    DWORD PTR [esp],0x400
  10:   e8 fc ff ff ff          call   11 <main+0x11>

   if(_ptr != NULL)
  15:   85 c0                   test   eax,eax
  17:   74 08                   je     21 <main+0x21>
   {
      free(_ptr);
  19:   89 04 24                mov    DWORD PTR [esp],eax
  1c:   e8 fc ff ff ff          call   1d <main+0x1d>
   }

   return 0;
}
  21:   31 c0                   xor    eax,eax
  23:   c9                      leave  
  24:   c3                      ret    

注意 正如其他人指出的那样,检查本身也不是必需的。一种更自然的方法是:

免费(_ptr); _ptr=NULL;

*在这台机器上,使用这个操作系统、这个 GCC 版本和这个 CPU,并且只有当星星以正确的方式对齐时......

【讨论】:

  • 我假设你的 _ptr 是一个指针变量,而不是,例如,一个 int?
  • OP 说它是指向内存的。我没有探索任何其他场景
  • 当然,从 C 语言的角度来看,条件是不等价的。它恰好在您的特定平台上是等效的。
  • 我不会因为适当的免责声明和模范努力而投反对票,但这里有太多的投赞成票。这不是解决这个问题的正确方法,因为真正要问的是它是否做了一些可能使其在异国情调的条件下工作的事情。
【解决方案2】:

一个例子,它可以给出不同的结果(并且在我的特定系统上,当我刚刚尝试它时):

int _ptr = 0;
int whatever = 17;

if (*((void **) &(_ptr)) != (void *) NULL) {
    printf("Not equal (1)\n");
}

if (_ptr != NULL) {
    printf("Not equal (2)\n");
}

第一个版本假装整数变量_ptr是一个空指针,并像访问空指针一样访问它的内存。在我的计算机上,int 是 32 位,指针是 64 位,这意味着读取变量之外的内存。这当然是未定义的行为,在这种情况下,它导致条件评估为真。

如果 _ptr 是非 void* 类型的指针,在该指针类型大小不同或表示方式不同于一个空指针。

【讨论】:

    【解决方案3】:

    嗯,有什么区别取决于_ptr的类型是什么。

    if (_ptr != NULL )
    

    如果_ptr 不是指针类型将不起作用(并且NULL 是一个包含对void* 的强制转换的空指针常量,如果NULL 只是一个值为0 的整数常量,它可能会起作用如果_ptr 没有指针类型)。

    如果_ptr 具有指针类型,if (_ptr != NULL ) 将_ptr 与空指针进行比较。简单。

    if(*((void **) &(_ptr)) != (void *) NULL)
    

    如果它没有调用未定义的行为,则将从地址&amp;_ptr 开始的sizeof (void*) 字节解释为void*,并将重新解释的结果与void* 类型的空指针进行比较。

    如果_ptr 是一个指针类型的值,其表示形式与void* 不同,它的行为可能会有所不同。

    如果_ptr 不是指针类型,它可以工作。

    然而,在所有合理的情况下,这只是一种更复杂的说法

    if ((void*)_ptr != NULL)
    

    【讨论】:

      【解决方案4】:

      *((void **) &amp;(_ptr)) != (void *) NULL

      此检查也适用于 _ptr 不是指针类型的情况,例如如果_ptr 是uintptr_t 或其他东西。在这种情况下,简单的比较 _ptr != NULL 可能无法处理空指针值没有“全零”表示的系统。

      当然,将整数作为指针读取也不是可移植的,所以这段代码用一组问题换了一组不同的问题。

      【讨论】:

      • 无论系统内部表示是什么,空指针总是比较等于整数值 0。另请参阅此 C 常见问题解答条目:c-faq.com/null/ptrtest.html
      • @datenwolf:一个零值的整数常量总是转换为一个空指针值,它总是比较等于一个空指针。空指针值可能不具有全零表示。如果_ptr 是整数类型但包含空指针的表示形式(例如,通过memcpy 或*(void*)&amp;_ptr = (void*)NULL 等强制转换),则它可能具有非零整数 值并将比较为不等于零。
      • @CharlesBailey:抱歉,*(void*)&amp;_ptr = (void*)NULL 语句无法编译,因为您无法取消引用 void*。或者第一个星号是一个错字,但表达式(void*)&amp;_ptr 也不会暴露内部表示;它只会将指向_ptr 类型uintptr_t* 的指针转换为void*。仅使用 C 是不可能公开指针的内部表示的;这样做的所有尝试都会调用未定义的行为。实际检索内部表示的最佳选择是使用内联汇编。
      • @datenwolf:对不起,我的意思是 (void**) 就像在比较中一样。
      • @datenwolf:如果您想绝对确定避免未定义的行为,那么您可以通过将指针转换为指向 char* 或 unsigned char* 的指针并读取指针的表示形式并读取底层字节。显然,问题中的比较会导致未定义的行为,因此要回答这个问题,您确实必须考虑实际系统上可能的实际结果。正如我所说,将整数作为指针读取是不可移植的。这并不意味着它有时不能在某些系统上具有有用的行为。
      【解决方案5】:

      *((void **) &(_ptr)
      

      表达式对对象_ptr 占用的内存区域执行原始内存重新解释。第一个 sizeof(void *) 字节被重新解释为 void * 类型的对象。同时,对象_ptr 本身可以具有任何类型。很自然地假设它是一个与void * 大小相同(或更大)的对象。

      例如,_ptr 可以是某种适当大小的整数类型的对象。显然,在这种情况下,if (_ptr == NULL) 可能会拒绝在将NULL 定义为(void *) 0 的实现中进行编译。

      【讨论】:

        【解决方案6】:

        除非_ptr 具有void* 类型,否则代码会违反严格的别名规则并具有未定义的行为:

        一个对象的存储值只能由左值访问 具有以下类型之一的表达式:76)

        ——兼容的类型 与对象的有效类型,

        ——类型的限定版本 兼容对象的有效类型,

        ——一个类型是 对应于有效类型的有符号或无符号类型 object, — 一种类型,它是对应于 a 的有符号或无符号类型 对象的有效类型的限定版本,

        ——聚合或 联合类型,其中包括上述类型之一 成员(包括,递归地,子聚合的成员或 包含联合),或

        ——一种字符类型。

        在代码中_ptr是通过void*类型的左值访问的,它只与自身兼容,所以以上条件都不成立。

        很有可能它像 _ptr != NULL 一样工作,但使用这样的代码仍然是一种糟糕的做法。

        【讨论】:

          猜你喜欢
          • 2017-12-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-11
          相关资源
          最近更新 更多