Reputation: 31
What is the difference between those two programs:
#include <stdio.h>
int main( void )
{
int* ptr;
*ptr = 600;
printf("ptr = %d\n", *ptr );
return 0;
}
and
#include <stdio.h>
int main( void )
{
int* ptr = (int*) 600;
printf("ptr = %d\n", *ptr );
return 0;
}
Why on the first one I receive ptr = 600
, while on the second, I receive an error saying that the program has stopped working?
Upvotes: 0
Views: 59
Reputation: 7161
In the first example, you are writing 600
to wherever ptr
points, which is undefined because the pointer was never assigned to anything. It may seem to work because it is writing 600
somewhere then reading the value back. You just got lucky that accessing this somewhere didn't cause a segmentation fault.
In the second example, you are explicitly assigning the address 600
to the variable ptr
. This is a rather low value for a memory address, which is almost certainly not somewhere in memory you should be trying to read from. When you dereference it (the line with the printf
), your program "stopped working" because it attempted to read memory from somewhere it shouldn't.
In short, neither one works. But the way in which the first one fails is a bit more deceptive.
Memory is a strange and wonderful thing in that fundamentally everything is just bytes of binary data. When reading anything from memory, a compiler needs some notion of:
The line from your second example int *ptr = (int *)600
declares the variable ptr
as a pointer to an int
. The pointer ptr
allows you to read from arbitrary places in memory when dereferencing it (with *ptr
). Notice how the three above requirements are specified. The value of ptr
is where to start reading. The other two requirements are given by its type int *
, which includes the type of the thing it points to: read sizeof(int)
bytes and interpret what is read as a signed integer int
.
("How to interpret it" is a bit optional. A void *
for example specifies "where to start reading", but explicitly gives no notion of how to interpret what is there, leaving it only as bytes of binary data. Any interface that uses a void *
likely has some other mechanism for specifying "how much to read", such as another parameter of type size_t
.)
The initialization (int *)600
contains a type cast. This forces the compiler to think of 600
as the memory address where an int
resides, making it a value compatible to assign to int *ptr
. Hard-coding an address like this is almost certainly asking for trouble, unless you really know what you're doing (have a memory map for the machine you're writing for, etc.) and have a good reason to do so (e.g. memory-mapped I/O, etc.). In short, you are not giving a useful address.
As an analogy, imagine that you need to buy something, and know of a place that sells it. You send your friend out to buy it for you. But your friend is not very bright. He will do exactly as you tell him, but you must give short and simple instructions. You tell him to go to a specific address and buy four of whatever they sell. What could possibly go wrong? In your case, you got your friend arrested for trespassing. These instructions depend entirely on whether or not you provide a correct address. In the second example, he was not allowed to go to address 600, and was stopped trying to get there.
It also depends on whether or not the store you are thinking of even exists. In both of your examples, ptr
has a meaningless value. To follow the analogy with your first example, you are not providing any address at all. Your friend instead goes to the last place he was thinking of, gives something (the number 600) to whomever is there, then takes it back. If it worked, it was only because you got lucky about where he went and the person he encountered was cooperative.
The way to "fix" your code is to make your pointers actually point to something meaningful. In both examples, you don't actually have a variable of type int
that is yours to use anywhere in memory. You could allocate an int
as a local variable (on the stack) and have ptr
point to it with something like:
int x; // now x is an int that actually exists
int *ptr = &x; // initialize ptr to point to x
Then any assignment to *ptr
would write to x
, for example:
*ptr = 600; // now x == 600
The more useful thing to do with pointers is to use dynamically allocated heap memory, as in:
int *ptr;
ptr = malloc(sizeof(*ptr));
if ( ptr != NULL ) {
*ptr = 600;
}
This allocates an int
in heap memory, makes ptr
point to it, then (if it was successfully allocated) writes 600
to it. A heap allocation like this call to malloc()
is what appears to be missing from your first example.
Upvotes: 1
Reputation: 21965
What is the difference between those two programs:
First piece :
int* ptr; // Uninitialized pointer
*ptr = 600; // Dereferencing causes undefined behaviour
Second piece :
int* ptr = (int*) 600; //600 - A memory location which you may not have access to.
printf("ptr = %d\n", *ptr ); // Dereferencing causes undefined behaviour
Conclusion
There is no difference. Both will crash sooner or later.
Upvotes: 1
Reputation: 134346
Both your programs invoke undefined behavior.
The first one, writing to an unitialized pointer.
The second one, reading from a possibly invalid memory location. FWIW, conversion of an int
to a pointer type is also implementation-defined behaviour.
Solution:
Allocate proper memory to ptr
before you dereference it.
Upvotes: 1