11
int main()
{
    int x = 0;
    free(x);
}

This compiles and appears to be a no-op. What actually happens? is this behavior defined?

Thanks!

rshepherd
  • 1,396
  • 3
  • 12
  • 21
  • 1
    Did youy remember to include `stdlib.h` before calling `free`? I see no `#include` directives in your code. – AnT stands with Russia Feb 25 '10 at 22:37
  • 2
    `free(0)` is a no-op. `free(x)` given `int x` is undefined; what actually happens depends on your compiler. I'm surprised that your compiler will implicitly typecast `int` to `void*` without at least warning you; which compiler is it? – Mike DeSimone Feb 25 '10 at 22:41
  • @AndreyT: Egads, you're right. If `free()` isn't declared, the compiler will assume its signature is `int free(int)` when it sees it used, and the linker will not be the wiser, since C doesn't mangle the parameter types into the name... – Mike DeSimone Feb 25 '10 at 22:43
  • It just dawned on me that, if you were using a platform with an ABI that 1) mapped the first few function parameters to registers, instead of stacking them, and 2) had separate registers for addresses and data (e.g. m68k), this probably would *not* work. – Mike DeSimone Feb 25 '10 at 22:46
  • 1
    In C having no prototype in fact only assumes the return type is int, the parameters remain undefined (any number any type are allowed, and no checking occurs). In C++ it is just illegal. – Clifford Feb 25 '10 at 22:48

8 Answers8

14

No, the behavior is not defined. Moreover, the code is not supposed to compile.

Firstly, the code is not supposed to compile because it contains a constraint violation. The expression you are passing as an operand to free has int type. The parameter of free has void * type. The only case when an int value can be implicitly converted to void * type is when the int value is an Integral Constant Expression (ICE) with value 0. In your case x is not an ICE, meaning that it is not implicitly convertible to void *. The only reason your code compiles is that for historical reasons (to support legacy code) your compiler quietly overlooks the constraint violation present in the free(x) call. I'm sure that if you elevate the level of warnings in your compiler, it will complain (at least with a warning). A pedantic compiler will immediately issue an error for free(x) call. Try Comeau Online, for example in C89/90 mode:

"ComeauTest.c", line 6: error: argument of type "int" is incompatible with parameter
          of type "void *"
      free(x); 
           ^

(Also, did you remember to include stdlib.h before calling free?)

Secondly, let's assume that the code compiles, i.e. it is interpreted by the compiler as free((void *) x). In this case a non-constant integral value x is converted to pointer type void *. The result of this conversion is implementation defined. Note, that the language guarantees that when an ICE with value of 0 is converted to pointer type, the result is a null pointer. But in your case x is not an ICE, so the result of the conversion is implementation-defined. In C there's no guarantee that you will obtain a null pointer by converting a non-ICE integer with value 0 to pointer type. On your implementation it probably just happened that (void *) x with non-ICE x equal to 0 produces a null pointer value of type void *. This null pointer value, when passed to free, results in a no-op, per the specification of free.

In general case though, passing such a pointer to free will result in undefined behavior. The pointers that you can legally pass to free are pointers obtained by previous calls to malloc/calloc/realloc and null pointers. Your pointer violates this constraint in general case, so the behavior is undefined.

This is what happens in your case. But, again, your code contains a constraint violation. And even if you override the violation, the behavior is undefined.

P.S. Note, BTW, that many answers already posted here make the same serious mistake. They assume that (void *) x with zero x is supposed to produce a null pointer. This is absolutely incorrect. Again, the language makes absolutely no guarantees about the result of (void *) x when x is not an ICE. (void *) 0 is guaranteed to be null pointer, but (void *) x with zero x is not guaranteed to be null pointer.

