Is it dangerous to use NULL pointer dereferencing at rvalue side ? - c

I have the following code.
int *a = NULL;
int b;
b = *a;
Is it undefined in any way?
I am trying to access the location pointed by a only for reading. What wrong it can do ?

Is it undefined in any way?
Yes, the behavior is undefined.
I am trying to access the location pointed by a only for reading.
The location pointed to does not exist. There's nothing there. To read something, there has to be a value to read, but a null pointer points... nowhere. It doesn't point at anything.

Implementation chooses the physical values of null-pointers specifically to make them "point" to address(es) that contains nothing of any value on the given platform. That means that there's nothing to see there. Moreover, on a modern virtual memory platform there might not even be a specific "there" associated with that address.
If you are doing it out of pure curiosity, it still won't work on a typical modern platform: that [virtual] memory region is typically "not yours", meaning that hardware/OS protection mechanism will prevent you from reading it (by crashing your program).
Meanwhile, the language states that any attempts to dereference a null-pointer leads to undefined behavior, regardless of what you are dereferencing it for.

Yes, it's totally undefined behaviour. You are dereferencing a null pointer. Anything could happen.

Don't dereference NULL pointers. It's like crossing the streams, but worse. I'm not even sure what you're trying to accomplish? Are you trying to initialize two pointers to NULL?

The result is undefined and will be different under different platforms.
NULL is equal to 0. Therefore saying int* a = NULL means that you make a pointer to address 0, which should contain an integer.
By dereferecing a by b = *a you instruct the program to go to the address 0 and get the data.
Now, depending on the platform, address 0 will most probably be inaccessible to the user code. If you are not running on top of an operating system (as the case is for example on a microcontroller environment), then address 0 is normally the beginning of the internal ROM. In other cases it can be the beginning of the stack etc.
In most of the cases you are going to get a null pointer exception when you dereference a null pointer.

You have two alarms that pop up here. NULL and all other forms of null pointers are strickly forbidden by the standard. Not because they are often realized internally by a bit pattern of all 0 for the representation of such a pointer but because that is the one-and-single reserved value for pointers that indicate that it isn't pointing anywhere. Don't do this.
Having a pointer point to the address that corresponds to the integer value 0 is a different thing. You could only enforce such a value and interpretation of a point by some trick, e.g
union over {
int *a;
uintptr_t b;
} ov;
ov.b = 0;
int c = *ov.a;
What then happens is completely depending on your platform. If 0 is a valid address in your address space this could work, it probably wouldn't. Don't do it unless you know better.

Related

