2

I am learning C in one of my classes. In one of my labs we need to use an array of structs.

One of my lab TAs told me I should be using array like this:

typedef struct person {

  int age;
  char *name;

} Person;

int main() {

  Person **people = (Person **)malloc(sizeof(Person *));

  Person *personA = (Person *)malloc(sizeof(Person));
  personA->age = 18;
  personA->name = "LeBron James";

  Person *personB = (Person *)malloc(sizeof(Person));
  personB->age = 20;
  personB->name = "Kobe Bryant";

  Person *personC = (Person *)malloc(sizeof(Person));
  personC->age = 21;
  personC->name = "Michael Jordan";

  people[0] = personA;
  people[1] = personB;
  people[2] = personC;

  printf("Name of first person is %s \n", people[0]->name);
  printf("Name of second person is %s \n", people[1]->name);
  printf("Name of second person is %s \n", people[2]->name);

The result is right. But what I do not understand why the pointer to a pointer (people) can behave like an array? (e.g people[0] = personA)

Can somebody please explain this to me?

Simon Adcock
  • 3,554
  • 3
  • 25
  • 41
Roy Li
  • 511
  • 1
  • 6
  • 14
  • That code is completely wrong. Don't listen to that TA. – SLaks Feb 19 '14 at 20:56
  • 1
    @Slaks , It's not the right way to do it, but its not completely wrong.. – UldisK Feb 19 '14 at 20:58
  • A pointer to a pointer behaves like an array of strings – Luis Alves Feb 19 '14 at 20:59
  • 1
    `Person **people = (Person **)malloc(sizeof(Person *));` If the number of persons is > sizeof(Person*), then you have a serious problem with the program. – PaulMcKenzie Feb 19 '14 at 20:59
  • Your specific code aside (as it seems to have an error or two... especially in the malloc()), there is no functional difference between indexing a pointer like an array instead of using pointer arithmetic. In fact, its often preferred because arrays are simple to look at and understand where as pointer arithmetic with a deference can look more confusing at first glance. But this is just a preference and will vary per task/programmer. See here: http://stackoverflow.com/questions/4622461/difference-between-pointer-index-and-pointer – MrHappyAsthma Feb 19 '14 at 21:00
  • @PaulMcKenzie sizeof() of a pointer isn't always 4 bytes (std 32-bit system)? Doesn't the code already have a problem? – gibertoni Feb 19 '14 at 21:04
  • @UldisK: No; he's writing into unallocated memory. – SLaks Feb 19 '14 at 21:05
  • @SLaks, O! Yeah, did noticed the missing `*3` in first malloc. My bad. – UldisK Feb 19 '14 at 21:07

2 Answers2

4

It works because it's basically syntactic sugar. The ANSI standard defines it:

The definition of the subscript operator [] is that E1[E2] is identical to (*(E1+(E2)))

So you can use the dereference operator *a to look a specific element at a address, or the a[b] operator to look at the bth element of a.

Your example code is, as others already pointed out, quite wrong.

Juri Robl
  • 5,614
  • 2
  • 29
  • 46
  • This does makes sense. An additional question: when using this way how do we know the size of this "array"? How do we know how many Persons have been stored? – Roy Li Feb 19 '14 at 21:16
  • @RoyLi, you *don't* unless you can find the `malloc` call. – Ian McLaird Feb 19 '14 at 21:19
  • @RoyLi Either you save it somwhere along with your "array" or you have to find where the memory gets allocated. – Juri Robl Feb 19 '14 at 21:22
  • @IanMcLaird Does that mean using array instead is better approach then? Because I think for most of time of size of the array should be an important thing to know. – Roy Li Feb 19 '14 at 21:24
  • 1
    @RoyLi It's only an array if you use it in the original function. If you try to pass an array as a parameter to a function, it gets converted to a pointer and you have to know the size again somehow (by passing it along to the function). – Juri Robl Feb 19 '14 at 21:28
  • 1
    The trouble with arrays is that you have to know how big they are ahead of time. Sometimes that's not practical. My C is a bit rusty, but I believe that if you use the array declaration syntax, you get a `const` pointer, so you can't re-`malloc` later if you need more space, which you *can* do if you use the pointer syntax. – Ian McLaird Feb 19 '14 at 21:29
  • 1
    @IanMcLaird Yes you are right, but since C99 you can at least declare arrays which size is not known at compile time, but depends on variables. But once the array is build, that's it. There's no changing that size. – Juri Robl Feb 19 '14 at 21:31
2
Person **people = (Person **)malloc(sizeof(Person *));

should be

Person **people = (Person **)malloc(sizeof(Person *) * 3);

Otherwise when you do this

people[0] = personA;
people[1] = personB;
people[2] = personC;

You write 8 bytes of data (assuming 32-bit) beyond what was allocated.

The reason you can use pointers like arrays is because they are more-or-less the same thing especially in straight-C.

for example:

int* numberlist = malloc(sizeof(int) * 10)

and

int numberlist[10];

are accessible the same way. The only difference is how the memory is allocated.

Lother
  • 1,699
  • 1
  • 12
  • 17