This is covered in C FAQ http://c-faq.com/null/runtime0.html . For those interested in better understanding of why it is so it might be a good idea to read the entire section on null pointers http://c-faq.com/null/index.html

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • I don't think it's a constraint violation if the proper declaration of `free()` isn't in scope (which seems to be the case in the example). In this case, it's just undefined behaviour (calling `free()` with actual parameters `(int)` that don't match the parameter type list in the definition `(void *)`). – caf Feb 25 '10 at 22:30
  • I (now) agree with the ICE vs int with value zero difference; but I was under the impression that integers could be implicitly converted to `void*` albeit with an implementation-defined result. 6.3.2.3/5 says that an integer may be converted to _any_ pointer type. – CB Bailey Feb 25 '10 at 22:33
  • @caf: That's a good point. But I'd expect even a C89/90 compiler to issue a warning about a call to undeclared function. The OP mentions no warnings, which probably means that they included the proper declarattion of `free`. Just guessing, though... – AnT stands with Russia Feb 25 '10 at 22:34
  • @Charles Bailey: 6.3.2.3 talks about *explicit* conversions, i.e. *casts*. It says that you can *cast* an `int` to `void *`. The *implicit* conversions in C are described on per-operator basis. And nothing says anywhere that you can pass an `int` in place of a `void *`. – AnT stands with Russia Feb 25 '10 at 22:36
  • 1
    It's not undefined. The standards are very explicit in the meaning of undefined and implentation-defined: An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation. – paxdiablo Feb 25 '10 at 22:38
  • @paxdiablo: When I say "undefined", I'm not talking abou the result of the conversion, I'm talking about the behavior of `free` when it receives an invalid pointer. And it is indeed undefined. I described it in my answer. – AnT stands with Russia Feb 25 '10 at 22:43
  • @AndreyT: Actually I now see there is an explicit restriction in the constraints for assignment (as reference by function calls). I *think* that the rules were more lax in C90 which I've been doing more work it recently (hence my confusion) but I don't have a C89 reference to hand. – CB Bailey Feb 25 '10 at 23:02
  • @Charles Bailey: I don't see any significant differences between C89 and C99 with relation to this issue. – AnT stands with Russia Feb 25 '10 at 23:14
  • @jbcreix: Absolutely incorrect. Casting a run-time value of `0` to pointer type does *not* produce a null pointer in C. Only a zero Integral Constant Expression does, as I said above (in which case a cast is not required). Please, read the C FAQ link I provided at the end of my answer for better understanding of relationsip between integral zeros and null pointers. BTW, the C++ language standard makes an explicit remark about this in footnote 64. C standard doesn't, but the semntics is the same. Again, read the FAQ. – AnT stands with Russia Feb 26 '10 at 03:55
  • @AndreyT. okay so you can say for A=0 and B=0 ((B==0)&&(A==0)&&(A!=B)) has to evaluate to true if B's 0 comes from a casted int and A's comes from a literal? I don't think the *C* standard supports that as it is. As casting forth and back is allowed I'd like to know how casting 0 would not yield a value that compares equal to 0 ie NULL in the context of pointers. If the standard doesn't sort this then obviously they need to put more thought into it. –  Feb 26 '10 at 04:20
  • And let's say NULL=123 and the compiler turns (void*)x==0 to (void*)x==123. Then how do you compare x to the "valid" address 0? –  Feb 26 '10 at 04:42
  • BTW, the "answer" in the FAQ is "use a machine dependent trick". Then why contaminate C with your machine dependent weirdness? Just make the casts turn it to 123 as well and use the trick as you should have done in the first place. A bad and inconsistent hack however you look at it. –  Feb 26 '10 at 04:51
  • @jbcreix: I don't understand your A and B example, but the point is that C language doesn't guarantee that `(void *) x == 0` expression evaluates to true. When `x` is zero, `(void *) x` will normally produce a `0x0` pointer, while a constant `0` in pointer context get translated into the physical null pointer representation, which might be `0xBAADFOOD` for example. `0x0` is not equal to `0xBAADFOOD`, which is why `(void *) x == 0` is generally false. – AnT stands with Russia Feb 26 '10 at 05:52
  • As for how you compare something with adress `0`... You don't. C language doesn't have such feature as comparing a pointer to an explicit numerical address. Such comparisons are only possible within the implementation-defined realm, which lies outside the bounds of formal C language. – AnT stands with Russia Feb 26 '10 at 05:53
  • @AndreyT now I see the defined behavior, so you are right. It still strikes me as arbitrary and poorly considered. There is no reason for those machines not to use 0 or whatever it maps to when casted to a pointer in the first place. Accessing memory doesn't crash at all on many architectures. They could also behave as if (void*)123==0 in any situation. And if performance is the worry, just don't allow the "NULL==0 sometimes" rule and have its value be machine dependent, which is probably the right thing to do. It's not as if the standard has never disallowed previously valid behavior. –  Feb 26 '10 at 07:19
12

In your case, it works because calling free(NULL) (i.e., free(0)) is a NOOP. However, if you called it with any value other than 0 (NULL), the behavior would be undefined-- crashes and/or memory corruption are likely candidates.

EDIT: As others have pointed out later, free(x) (with x=0) and free(NULL) are not the same thing. (Though it is often 0, the value of the NULL pointer is implementation-defined, and cannot be relied upon.) Please see AndreyT's answer for a very good clarification.

Eric Pi
  • 1,904
  • 12
  • 13
  • 9
    `(void *) x` is not guaranteed to be null pointer. This guarantee only applies when `x` is an Integral Constant Expressions. Calling `free(x)` with `x` set to `0` is *not* equivalent to `free(0)`. The code produces undefined behavior that just happened to work out as no-op in OP's case. – AnT stands with Russia Feb 25 '10 at 22:25
  • Eric, your answer is accepted, but it is wrong - `free(x)` and `free(0)` are not the same thing, as some of the other answers have noted. Since it is the accepted answer, you should probably edit your answer. – Alok Singhal Feb 26 '10 at 02:14
  • @Alok: Good idea. I added a note referencing AndreyT's good description of the subject. – Eric Pi Feb 26 '10 at 11:57
4

Freeing NULL (or 0) does nothing. It's a no-op.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
3

Have you tried turning your compiler's warning level up? For example gcc -ansi -pedantic -W -Wall reports:

tmp.c:6: warning: passing argument 1 of ‘free’ makes pointer from integer without a cast

The behaviour is undefined. Don't do it.

Philip Potter
  • 8,975
  • 2
  • 37
  • 47
  • 1
    It's not defined. Only integer constant expressions are guaranteed to convert to a null pointer constant. 'x' is not a constant expression. See n1256 6.3.2.3p3. – Philip Potter Feb 25 '10 at 22:09
  • Actually, free(0) is defined, because even if a null pointer has nonzero representation, a constant integer expression with value zero will magically convert to the correct null pointer. This happens through compiler magic and is defined in the C standard. free(x) however is not defined, even if x==0, because x is not a constant expression. – Philip Potter Feb 25 '10 at 22:14
  • Ints are not the same size a pointers on all platforms, shockingly. Nor does the C spec specify that they should be. The AS/400 had 32 bit ints and 128 bit pointers, for instance. – Joel Feb 25 '10 at 22:20
  • Slight nitpick, @Phillip, it's implementation-defined , not undefined. – paxdiablo Feb 25 '10 at 22:26
  • 1
    @paxdiablo: The whole thing is *undefined*. The result of the conversion of `int` to `void *` is implementation-defined. But when you pass such a pointer to `free`, the behavior becomes undefined (in general case). – AnT stands with Russia Feb 25 '10 at 22:39
  • Freeing a pointer that wasn't allocated by one of the allocation functions is undefined, but the standard specifically allows free(0) which is what the OP posted, and it's an implementation issue if you cast a validly allocated pointer to int then back again. Nonetheless, it's a bad idea, I agree with that. – paxdiablo Feb 25 '10 at 22:49
  • 1
    @paxdiablo: The structure of the behavior in this code is the following: There's an implementation-defined chance that the code is a no-op. In all other cases the behavior is undefined. What is the "grand total" of that in general case? In general case the behavior is undefined. In general case you have to choose the *worst*, even if the chances of that "worst" happening are low. – AnT stands with Russia Feb 25 '10 at 22:56
2

From the free man page:

free() frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behaviour occurs. If ptr is NULL, no operation is performed.

In your example, you're actually calling free(0), since free accepts a pointer as an argument. You're essentially telling the runtime to free the memory at address 0, which has not been previously allocated by malloc.

Since '0' is NULL, nothing will happen. (Thanks to the comments for pointing out my silly error).

Skrud
  • 11,604
  • 5
  • 24
  • 22
  • 3
    `free(0)` is defined and valid. See Eric Pi's answer and http://stackoverflow.com/questions/1938735/does-freeptr-where-ptr-is-null-corrupt-memory/1938758#1938758 – Fred Larson Feb 25 '10 at 22:08
  • 3
    Your quote says "If ptr is NULL, no operation is performed." but you are surprised that there is no segmentation fault? – CB Bailey Feb 25 '10 at 22:08
0

It should throw an access violation on most architectures. OK it's a 0 so it works, but if it was not 0 it would fail. It's undefined. You can't free() a stack allocated variable.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • 4
    No. 0 can be converted to a `void*`, it converts to the null pointer and `free` has no effect when passed a null pointer – CB Bailey Feb 25 '10 at 22:04
  • 3
    "free() a stack allocated variable" would be free(&x). – paxdiablo Feb 25 '10 at 22:07
  • Ok. You found the one in a million case where it might work. But `free`ing a stack allocated variable will crash in 99% of cases. – Billy ONeal Feb 25 '10 at 22:07
  • 2
    An integer value of zero is always converted to a null pointer value. The relative width of `int` and `void*` is immaterial assuming that a correct prototype for `free` is visible. – CB Bailey Feb 25 '10 at 22:10
  • @Charles Bailey: Please feel free to club me over the head. You're correct. – Billy ONeal Feb 25 '10 at 22:20
  • 3
    @Charles: only if it's a compile-time constant. – Alok Singhal Feb 25 '10 at 22:27
  • @Alok: Good point, although it is *likely* to work. I haven't come across any implementations where the cast of an `int` with value zero doesn't convert to the null pointer unlike a constant expression which must. – CB Bailey Feb 25 '10 at 22:35
  • @Charles: I remember reading about compilers where `NULL` wasn't all bits 0. Most likely, `int` 0 converted to `void *` will be `NULL` with most of the compilers today. – Alok Singhal Feb 25 '10 at 22:37
0

free() works on dynamically allocated memory, i.e., memory allocated with malloc, calloc, or realloc. Also, it takes a pointer, and you are passing teh value of x. There is no reason to call it on stack allocated variables that will be freed when the stack unwinds and that behavior is undefined. Always good to read the documentation.

http://www.cplusplus.com/reference/clibrary/cstdlib/free/

Ed S.
  • 122,712
  • 22
  • 185
  • 265
0
  

free (0 );

"Calling free function with NULL value , will have no effect."

Actually it will make impact , when we are freeing the using memory.

Here x value is zero , So that it does not make any problem. If x has value other than

zero , in this case it may get segmentation fault . Because x value might be using as a

memory representation some other variables.

Pavunkumar
  • 5,147
  • 14
  • 43
  • 69