什么“if(*((void **)&(_ ptr))!=(void *)NULL)”检查?

时间:2013-06-04 08:41:34

标签: c pointers casting

我遇到了一段代码如下:

/* 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 */}

抱歉引起混淆。

6 个答案:

答案 0 :(得分:16)

符合它的价值:

我无法自己解决这个问题,所以我已经用我的编译器讨论了这个问题,他说条件等同于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,只有当星星以正确的方式对齐时...

答案 1 :(得分:12)

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

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 是一个void指针,并访问其内存,就像它是一个void指针一样。在我的计算机上,int是32位,指针是64位,这意味着读取变量外的内存。这当然是未定义的行为,在这种情况下,它导致条件评估为真。

如果 _ptr void * 以外的类型的指针,在指针类型大小不同或表示方式不同的系统上,您会得到类似的结果无效指针。

答案 2 :(得分:6)

嗯,区别在于_ptr的类型是什么。

if (_ptr != NULL )
如果_ptr不是指针类型,则

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

如果if (_ptr != NULL )具有指针类型,_ptrif(*((void **) &(_ptr)) != (void *) NULL) 与空指针进行比较。 Simples

sizeof (void*)

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

如果_ptr是指针类型的值不同于void*,则行为可能会有所不同。

如果_ptr不是指针类型,则它有效。

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

if ((void*)_ptr != NULL)

答案 3 :(得分:3)

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

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

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

答案 4 :(得分:1)

*((void **) &(_ptr)

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

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

答案 5 :(得分:1)

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

  

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

     

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

     

- 类型的合格版本   与对象的有效类型兼容,

     

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

     

- anaggregate或   联合类型,其中包括上述类型之一   成员(包括,递归地,成为子集合或成员的成员)   包含联盟),或

     

- 字符类型。

在代码中_ptr是通过类型void*的左值访问的,它只与兼容与其自身相关,因此上述条件都不是真的。

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