3

I'm trying to understand how pointer arithmetic works in C. Say if I have two arrays, one static and the other dynamic like so,

int a[10];
int * b = malloc(sizeof(int) * 10); 

And lets assume each array has been initialized so that they both store numbers from 0 to 9 in order (well in the case for b, it stores the address to an array of numbers from 0 to 9 in order).

If we want to access the value 9 in array a using pointer arithmetic we can simply write *(a + 9). Similarly if we want to access the value 9 in array b, we can simply write *(b + 9).

However what confuses it me is how the computer is able to know when to dereference a variable. With the case for a, the computer treats the name as an address of the array a. However with b, it seems that the computer reads the address of b and also dereferences it additionally in order to get the base address of the array pointed by b.

Could someone please explain?

(Edit: specifically my confusion is related to how the names of both arrays are treated by the computer. For *(a + 9), the name a is read by the computer as an address of array a itself and added to (9 * 4). While for *(b + 9), b is instead read as the address stored in variable b.)

3
  • a is a constant. Like 12. A constant address. Sort of. But still an address. So *(b+9) is exactly the same arithmetic as *(a+9). The difference is the same as between int b=12; and then comparing b+5 and 12+5. Sure, b is a variable and 12 is not. Yet, it is the same arithmetic. So, same for your pointer. Sure b is a variable (containing an address), and a is not. Still, same arithmetic. (Switched a and b in previous comment. And realized 1 minutes too late to edit) Commented Jul 22 at 2:41
  • For your edit: a is not a variable. There is no address of a. It is stored nowhere. a[0], a[1], ... are stored somewhere in the memory. But not a. Commented Jul 22 at 2:46
  • "With the case for a, the computer treats the name as an address of the array a" -- sort of. In the scope of that declaration of a, C treats the symbol a as designating the array object it is associated with. Just like in the context of b, it always treats the symbol b as designating the pointer object it is associated with. There just are special rules for how arrays are handled, as your answers and other comments explain in more detail. There are special rules for a variety of other data types, too, though those for arrays are somewhat unusual. Commented Jul 22 at 14:12

2 Answers 2

6

With the case for a, the computer treats the name as an address of the array a. However with b, it seems that the computer reads the address of b and also dereferences it additionally in order to get the base address of the array pointed by b.

Not exactly. An array variable, when used in a expression, will decay into a pointer to its first member in most cases. That is what happens with a. In contrast, b is a pointer which contains the address of the first member of an array. No dereference happens until the * operator is applied.

In the case of *(a + 9) (which is exactly equivalent to a[9]), a first decays to a pointer whose value is the address of the first member of the array, i.e. the element with index 0. Then the addition applied to this pointer value results in a pointer to the member with index 9, then the dereference operator accesses that member.

Similarly in the case of *(b + 9) (which is exactly equivalent to b[9]), the value of b is the address first member of the dynamically allocated array, i.e. the element with index 0. Then the addition applied to this pointer value results in a pointer to the member with index 9, then the dereference operator accesses that member.

Sign up to request clarification or add additional context in comments.

6 Comments

Side question: « decay into a pointer to its first member in most cases » : in what case does it not? (I take you say "most", because there exist some rare counter example)
When the array is the operand of either the sizeof operator or the & operator. In the former case, it results in the size in bytes of the entire array, and in the latter case it results in a pointer to an array.
Oh, of course! Thanks. Btw, the & case may be important here: OP edit is probably (among other thing) referring to a being the same address as &a (not the same pointer, strictly speaking, but same address)
Exceptions: C23 § 6.3.2.1 3: Except when it is the operand of the sizeof operator, or typeof operators, or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue.
So, in the case when an array variable is statically declared, when accessing the elements the variable will always turn into a pointer to its first member?
Yes, except that nothing magic happens. The memory address where the array is stored also happens to be the memory address of its first element. There are no operations needed to do this "conversion".
4

Background: Static vs Dynamic Arrays in C

Static Array(s)

int a[10];
  • This is a statically allocated array.
  • Memory for 10 integers is allocated at compile-time.
  • You can access elements using a[index] or with pointer arithmetic: *(a + index).

Dynamic Array(s)

int *b = malloc(sizeof(int) \* 10);
  • This is a dynamically allocated array.
  • Memory is allocated at runtime on the heap.
  • b is a pointer to the first element of that memory block.
  • You also access elements with b[index] or *(b + index).

Pointer Arithmetic & De-referencing

Let’s go over this in parts.

What does *(a + 9) mean?

  • a is the name of the static array.

  • In C, the array name a decays to a pointer to its first element.

  • So a is treated like &a[0].

  • Adding 9: a + 9 means “move 9 int positions forward”.

  • De-referencing it: *(a + 9) gives you the 10th element (a[9]).

Same logic applies to b:

b is already a pointer to an int, so b + 9 moves 9 int positions forward.

*(b + 9) is the value at that position, just like b[9].

Conclusion: Whether it's a static or dynamic array, pointer arithmetic works the same way because both a and b are (or behave like) pointers to the start of the array.


The key confusion was this part:

"How does the computer know when to de-reference a variable?"

Clarification:

The computer doesn't “decide” when to de-reference——you do when you write *something.

a and b are both used as pointers in pointer arithmetic.

a is a special case: while it's not literally a pointer, it behaves like one when used in expressions.

b is an actual pointer. It holds the address to memory allocated by malloc.

So:

*(a + 9) = element at index 9 in a

*(b + 9) = element at index 9 in memory pointed to by b

The computer doesn’t “guess” anything--it simply follows your instructions exactly.


Example

Let’s assign values:

for (int i = 0; i < 10; i++) {
    a[i] = i;
    b[i] = i;
}

Now:

printf("%d\n", *(a + 9)); // prints 9
printf("%d\n", *(b + 9)); // also prints 9

They work the same way because pointer arithmetic is about addressing, not about how the array was allocated.


Let me know if you want more info :)

2 Comments

"This is a statically allocated array." --> depends: int a[10]; can be coded outside a function - making a static. int a[10]; inside a function is not static. int * b = malloc(sizeof(int) * 10); is not allowed outside a function. Inside a function, b like a are both non-static. int *b = malloc(sizeof(int) \* 10); and "This is a dynamically allocated array." deserves clarity. The This is the allocation and not b, which remains a pointer. (also typo with the extra \.)
Worth a +1 for "The computer doesn't “decide” when to de-reference——you do when you write *something." alone.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.