Why init pointer to NULL is a good practice? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
In case you don't want (or can not) init a pointer with an address, I often hear people say that you should init it with NULL and it's a good practice.
You can find people also say something like that in SO, for example here.
Working in many C projects, I don't think it is a good practice or at least somehow better than not init the pointer with anything.
One of my biggest reason is: init a pointer with NULL increase the chance of null pointer derefence which may crash the whole software, and it's terrible.
So, could you tell me what are the reasons if you say that it is a good pratice or people just say it for granted (just like you should always init an variable) ?
Note that, I tried to find in Misra 2004 and also did not find any rule or recommendation for that.
Update:
So most of the comments and answers give the main reason that the pointer could have an random address before being used, so it's better to have it as null so you could figure out the problem faster.
To make my point clearer, I think that it doesn't make senses nowadays in commercial C softwares, in a practical point of view. The unassigned pointer that is used will be detected right the way by most of static analyzer, that's why I prefer to let it be un-init because if I init it with NULL then when developers forget to assign it to a "real" address, it passes the static analyzer and will cause runtime problems with null pointer.
You said
One of my biggest reason is: init a pointer with NULL increase the chance of null pointer derefence which may crash the whole software, and it's terrible.
I would argue the main reason is actually due to exactly this. If you don't init pointers to NULL, then if there is a dereferecing error it's going to be a lot harder to find the problem because the pointer is not going to be set to NULL, it's going to be a most likely garbage value that may look exactly like a valid pointer.
C has very little runtime error checking, but NULL is guaranteed not to refer to a valid address, so a runtime environment (typically an operating system) is able to trap any attempt to de-refernce a NULL. The trap will identify the point at which the de-reference occurs rather then the point the program may eventually fail, making identification of the bug far easier.
Moreover when debugging, and unitialised pointer with random content may not be easily distinguishable from a valid pointer - it may refer to a plausible address, whereas NULL is always an invalid address.
If you de-reference an uninitialised pointer the result is non-deterministic - it may crash, it may not, but it will still be wrong.
If it does crash you cannot tell how or even when, since it may result in corruption of data, or reading of invalid data that may have no effect until that corrupted data is later used. The point of failure will not necessarily be the point of error.
So the purpose is that you will get deterministic failure, whereas without initialising, anything could happen - including nothing, leaving you with a latent undetected bug.
One of my biggest reason is: init a pointer with NULL increase the chance of null pointer derefence which may crash the whole software, and it's terrible.
Deterministic failure is not "terrible" - it increases your chance of finding the error during development, rather then having your users finding the error after deployment. What you are effectively suggesting is that it is better to leave the bugs in and hide them. The dereference on null is guaranteed to be trapped, de-referencing an unitialised pointer is not.
That said initialising with null, should only be done if at the point of declaration you cannot directly assign an otherwise valid value. That is to say, for example:
char* x = malloc( y ) ;
is much preferable to:
char* x = NULL ;
...
x = malloc( y ) ;
which is in turn preferable to:
char* x ;
...
x = malloc( y ) ;
Note that, I tried to find in Misra 2004 and also did not find any
rule or recommendation for that.
MISRA C:2004, 9.1 - All automatic variables shall have been assigned a value before being used.
That is to say, there is no guideline to initialise to NULL, simply that initialisation is required. As I said initialisation to NULL is not preferable to initialising to a valid pointer. Don't blindly follow the "must initialise to NULL advice", because the rule is simply "must initialise", and sometimes the appropriate initialisation value is NULL.
If you don't initialize the pointer, it can have any value, including possibly NULL. It's hard to imagine a scenario where having any value including NULL is preferable to definitely having a NULL value. The logic is that it's better to at least know its value than have its value unpredictably depend on who knows what, possibly resulting in the code behaving differently on different platforms, with different compilers, and so on.
I strongly disagree with any answer or argument based on the idea that you can reliably use a test for NULL to tell if a pointer is valid or not. You can set a pointer to NULL and then test it for NULL within a limited context where that is known to be safe. But there will always be contexts where more than one pointer points to the same thing and you cannot ensure that every possible pointer to an object will be set to NULL at the very place the object is freed. It is simply an essential C programming discipline to understand that a pointer may or may not point to a valid object depending on what is going on in the code.
One issue is that given a pointer variable p, there is no way defined by the C language to ask, "does this pointer point to valid memory or not?" The pointer might point to valid memory. It might point to memory that (once upon a time) was allocated by malloc, but that has since been freed (meaning that the pointer is invalid). It might be an uninitialized pointer, meaning that it's not even meaningful to ask where it points (although it is definitely invalid). But, again, there's no way to know.
So if you're bopping along in some far-off corner of a large program, and you want to say
if(p is valid) {
do something with p;
} else {
fprintf(stderr, "invalid pointer!\n");
}
you can't do this. Once again, the C language gives you no way of writing if(p is valid).
So that's where the rule to always initialize pointers to NULL comes in. If you adopt this rule and follow it faithfully, if you initialize every pointer to NULL or as a pointer to valid memory, if whenever you call free(p) you always immediately follow it with p = NULL;, then if you follow all these rules, you can achieve a decent way of asking "is p valid?", namely:
if(p != NULL) {
do something with p;
} else {
fprintf(stderr, "invalid pointer!\n");
}
And of course it's very common to use an abbreviation:
if(p) {
do something with p;
} else {
fprintf(stderr, "invalid pointer!\n");
}
Here most people would read if(p) as "if p is valid" or "if p is allocated".
Addendum: This answer has attracted some criticism, and I suppose that's because, to make a point, I wrote some unrealistic code which some people are reading more into than I'd intended. The idiom I'm advocating here is not so much valid pointers versus invalid pointers, but rather, pointers I have allocated versus pointers I have not allocated (yet). No one writes code that simply detects and prints "invalid pointer!" as if to say "I don't know where this pointer points; it might be uninitialized or stale". The more realistic way of using the idiom is do do something like
/* ensure allocation before proceeding */
if(p == NULL)
p = malloc(...);
or
if(p == NULL) {
/* nothing to do */
return;
}
or
if(p == NULL) {
fprintf(stderr, "null pointer detected\n");
exit(0);
}
(And in all three cases the abbreviation if(!p) is popular as well.)
But, of course, if what you're trying to discriminate is pointers I have allocated versus pointers I have not allocated (yet), it is vital that you initialize all your un-allocated pointers with the explicit marker you're using to record that they're un-allocated, namely NULL.
One of my biggest reason is: init a pointer with NULL increase the chance of null pointer derefence which may crash the whole software, and it's terrible.
Which is why you add a check against NULL before using that pointer value:
if ( p ) // p != NULL
{
// do something with p
}
NULL is a well-defined invalid pointer value, guaranteed to compare unequal to any object or function pointer value. It's a well-defined "nowhere" that's easy to check against.
Compare that to the indeterminate value that the uninitialized pointer1 may have - most likely, it will also be an invalid pointer value that will lead to a crash as soon as you try to use it, but it's almost impossible to determine that beforehand. Is 0xfff78567abcd2220 a valid or invalid pointer value? How would you check that?
Obviously, you should do some analysis to see if an initialization is required. Is there a risk of that pointer being dereferenced before you assign a valid pointer value to it? If not, then you don't need to initialize it beforehand.
Since C99, the proper answer has been to defer instantiating a pointer (or any other type of object, really) until you have a valid value to initialize it with:
void foo( void )
{
printf( "Gimme a length: " );
int length;
scanf( "%d", &length );
char *buf = malloc( sizeof *buf * length );
...
}
ETA
I added a comment to Steve's answer that I think needs to be emphasized:
There's no way to determine if a pointer is valid - if you receive a pointer argument in a function like
void foo( int *ptr )
{
...
}
there is no test you can run on ptr to indicate that yes, it definitely points to an object within that object's lifetime and is safe to use.
By contrast, there is an easy, standard test to indicate that a pointer is definitely invalid and unsafe to use, and that's by checking that its value is NULL. So you can at least avoid using pointers that are definitely invalid with the
if ( p )
{
// do something with p
}
idiom.
Now, just because p isn't NULL doesn't automatically mean it's valid, but if you're consistent and disciplined about setting unused pointers to NULL, then the odds are pretty high that it is.
This is one of those areas where C doesn't protect you, and you have to devote non-trivial amounts of effort to make sure your code is safe and robust. Frankly, it's a pain in the ass more often than not. But being disciplined with using NULL for inactive pointers makes things a little easier.
Again, you have to do some analysis and think about how pointers are being used in your code. If you know you're going to set a pointer to valid value before it's ever read, then it's not critical to initialize it to anything in particular. If you have multiple pointers pointing to the same object, then you need to make sure if that object ever goes away that all those pointers are updated appropriately.
This assumes that the pointer in question has auto storage duration - if the pointer is declared with the static keyword or at file scope, then it is implicitly initialized to NULL.

