5

I've just started with C and I'm trying to understand the basics. Plenty of tutorials will tell you things and have you believe it without any real explanation and there are no answers on that I can find that are human readable.

In the following:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  int *a;

  a = malloc(5 * sizeof(int));

  a[2] = 4;

  printf("%d\n", a[0]); // Prints 0
  printf("%d\n", a[2]); // Prints 4

  return 0;
} 

I have not explicitly declared int *a as a pointer to an array, but if I allocate it some memory, I can then use a like I had declared it as an array. Is declaring a pointer with square brackets just a shortcut for what I've done below? Are square brackets actually doing some pointer arithmetic?

Cheeky Second question

Why is the memory address assigned to a and not *a?

oorst
  • 909
  • 2
  • 7
  • 29
  • 1
    *Is declaring a pointer with square brackets just a shortcut for what I've done below? Are square brackets actually doing some pointer arithmetic?* Maybe and yes, respectively. *Why is the memory address assigned to `a` and not `*a`?* Because you wrote `a = malloc(...)`, not `*a = malloc(...)`. – Frédéric Hamidi Apr 14 '15 at 01:36
  • 2
    Learn from a book. [Here](http://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list) are some great recommendations. – M.M Apr 14 '15 at 01:37
  • 2
    The normal books/tutorials generally make this harder than needed. `int *a;` declares pointer `a` of type `int` that presently points nowhere (it itself has an address, but it contains no address of anything else -- i.e. it is an empty pointer). `malloc` reserves a block of memory and returns the starting address to the block. `a` now contains the starting address to the new block. `a[2]` which is the same as `*(a + 2)` simply says get the value **at address** `a + 2`. `a` being declared as type `int` knows an `int` is `4-bytes` so it provides the correct address to the 3rd `int` in memory. – David C. Rankin Apr 14 '15 at 01:40
  • Thanks for prompt replies. I've got a few of those books. I guess I should pay more attention. – oorst Apr 14 '15 at 01:55
  • In all seriousness, the problem is that the real answers aren't going to make sense until you reach a certain level of understanding, so trying to "understand the basics" in tortuous detail isn't very productive when you're starting out. That's why they're not "human readable" to you. It's better to just plough through and accept a shallow level of understanding for a while, until enough practice makes it all start to click. – Crowman Apr 14 '15 at 01:59
  • I know what you mean Paul. I do try things out before I go searching or asking here. Though sometimes things can be so opaque to a newcomer that its hard to know if what you think you understand is even true. I don't know. I'm just glad you're all here to help. :) – oorst Apr 14 '15 at 03:19
  • I found a great explanation of pointers. The introductory chapter of O'Reilly Understanding and using C pointers. You can it for free [here](http://cdn.oreillystatic.com/oreilly/booksamplers/9781449344184_sampler.pdf) – oorst Apr 15 '15 at 01:45

5 Answers5

13

I have not explicitly declared int *a as a pointer to an array, but if I allocate it some memory, I can then use a like I had declared it as an array. Is declaring a pointer with square brackets just a shortcut for what I've done below?

Similar, but no. int *a declares a as pointer to int. int b[5] allocates space for 5 ints, declares b as constant pointer to int an array-of-int reference (which can in most cases be treated as a constant pointer to int), and defines b to be the pointer to the allocated space. Thus, int b[5] is doing way more than int *a, which also means int *a is more flexible than int b[5]. For example, you can increment a, but not b.

malloc allocates memory on heap. int b[5], if a global variable, will be in the program's data segment (i.e. it is literally compiled into the executable). If local, it will be allocated on stack. Again, similar, but different.

Are square brackets actually doing some pointer arithmetic?

In declaration, no. When you use pointer variables, yes: x[y] is identical to *(x + y). So a[1] is the same as *(a + 1), which is the same as *(1 + a), which is again the same as 1[a] (but please don't use this last one).

Why is the memory address assigned to a and not *a?

Cheeky answer for a cheeky question: Because you wrote a = ... and not *a = ....

EDIT: John Bode gave a useful pointer (heh): constant pointers and array references are not the same thing, although they are pretty damn similar. For one thing, sizeof(...) will give a different result.

Amadan
  • 191,408
  • 23
  • 240
  • 301
  • 1
    Good explanation. Now I see its important remember that the asterisk indicates a type. – oorst Apr 14 '15 at 01:52
  • 1
    `int *` indicates a type, in declarations. `*a` in `*a = ...` indicates a reference, meaning "write into the location pointed to by `a`" as opposed to "write into `a`" that is `a = ...`. – Amadan Apr 14 '15 at 01:56
  • Nit - `b` is not a pointer. It is an array expression that *is converted to* a pointer expression in most circumstances. It does not store a pointer value. – John Bode Apr 14 '15 at 03:43
  • @Amadan, how is `1[x]` the same as `a[1]`? Even if you meant `1[a]` instead of `1[x]`, I'd still have the same question. – ma11hew28 Apr 15 '20 at 12:12
  • @ma11hew28 Right, there's a typo. I'll add to what I did say, for clarity, and correct it: "`a[1]` is the same as `*(a + 1)`, which is the same as `*(1 + a)`, which is again the same as `1[a]`". – Amadan Apr 15 '20 at 12:17
  • @Amadan, oh, OK. I get it now. Thanks. :-) – ma11hew28 Apr 15 '20 at 12:32
7

Are square brackets actually doing some pointer arithmetic?

Yes. Brackets can be applied to any pointer, not just arrays. They provide a shorthand for pointer arithmetic and pointer dereferencing. Your code is essentially doing this:

int *a = malloc(5 * sizeof(int));

*(a+2) = 4;

printf("%d\n", *(a+0));
printf("%d\n", *(a+2));

Which is actually doing the equivalent of this:

int *a = malloc(5 * sizeof(int));

*((int*)(((unsigned long)a)+(2*sizeof(int)))) = 4;

printf("%d\n", *((int*)(((unsigned long)a)+(0*sizeof(int)))));
printf("%d\n", *((int*)(((unsigned long)a)+(2*sizeof(int)))));
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
6

Are square brackets actually doing some pointer arithmetic?

a[b] is equivalent to *(a + b). So yes, it's pointer arithmetic. It first offsets the pointer by b and then accesses that memory location, just like an array.

Why is the memory address assigned to a and not *a?

The memory address is stored in a because a is the pointer and pointers store memory addresses. *a is the data at that memory address.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
3

Is declaring a pointer with square brackets just a shortcut for what I've done below?

Of course not! An array and a pointer are two completely different things. int a[5];, here a is of type int[5] while in int *b; b's type is int*. Former decays into latter in many circumstances. This is called arry decaying. Read more about it here.

Are square brackets actually doing some pointer arithmetic?

Yes, when you do a[3] what really happens is *(a + 3) i.e. a decays into int* and the rest is just pointer arithmetic i.e. to the address of the first element of a, 3 * sizeof(int) is added.

Why is the memory address assigned to a and not *a?

I think you're confusing between an identifier and an operator applied to a variable. A pointer is just another variable; all variable names should follow the rules of an identifier i.e. from the standard:

An identifier is an arbitrarily long sequence of letters and digits.

While the operator * dereferences the pointer to which it is applied and returns a result based on the pointer type. So the memory address is always assigned to a, the variable. The value at the memory address pointed-to by a would always be referred to by *a.

Community
  • 1
  • 1
legends2k
  • 31,634
  • 25
  • 118
  • 222
0

Why is the memory address assigned to a and not *a?

Because malloc() returns an address which you are assigning to (storing in) pointer vairable a using = operator.

Normally we store these address returned by malloc() in a pointer variable like how you have done with your statement a = malloc(5 * sizeof(int));.

If you try to store the address returned by malloc() in a non-pointer variable then you will have issues.

sps
  • 2,720
  • 2
  • 19
  • 38