-1
#include <stdio.h>

int f( int *x ) {
    *x = 35;
}

int main( int argc, char *argv[] ) {
    int p[32];
    int *q = p + 5;

    f( q );

    printf( "%d", p[5] );

    return 0;    
}

Can you please explain why the output is 35?

I tried to output the value of p by using printf("%d", p) and right after int p[32], it returned a -2077686688.

I think it just simply because I didn't assign any value to the p[32] array yet, so it just returned a random number.

However, the part that confuses me the most is *q = p + 5

How can an array do that?

Since there is no value in the p array, how can it just return its size in this expression?

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Time
  • 65
  • 1
  • 5
  • 2
    There is no such thing as an "empty array". –  Jan 26 '19 at 22:45
  • *"how can it just return its size in this expression"* - it doesn't. The size of the array is not involved here at all. `int *q = p + 5;` is the same as `int *q = &p[5];` – UnholySheep Jan 26 '19 at 22:47
  • 1
    the reason printf("%d", p) printed -2077686688 is because that is the memory address (as a signed integer) of the location where the array p is stored. – Bob Shaffer Jan 26 '19 at 23:00
  • Adding to Bob's comment, to output an address/pointer use `%p`. That is, try this: `printf("p=%p q=%p p[5]=%d *q=%d\n",p,q,p[5],*q)` – Craig Estey Jan 26 '19 at 23:11

3 Answers3

3

In your code, int p[32] sets aside an array of size 32 that can be referenced using p. When you define q to be p + 5, you are assigning q to be a pointer to the 6th (1-indexed) element in memory starting from wherever p points to.

When you pass q to f(), the value at q is set to 35 from whatever was there before (uninitialized memory). Since q points to the same location as p[5], p[5] will be 35 since that is the value set at the location in memory by f().

Andrew Fan
  • 1,313
  • 5
  • 17
  • 29
  • 1
    `p` is **not** a pointer and `q` points to the **sixth** element – UnholySheep Jan 26 '19 at 22:55
  • 3
    @BobShaffer no, `p` is an **array**. Arrays and pointers are different things – UnholySheep Jan 26 '19 at 22:59
  • I've adjusted the wording of my answer. In regards to the fifth/sixth element, it depends on whether you count from zero or from one. I've adjusted to 6th (1-indexed) which assumes that `p[0]` is the first element. – Andrew Fan Jan 26 '19 at 23:00
  • @UnholySheep There is really no difference in C. A variable defined as an array is a pointer to an array. The only difference between "int p[32]" and "int *p" is that the latter does not allocate memory for the array or initialize the pointer. – Bob Shaffer Jan 26 '19 at 23:04
  • 2
    @BobShaffer you are wrong: https://stackoverflow.com/questions/4607128/in-c-are-arrays-pointers-or-used-as-pointers If `p` were a pointer then `sizeof(p)` would return the size of a pointer (which it doesn't) – UnholySheep Jan 26 '19 at 23:06
  • @BobShaffer The are many important differences between an array object and a pointer object. True many similarities too, yet they are _different_. In particular, it is the lack of difference clarity that is at the root of OP's question. – chux - Reinstate Monica Jan 27 '19 at 00:19
  • I do see that there are, in fact, two operators that are handled differently. There is, indeed, a subtle difference. It would seem, then, that (&p == &p[0] == p) for **arrays** but not **pointers**, which I actually never even realized, or even thought about. I never use sizeof() on anything other than a type, and would generally discourage it, so I never would have realized a difference there. At any rate, I stand corrected. – Bob Shaffer Jan 27 '19 at 00:36
  • @BobShaffer `&p` =/= `p` or `&p[0]` for arrays either. – Ajay Brahmakshatriya Jan 27 '19 at 01:15
  • @AjayBrahmakshatriya according to that link, the only real difference is in the two operators "&" and "sizeof". UnholySheep demonstrated the sizeof difference. I have, in fact, now confirmed that for an array declared as "int foo[11]", &foo, foo, and &foo[0] are all equal. – Bob Shaffer Jan 27 '19 at 03:55
0

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.

Tugrul Ates
  • 9,451
  • 2
  • 33
  • 59
-1

int main() {
    int p[32] = { 0 };
    // initializing array with zeros; no mode random values in array!

    int *q = p + 5;

    if (&p == &p[0] && &3[p] == &p[3]) {
        printf("Sick sad C world\n");
    }
    /* We can say that there's no such thing as 'array' in C!
     * (actually, C has arrays)
     * but C arrays are 'thin layer'; try to compare JS Array and C Arrays
     * See this: https://stackoverflow.com/a/381549/10972945
     * So: p[0] == *(p + 0) == *p
     * 'Array' is an address of it's zero element! */

    printf(
        "p located at %p\n"
        "p + 1 located at %p\n"
        "p + 5 located at %p\n"
        "Size of int (in bytes) is %zu\n",
        (void*) p,
        (void*) (p + 1),
        (void*) (p + 5),
        sizeof(int) 
    );
    /*  Try to run this code and substract addresses, one from another.

            p located at 0x7ffee3e04750
            p + 1 located at 0x7ffee3e04754
            p + 5 located at 0x7ffee3e04764
            Size of int (in bytes) is 4

        See:
        address of (p + 1) - address of p
            == 0x7ffee3e04754 - 0x7ffee3e04750
            == 4 == sizeof(int)
        address(p + 5) - address(p)
            == 0x7ffee3e04764 - 0x7ffee3e04750
            == 0x14 == 20 == 5 * sizeof(int)
     */
}
Alex Python
  • 329
  • 1
  • 8