我遇到了一段代码如下:
/* 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 */}
抱歉引起混淆。
答案 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 )
具有指针类型,_ptr
将if(*((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
不是指针类型的情况,例如如果_ptr
是uintptr_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
一样工作,但使用这样的代码仍然是一种可怕的做法。