y
is a variable of type int
. Upon declaration(and initialization in this example) the compiler gives it a memory address that holds it's value. Assigning to y
writes to that memory address and reading y
reads that memory address.
x
is a pointer to an int
, in this case x
points to the memory address of y
meaning that *x
will read any value that's in that memory address no matter how that value got there.
This means that:
*x = 5;
and
y = 5;
Both write to the same address and thus changes are visible for both y
and *x
since both read from that same address.
Since in java an int
is an immutable object, the C equivalent of a Java int
is a int* const
in C, a constant pointer to an int
.
If we have this in Java:
int a = 5;
int z = 3;
a = z;
The C equivalent would be:
/* int a = 5; */
int* a = malloc(sizeof(int)); // or `new` in C++
*a = 5;
/* int z = 3; */
int* z = malloc(sizeof(int));
*z = 3;
/* a = z; */
free(a); // or `delete` in C++
a = malloc(sizeof(int));
*a = *z;
We need pointers in C to reference other variables, in Java this feature is standard.
For example, let's say we have this Java class:
public class Foo
{
public int x;
public Foo()
{
x = 3;
}
}
And now we use it like this:
Foo foo = new Foo();
foo.x = 5;
Foo foo2 = foo;
foo2.x = 10;
System.out.println(foo.x);
The result would be, as you know, 10
. This is because the line:
Foo foo2 = foo;
Doesn't actually copy over the values of foo
to foo2
as it's done in C-like languages but all it does is change where the reference is pointing to. In other words, foo
can now change the state of foo2
.
In C on the other hand, this isn't the standard behavior.
For example, let's say we have this struct:
typedef struct
{
int x;
} Foo;
And we use it like we used the class in the Java example:
Foo foo;
foo.x = 5;
Foo foo2;
foo2 = foo;
foo2.x = 10;
printf("%d", foo.x);
The output is 5
instead of 10
. Huh? Why is that?
Well, my friend, that's because C's "objects" by default are the actual objects, not references to memory of that object like in Java. This means that the line:
foo2 = foo;
Does a full memory copy and thus just copies over the values from one struct to the other. This means that changing foo2
will not change foo
.
Okay, okay, but what if I want to change foo
through foo2
then??
That's where pointers come in handy, we can just point to the memory address of foo
:
Foo foo;
foo.x = 5;
Foo* foo2 = &foo;
foo2->x = 10;
printf("%d", foo.x);
Voila, you get your wanted 10
as you'd expect, just as in Java.
Every variable in Java (some exceptions apply) is a C pointer under the hood, the JVM just takes care of memory management for you (using malloc/new
and free/delete
).