I came across a piece of code as following:
/* Allocate memory for _ptr */
if(*((void **) &(_ptr)) != (void *) NULL)
{
/* free _ptr */
}
What is it different from the following?
/* Allocate memory for _ptr */
if (_ptr != NULL )
{
/* free _ptr */
}
EDIT: _ptr may be any type, actually, this is a macro as following:
#define RETURN_MEM_CHK(_ptr) \
{if(*((void **) &(_ptr)) != (void *) NULL){/* free _ptr */}
Sorry about bringing confusion.
void (C++) When used in the declaration of a pointer, void specifies that the pointer is "universal." If a pointer's type is void* , the pointer can point to any variable that's not declared with the const or volatile keyword. A void* pointer can't be dereferenced unless it's cast to another type.
The void pointer in C is a pointer that is not associated with any data types. It points to some data location in the storage. This means that it points to the address of variables. It is also called the general purpose pointer. In C, malloc() and calloc() functions return void * or generic pointers.
JavaScript void 0 means returning undefined (void) as a primitive value. You might come across the term “JavaScript:void(0)” while going through HTML documents. It is used to prevent any side effects caused while inserting an expression in a web page.
If the system is 16-bit, size of void pointer is 2 bytes. If the system is 32-bit, size of void pointer is 4 bytes. If the system is 64-bit, size of void pointer is 8 bytes.
For what it's worth:
I couldn't figure this out on my own, so I've discussed this with my compiler and he says the condition is equivalent* to 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
Note The check itself isn't really necessary either, as other have pointed out. A more natural way would be to just do:
free(_ptr); _ptr=NULL;
*On this machine, with this OS and this GCC version and this CPU and only when the stars align in just the right way...
One example where it could give different results (and did, on my particular system, when I just tried it):
int _ptr = 0;
int whatever = 17;
if (*((void **) &(_ptr)) != (void *) NULL) {
printf("Not equal (1)\n");
}
if (_ptr != NULL) {
printf("Not equal (2)\n");
}
The first version pretends that the integer variable _ptr is a void pointer, and accesses its memory as if it was a void pointer. On my computer, where ints are 32 bits and pointers are 64 bits, this means reading memory outside the variable. This is of course undefined behavior, and in this case it resulted in the condition evaluating to true.
You would get similar results if _ptr is a pointer of a type other than void*, on a system where that pointer type is of different size or represented differently than a void pointer.
Well, what the difference is depends on what the type of _ptr
is.
if (_ptr != NULL )
would not work if _ptr
is not a pointer type (and NULL
is a null pointer constant that includes a cast to void*
, it could work if NULL
is just an integer constant with value 0 even if _ptr
does not have pointer type).
If _ptr
has pointer type, if (_ptr != NULL )
compares _ptr
with a null pointer. Simples.
if(*((void **) &(_ptr)) != (void *) NULL)
if it does not invoke undefined behaviour, interprets the sizeof (void*)
bytes starting at address &_ptr
as a void*
and compares the result of that reinterpretation to a null pointer of type void*
.
It could behave differently if _ptr
is a value of a pointer type with different representation than void*
.
It works if _ptr
is not of pointer type.
In all reasonable situations, however, it would just be a more complicated way of saying
if ((void*)_ptr != NULL)
*((void **) &(_ptr)) != (void *) NULL
This check also works where _ptr
is not a pointer type, e.g. if _ptr
was a uintptr_t
or something. In this case, the simple comparison _ptr != NULL
might not handle systems where null pointer values did not have an "all zero" representation.
Of course, reading an integer as pointer is not portable either so this code trades one set of problems for a different set of problems.
The
*((void **) &(_ptr)
expression performs raw-memory reinterpretation of the memory region occupied by object _ptr
. The first sizeof(void *)
bytes are reinterpreted as an object of void *
type. Meanwhile, the object _ptr
itself can have absolutely any type. It is natural to assume that it is intended to be an object of the same size as void *
(or greater size).
For example, _ptr
can be an object of some integral type of appropriate size. Obviously in that case if (_ptr == NULL)
might simply refuse to compile in an implementation that defines NULL
as (void *) 0
.
Unless _ptr
has void*
type then the code breaks strict aliasing rules and has undefined behavior:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types: 76)
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object, —a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— anaggregate or union type that includes one of the aforementioned types among its members (including, recursively ,amember of a subaggregate or contained union), or
—a character type.
In the code _ptr
is accessed through lvalue of type void*
, which is only compatible with itself, so none of the above conditions are true otherwise.
There's a good chance that it works like _ptr != NULL
but using code like that is still a terrible practice.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With