Invalid pointer becoming valid again

int *p;
{
int x = 0;
p = &x;
}
// p is no longer valid
{
int x = 0;
if (&x == p) {
*p = 2; // Is this valid?
}
}
Accessing a pointer after the thing it points to has been freed is undefined behavior, but what happens if some later allocation happens in the same area, and you explicitly compare the old pointer to a pointer to the new thing? Would it have mattered if I cast &x and p to uintptr_t before comparing them?
(I know it's not guaranteed that the two x variables occupy the same spot. I have no reason to do this, but I can imagine, say, an algorithm where you intersect a set of pointers that might have been freed with a set of definitely valid pointers, removing the invalid pointers in the process. If a previously-invalidated pointer is equal to a known good pointer, I'm curious what would happen.)
By my understanding of the standard (6.2.4. (2))
The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.
you have undefined behaviour when you compare
if (&x == p) {
as that meets these points listed in Annex J.2:
— The value of a pointer to an object whose lifetime has ended is used (6.2.4).
— The value of an object with automatic storage duration is used while it is indeterminate (6.2.4, 6.7.9, 6.8).
Okay, this seems to be interpreted as a two- make that three part question by some people.
First, there were concerns if using the pointer for a comparison is defined at all.
As is pointed out in the comments, the mere use of the pointer is UB, since $J.2: says use of pointer to object whose lifetime has ended is UB.
However, if that obstacle is passed (which is well in the range of UB, it can work after all and will on many platforms), here is what I found about the other concerns:
Given the pointers do compare equal, the code is valid:
C Standard, §6.5.3.2,4:
[...] If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.
Although a footnote at that location explicitly says. that the address of an object after the end of its lifetime is an invalid pointer value, this does not apply here, since the if makes sure the pointer's value is the address of x and thus is valid.
C++ Standard, §3.9.2,3:
If an object of type T is located at an address A, a pointer of type cv T* whose value is the address A is said to point to that object, regardless of how the value was obtained. [ Note: For instance, the address one past the end of an array (5.7) would be considered to point to an unrelated object of the array’s element type that might be located at that address.
Emphasis is mine.
It will probably work with most of the compilers but it still is undefined behavior. For the C language these x are two different objects, one has ended its lifetime, so you have UB.
More seriously, some compilers may decide to fool you in a different way than you expect.
The C standard says
Two pointers compare equal if and only if both are null pointers, both
are pointers to the same object (including a pointer to an object and
a subobject at its beginning) or function, both are pointers to one
past the last element of the same array object, or one is a pointer to
one past the end of one array object and the other is a pointer to the
start of a different array object that happens to immediately follow
the first array object in the address space.
Note in particular the phrase "both are pointers to the same object". In the sense of the standard the two "x"s are not the same object. They may happen to be realized in the same memory location, but this is to the discretion of the compiler. Since they are clearly two distinct objects, declared in different scopes the comparison should in fact never be true. So an optimizer might well cut away that branch completely.
Another aspect that has not yet been discussed of all that is that the validity of this depends on the "lifetime" of the objects and not the scope. If you'd add a possible jump into that scope
{
int x = 0;
p = &x;
BLURB: ;
}
...
if (...)
...
if (something) goto BLURB;
the lifetime would extend as long as the scope of the first x is reachable. Then everything is valid behavior, but still your test would always be false, and optimized out by a decent compiler.
From all that you see that you better leave it at argument for UB, and don't play such games in real code.
It would work, if by work you use a very liberal definition, roughly equivalent to that it would not crash.
However, it is a bad idea. I cannot imagine a single reason why it is easier to cross your fingers and hope that the two local variables are stored in the same memory address than it is to write p=&x again. If this is just an academic question, then yes it's valid C - but whether the if statement is true or not is not guaranteed to be consistent across platforms or even different programs.
Edit: To be clear, the undefined behavior is whether &x == p in the second block. The value of p will not change, it's still a pointer to that address, that address just doesn't belong to you anymore. Now the compiler might (probably will) put the second x at that same address (assuming there isn't any other intervening code). If that happens to be true, it's perfectly legal to dereference p just as you would &x, as long as it's type is a pointer to an int or something smaller. Just like it's legal to say p = 0x00000042; if (p == &x) {*p = whatever;}.
The behaviour is undefined. However, your question reminds me of another case where a somewhat similar concept was being employed. In the case alluded, there were these threads which would get different amounts of cpu times because of their priorities. So, thread 1 would get a little more time because thread 2 was waiting for I/O or something. Once its job was done, thread 1 would write values to the memory for the thread two to consume. This is not "sharing" the memory in a controlled way. It would write to the calling stack itself. Where variables in thread 2 would be allocated memory. Now, when thread 2 eventually got round to execution,all its declared variables would never have to be assigned values because the locations they were occupying had valid values. I don't know what they did if something went wrong in the process but this is one of the most hellish optimizations in C code I have ever witnessed.
Winner #2 in this undefined behavior contest is rather similar to your code:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = (int*)malloc(sizeof(int));
int *q = (int*)realloc(p, sizeof(int));
*p = 1;
*q = 2;
if (p == q)
printf("%d %d\n", *p, *q);
}
According to the post:
Using a recent version of Clang (r160635 for x86-64 on Linux):
$ clang -O realloc.c ; ./a.out
1 2
This can only be explained if the Clang developers consider that this example, and yours, exhibit undefined behavior.
Put aside the fact if it is valid (and I'm convinced now that it's not, see Arne Mertz's answer) I still think that it's academic.
The algorithm you are thinking of would not produce very useful results, as you could only compare two pointers, but you have no chance to determine if these pointers point to the same kind of object or to something completely different. A pointer to a struct could now be the address of a single char for example.

Address of members of a struct via NULL pointer

Why the following expression is not a (null pointer) runtime error?
typedef struct{
int a,b,c;
} st;
st obj={10,12,15};
st *ptr1=&obj;
st *ptr2=NULL;
printf("%d",*(int *)((char*)ptr1+(int)&ptr2->b));
Because you are performing pointer arithmetic on a NULL pointer which invokes undefined behavior - and that's not required to crash.
actually, in GNU C, &ptr2->b while st *ptr2=NULL; produce the data member 'b' 's byte offset in the struct, it is 4 here
Unfortunately, dereferencing a null pointer doesn't always crash! Sometimes it just happily moves ahead, accessing invalid memory!
You're probably seeing the output value '12'. What the compiler is doing when you dereference a pointer is adding an offset to get the address of member.
ints are typically 4 bytes long, so if ptr2 had an address of, say, 10, then ptr2->a must also be at address 10, ptr2->b must be at 14, and ptr2->c must be at address 18.
Since ptr2 is NULL, it's adding 4 to get to member b, and NULL+4=4. Then you're adding 4 to ptr1 and dereferencing that, which gets you ptr1's b member!
But this is not portable, and you shouldn't do this!
This is perfectly legal usage because the "take address of"-operator actually means: "Do not load/store the value of the variable, just give me the address you have computed to access it." It stops the access before the actual undefined behaviour is invoked.
This is such a typical idiom in programming that it was actually encapsulated in the offsetof() macro (which is used quite extensively in linux) and has since made its way into the compilers themselves to provide more meaningful error messages. See http://en.wikipedia.org/wiki/Offsetof for more details, even though I disagree with this article on the legality of the statement.

Saving code space by altering a function call in C

I am calling a function that returns a variable through a pointer parameter. I do not care about the return value of this parameter nor do I want to make a dummy variable to pass to the function. For a simple example's sake, let's say the function is as follows and I don't care about, nor want to make a dummy variable for parameter "d".
void foo(int a, int b, int* c, int* d)
{
*c = a+b;
*d = a+b+*c;
}
I understand that a NULL pointer is in theory a pointer to a location that is not the address of any object or function. Would it be correct to pass NULL into "d" in this function if NULL was defined as the following? Or is this going to change whatever is at the 0'th element in memory?
#define NULL ((void *)0)
The target device is an MSP430 and I am using IAR C. No operating system is used therefore no memory management is implemented
EDIT: Please note that I do not want to create a dummy variable. Also if there was a way to fool the compiler into optimizing the "d" parameter out without altering the function definition, this is preferable.
EDIT#2: I would rather not use the & operator in the function call as it generates inefficient code that I do not want to generate
EDIT#3: For those who don't believe me when I am talking about the & operator... the compiler manual states "Avoid taking the address of local variables using the & operator. This is inefficient
for two main reasons. First, the variable must be placed in memory, and thus cannot be placed in a processor register. This results in larger and slower code. Second, the optimizer can no longer assume that the local variable is unaffected over function calls."
No, it is not correct.
The C standard does not define the behavior when you do this. On many systems, it will cause an error (some sort of memory fault) when foo attempts to store to address 0. If it does not, then you will have written data to address 0, presumably overwriting something else there that may have been needed, so your system may fail at a later time.
You should change your function a bit to allow passing NULL
void foo(int a, int b, int* c, int* d)
{
if(c != NULL)
{
*c = a+b;
if(d != NULL)
{
*d = a+b+*c;
}
}
}
Now you can safely pass NULL. Otherwise, as the other answers already state, you end up dereferencing a NULL pointer which results in undefined behavior.
In your example, if you don't care about the pointer d and you pass NULL as you defined then it'll probably crash due to dereferencing NULL.
You should pass a valid pointer even if you don't care about the result.
Why not just declare a temporary and pass?
int tempd;
foo(a,b,&c, &tempd);
There is no such thing as the 0th element in memory due to virtual memory. However, if you attempt this, your program will crash with a memory exception. I assume you want to ignore d if it's null so simply do this:
if(d != NULL)
{
*d = a+b+*c
}
Since you don't want to create a dummy variable and can't change the function you'll most likely end up scribbling at the memory position 0 on your device whatever that means. Maybe it's a memory mapped hardware register, maybe it's just normal physical memory.
If it's a register, maybe it doesn't have any effect unless you write the magical value 4711 into it which will happen once every three months and the device halts and catches fire. (has happened to me, it's fun to overwrite the boot eeprom on a device)
Or if it's memory maybe you'll send a NULL pointer to a different function later and that function will happily read the value that this function wrote there and you'll end up at 5 in the morning tearing your hair out and yelling "this can't possibly affect that!". (has happened to me on some ancient unix that used to map the NULL page)
Maybe your compiler adds a safety net for you. Maybe it doesn't. Maybe the next version will. Maybe the next hardware revision will come with memory unmapped at address 0 and the device will halt.
I'd create a dummy variable in the calling function and move on to a more interesting problem, but if you're a stress junkie, pass NULL and see what happens (today or in 10 years).
In that specific example code both passed in pointers are dereferenced. Dereferencing NULL is undefined behavior. Just go with the dummy variables.
In general: If a function accepts a pointer it should state if the null pointer is a valid value for the argument. If it doesn't say anything stay on the safe side and just assume it isn't. This will safe you a lot of grief.
Interesting question! Generally speaking, NULL is reserved as an "invalid" address. You shouldn't try to write to it, but I don't think the standard specifies what should happen if you do. On a Windows machine, this will generate an access violation exception. I don't know what will happen on your device.
In any case, passing NULL is the usual way to indicate that you're not interested in the value of an out parameter. But your function must be aware of this and act accordingly:
if( c ) {
*c = a+b;
if( d ) {
*d = a+b+*c;
}
}
Edit
If you can't change the function definition, then you're probably out of luck. You can trick the compiler into not passing d if the calling convention is cdecl. Just declare the function without the d parameter:
extern void foo( int a, int b, int * c );
However, you're definitely into dangerous shenanigans territory here. The function definition will still expect the d paramater, so it will see random garbage.
The only other thing I can think of is passing a fixed address. Since you're writing for a specific device, there might be an address range that's safe to write to. (That is, it won't cause exceptions or corrupt actual memory.)
void * SafeAddress = (void *)0x12345678;
foo( a, b, &c, SafeAddress );
Of course, the easiest thing is to just use the dummy variable. I know you've said more than once that this generates inefficient code, but does that have to be the case? Does it make a difference if the dummy is a local variable versus a global one?
The function tries to store value at the provided address. If the address is invalid, then there will be a malfunction of some sort -- whether you use an operating system or not is irrelevant.
Either you have to give the function some valid address (even if you don't care for the value in a particular case), or you have to change the function so that it does not store the value (and, probably, does not even even compute it), if the address for it is NULL (which may or may not be 0x0 on your platform, BTW).
You keep repeating, that you "don't want" to do the former and can not do the latter. Well, then you have an unsolvable dilemma. Maybe, there already exists some other address, where dummy values like this can be stored in your program (or on the platform) -- you can pass that.
If there is no OS involved, then you must be dealing with some funky programmable device, which means, there ought to be seasoned C-programmers around you. Ask them for confirmation of what you are told here -- clearly, you aren't trusting the answers given to you by several people already.
It will try to assign something at memory location 0x0, so I'd say it will crash
I was able to save code space without increasing memory usage on the stack by declaring a dummy variable as well as a pointer to the dummy variable.
int Dummy;
int* Dummy_ptr = &Dummy;
This allowed the compiler to make optimizations on the function call as the & operator was not used in the function call.
The call is now
foo(a, b, c_ptr, Dummy_ptr);
EDIT: For those of you who don't believe me.
I took a look at the assembler. The Dummy variable exists on the stack, though because it is not used later on, and because it is only a return from the function the address is never passed to the function and any use of that variable in the function is optimized out.

The state of a pointer after deallocation

What does the pointer of a dynamically allocated memory points to after calling the free() function.
Does the pointer points to NULL, or it still points to the same place it pointed before the deallocation.
does the implementation of free() has some kind of standard for this, or it's implemented differently in different platforms.
uint8_t * pointer = malloc(12);
printf("%p", pointer); // The current address the pointer points to
free (pointer);
printf("%p", pointer); // must it be NULL or the same value as before ?
Edit:
I know that the printf will produce the same results, I just want to know if I can count on that on different implementations.
The pointer value is not modified. You pass the pointer (memory address) by value to free(). The free() function does not have access to the pointer variable, so it cannot set it to NULL.
The two printf() calls should produce identical output.
According to the standard (6.2.4/2 of C99):
The value of a pointer becomes indeterminate when the object it points
to reaches the end of its lifetime.
In practice, all implementations I know of will print the same value twice for your example code. However, it is permitted that when you free the memory, the pointer value itself becomes a trap representation, and the implementation does something strange when you try to use the value of the pointer (even though you don't dereference it).
Supposing that an implementation wants to do something unusual, a hardware exception or program abort would be the most plausible I think. You probably have to imagine an implementation/hardware that does a lot of extra work, though, so that every time a pointer value is loaded into a register, it somehow checks whether the address is valid. That could be by checking it in the memory map (in which case I suppose my hypothetical implementation would only trap if the whole page containing the allocation has been released and unmapped), or some other means.
free() only deallocates the memory. The pointer is still pointing to the old location (dangling pointer), which you should manually set to NULL.
Setting the pointer to NULL is a good practice. As the memory location may be reused for other object, you may be able to access and modify data which doesn't belong to you. This is especially hard to debug, since it won't produce a crash, or produce crash at some point which is irrelevant. Setting to NULL will guarantee a re-producible crash if you ever access the non-existent object.
I tried this code in C++
#include<iostream>
using namespace std;
int main(void)
{
int *a=new int;
*a=5;
cout<<*a<<" "<<a<<"\n";
delete a;
*a=45;
cout<<*a<<" "<<a<<"\n";
}
The output was something like this
5 0x1066c20
45 0x1066c20
Running once more yielded a similar result
5 0x13e3c20
45 0x13e3c20
Hence it seems that in gcc the pointer still points to the same memory location after deallocation.
Moreover we can modify the value at that location.

Resources