C is a low-level programming language and the behavior around using memory through C variables might be a bit confusing to programmers coming from languages with higher level abstractions.
Let's break down your main
function first.
int p[32];
When a function is called in your program, it gets allocated some section in RAM assigned to your process. This section is called the stack. With this statement, you're telling the compiler that your function (main
) needs space for 32 integers in stack. Further statements you make with the variable p
will be operating on this space reserved for the 32 integers.
Note that, you're not telling anything to the compiler on how this portion of memory assigned for p
is initialized. So all these bytes allocated for 32 integers will store whatever they contained before your function is called.
Let's look at the next one.
int *q = p + 5;
This is very similar but now you are asking for some memory in stack with a size that can fit "a pointer to a integer". Pointer is the C abstraction for bare "memory address with a type". So this space will be used to store addresses in memory, and these addresses will refer to another space in RAM that is intended to store integers.
You are also telling the compiler to initialize the stack space for q
, with the value of p + 5
. Unlike the space for the 32 integers above (p
), the space for q
will be initialized right after your function is called.
The expression p + 5
is applying what is called "pointer arithmetic". This is used to take an address in RAM, and go up or down based on whatever offset we need. Remember, p
was an array and arrays in C work like pointers (addresses) when they take part in pointer arithmetic. Thus, what p + 5
really means is the "address that is 5 integers after the first address p points to". This ends up being the "pointer to the sixth element of p
" (first being p[0]
), in other words, the address of p[5]
.
f(q);
In this statement, you are passing the address stored in q
, which happened to be the address of the sixth element in p
. The function f
in return assigns 35
to the location in RAM pointed by this address, hence changing the integer that would be accessed by p[5]
to the integer value of 35
.
Right at this point, p[5]
is the only element within p
that has an initialized value. All other integers in p
will continue to store what they held before main
was called during the initialization of your program.
printf( "%d", p[5] );
When the execution returns back to main
, the integer that can be accessed by p[5]
is now set to 35
, and that is exactly what you expect to see with this printf
statement.