Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does " if(*((void **) &(_ptr)) != (void *) NULL) " check?

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.

like image 700
Joe.Z Avatar asked Jun 04 '13 08:06

Joe.Z


People also ask

What is void * used for?

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.

What is a void * pointer?

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.

What is a void *) 0?

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.

What is sizeof void *) in C?

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.


6 Answers

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...

like image 58
djf Avatar answered Oct 01 '22 15:10

djf


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.

like image 31
Thomas Padron-McCarthy Avatar answered Oct 01 '22 14:10

Thomas Padron-McCarthy


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)
like image 27
Daniel Fischer Avatar answered Oct 01 '22 15:10

Daniel Fischer


*((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.

like image 26
CB Bailey Avatar answered Oct 01 '22 15:10

CB Bailey


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.

like image 31
AnT Avatar answered Oct 01 '22 15:10

AnT


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.

like image 43
Timo Avatar answered Oct 01 '22 13:10

Timo