25

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.

12
  • 3
    Did you mean if (_ptr != NULL)? Commented Jun 4, 2013 at 8:45
  • 15
    Looks like a line from an obfuscated C programming contest Commented Jun 4, 2013 at 8:45
  • 9
    It's also unnecessary; one doesn't need to check for NULL before invoking free on a pointer. Commented Jun 4, 2013 at 8:46
  • 5
    What is type of _ptr? Commented Jun 4, 2013 at 8:46
  • 8
    @pinkpanther: r is not a null pointer... Commented Jun 4, 2013 at 9:06

6 Answers 6

16

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

Sign up to request clarification or add additional context in comments.

4 Comments

I assume your _ptr was a pointer variable, and not, for example, an int?
The OP said it was ptr to memory. I didn't explore any other scenarios
The condition is, of course, not equivalent from the point of view of C language. It just happens to be equivalent on your specific platform.
I won't downvote because of the proper disclaimer and exemplary effort, but there are too many upvotes here. This is not the correct way to approach this question, because what really is being asked is whether it does something which might make it work under exotic conditions.
12

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.

Comments

6

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) 

Comments

3

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

7 Comments

The null pointer always compares equal to a integer value of 0, no matter what the system internal representation is. Also see this C FAQ entry: c-faq.com/null/ptrtest.html
@datenwolf: An integer constant of value zero always converts to a null pointer value which will always compare equal to a null pointer. The null pointer value may not have an all zero representation. If _ptr is of an integer type but contains the representation of a null pointer (e.g. through a memcpy or a cast such as *(void*)&_ptr = (void*)NULL) then it may have a non-zero integer value and will compare as not equal to zero.
@CharlesBailey: Sorry, but the statement *(void*)&_ptr = (void*)NULL won't compile, since you can't dereference a void*. Or the first asterisk was a typo, but the expression (void*)&_ptr wouldn't expose the internal representation either; it would merely turn a pointer to _ptr of type uintptr_t* into a void*. Using just C it is impossible to expose the internal representation of a pointer; all attempts in doing so invoke undefined behavior. Your best bet in actually retrieving the internal representation would be the use of inline assembly.
@datenwolf: Sorry, I meant (void**) like in the comparison.
@datenwolf: If you want to be absolutely sure of avoiding undefined behaviour then you can read the representation of a pointer by casting a pointer to the pointer to a char* or unsigned char* and reading the underlying bytes. Obviously the comparison in the question causes undefined behavior so to answer the question you really have to consider likely practical outcomes on real systems. As I said, reading an integer as a pointer is not portable. That doesn't mean that it can't have useful behaviour on some systems some of the time.
|
1

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.

Comments

1

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.