333

I am reading a book called "Teach Yourself C in 21 Days" (I have already learned Java and C# so I am moving at a much faster pace). I was reading the chapter on pointers and the -> (arrow) operator came up without explanation. I think that it is used to call members and functions (like the equivalent of the . (dot) operator, but for pointers instead of members). But I am not entirely sure.

Could I please get an explanation and a code sample?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Mohit Deshpande
  • 53,877
  • 76
  • 193
  • 251
  • 11
    qrdl is correct -- the "Learn X in Y days" books are generally garbage. In addition to K&R, I would also recommend Prata's "C Primer Plus", which goes into more depth than K&R. – J. Taylor Apr 29 '11 at 23:29
  • 120
    Get a better book. http://norvig.com/21-days.html – joshperry Apr 04 '10 at 16:35
  • There's a good explanation in this duplicate question: http://stackoverflow.com/questions/1238613/what-is-the-difference-between-the-dot-operator-and-in-c – Ben Nov 13 '12 at 00:48
  • 3
    @Steve That question deals with C++. Calling it a caused some confusion for me when I started reading about operator overloading in that other answer, which is not relevant in C. – Johann May 13 '13 at 19:42
  • 1
    @Belton The hard way series are bad, the guy says stuff that wasn't even relevant when he wrote the book and he doesn't care about good practices. – Bálint Jun 25 '17 at 19:51
  • 1
    The Peter Norvig link to "Teach Yourself Programming in Ten Years" is great, one of my favorites. Here's the comic version which explains how to do this in 21 days, which I unfortunately remembered as an XKCD but I was wrong: http://abstrusegoose.com/249 – Ted Aug 19 '17 at 21:42
  • 6
    He didn't say it was great book! It's just the book he happened to be reading for whatever reason. Maybe he is getting a lot from it, and he plans on reading a better one after he is done. Comments like these are annoying – uh_big_mike_boi Jun 28 '19 at 14:14

12 Answers12

582

foo->bar is equivalent to (*foo).bar, i.e. it gets the member called bar from the struct that foo points to.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • 62
    It's worth noting that if the dereference operator had been made postfix, as in Pascal, the `->` operator would not have been needed at all, as it would have been equivalent to the much more legible `foo*.bar`. The whole mess of typedef-ing functions with all the extra parentheses would have been avoided as well. – user207421 Jan 26 '15 at 06:04
  • 1
    So would `foo*.bar` and `(*foo).bar` both be equivalent to `foo->bar`? What about `Foo myFoo = *foo; myFoo.bar`? – Aaron Franke Jan 09 '19 at 08:21
  • 16
    No, he is just saying *IF* the creators of C would have made the dereference operator as POSTfix operator instead of PREfix then it would have been more easy. But it IS a prefix operator in C. – reichhart Mar 16 '19 at 09:59
  • @user207421 Coulfd you please give a short description or link to the "typedef-ing functions with all the extra parentheses" that you mention? Thanks. – RoG May 27 '19 at 06:24
  • 1
    @user207421 nah, it would cause more parents.. so far, there is priority of () and [] to the right above * to the left. if they all on one side, you'll have put more parents. Same in expressions, because of conflict with multiplication operator. Pascal ^ could be an option but it was reserved for bit operation, still more parents. – Swift - Friday Pie Oct 22 '19 at 06:32
  • But why is it necessary to explicitely dereference at all? Since no type can be both a pointer and a struct, why could you not have `foo.bar` automatically desugar to `(*foo).bar` if foo is known to be a pointer? (And then desugar to `(*(*foo)).bar` if `*foo` is it self a pointer, etc..) ? – b0fh Mar 01 '21 at 10:40
  • @b0fh There's no technical reason why the language designers couldn't have made dereferencing implicit in this case (Rust does it in this case and other cases as well), but they didn't. Possibly because they felt that all dereferences should be explicit in the code and/or because they wanted to keep the language rules as simple as possible. Note that C++ smart pointers have their own methods (like `use_count()` for shared_ptrs), so there it is necessary to distinguish between calling a method on the pointer vs. the pointee, but of course that didn't retroactively affect the design of C. – sepp2k Mar 01 '21 at 11:39
157

Yes, that's it.

It's just the dot version when you want to access elements of a struct/class that is a pointer instead of a reference.

struct foo
{
  int x;
  float y;
};

struct foo var;
struct foo* pvar;
pvar = malloc(sizeof(struct foo));

var.x = 5;
(&var)->y = 14.3;
pvar->y = 22.4;
(*pvar).x = 6;

That's it!

Hymns For Disco
  • 7,530
  • 2
  • 17
  • 33
Jack
  • 131,802
  • 30
  • 241
  • 343
  • 3
    Since pvar is uninitialised, how would you initialise it if you wanted pvar to point to a new struct, that is not `pvar = &var`? – CMCDragonkai Sep 26 '15 at 14:27
  • 1
    The question was specifically about C, which does not have classes or reference variables. –  Dec 19 '16 at 19:34
  • 1
    hmm shouldn't you do a malloc before writing to pvar struct foo* pvar; ?? pvar->y write to unallocated space! – Zibri Mar 21 '18 at 08:46
  • pvar initialization: Initialize all members manually to some defaults you want to have or use something like calloc() if zero fill would be fine for you. – reichhart Mar 16 '19 at 10:03
  • 5
    shouldn't it be: pvar = malloc(sizeof(struct foo)) or malloc(sizeof(*pvar))?? – Yuri Aps Jul 14 '19 at 02:01
47

I'd just add to the answers the "why?".

. is standard member access operator that has a higher precedence than * pointer operator.

When you are trying to access a struct's internals and you wrote it as *foo.bar then the compiler would think to want a 'bar' element of 'foo' (which is an address in memory) and obviously that mere address does not have any members.

Thus you need to ask the compiler to first dereference whith (*foo) and then access the member element: (*foo).bar, which is a bit clumsy to write so the good folks have come up with a shorthand version: foo->bar which is sort of member access by pointer operator.

Lukasz Matysiak
  • 891
  • 7
  • 11
36

a->b is just short for (*a).b in every way (same for functions: a->b() is short for (*a).b()).

Peter Alexander
  • 53,344
  • 14
  • 119
  • 168
21

foo->bar is only shorthand for (*foo).bar. That's all there is to it.

Matti Virkkunen
  • 63,558
  • 9
  • 127
  • 159
15

Well I have to add something as well. Structure is a bit different than array because array is a pointer and structure is not. So be careful!

Lets say I write this useless piece of code:

#include <stdio.h>

typedef struct{
        int km;
        int kph;
        int kg;
    } car;

int main(void){

    car audi = {12000, 230, 760};
    car *ptr = &audi;

}

Here pointer ptr points to the address (!) of the structure variable audi but beside address structure also has a chunk of data (!)! The first member of the chunk of data has the same address than structure itself and you can get it's data by only dereferencing a pointer like this *ptr (no braces).

But If you want to acess any other member than the first one, you have to add a designator like .km, .kph, .kg which are nothing more than offsets to the base address of the chunk of data...

But because of the preceedence you can't write *ptr.kg as access operator . is evaluated before dereference operator * and you would get *(ptr.kg) which is not possible as pointer has no members! And compiler knows this and will therefore issue an error e.g.:

error: ‘ptr’ is a pointer; did you mean to use ‘->’?
  printf("%d\n", *ptr.km);

Instead you use this (*ptr).kg and you force compiler to 1st dereference the pointer and enable acess to the chunk of data and 2nd you add an offset (designator) to choose the member.

Check this image I made:

enter image description here

But if you would have nested members this syntax would become unreadable and therefore -> was introduced. I think readability is the only justifiable reason for using it as this ptr->kg is much easier to write than (*ptr).kg.

Now let us write this differently so that you see the connection more clearly. (*ptr).kg(*&audi).kgaudi.kg. Here I first used the fact that ptr is an "address of audi" i.e. &audi and fact that "reference" & and "dereference" * operators cancel eachother out.

71GA
  • 1,132
  • 6
  • 36
  • 69
12
struct Node {
    int i;
    int j;
};
struct Node a, *p = &a;

Here to access the values of i and j we can use the variable a and the pointer p as follows: a.i, (*p).i and p->i are all the same.

Here . is a "Direct Selector" and -> is an "Indirect Selector".

JgWangdu
  • 319
  • 2
  • 9
1

I had to make a small change to Jack's program to get it to run. After declaring the struct pointer pvar, point it to the address of var. I found this solution on page 242 of Stephen Kochan's Programming in C.

#include <stdio.h>

int main()
{
  struct foo
  {
    int x;
    float y;
  };

  struct foo var;
  struct foo* pvar;
  pvar = &var;

  var.x = 5;
  (&var)->y = 14.3;
  printf("%i - %.02f\n", var.x, (&var)->y);
  pvar->x = 6;
  pvar->y = 22.4;
  printf("%i - %.02f\n", pvar->x, pvar->y);
  return 0;
}

Run this in vim with the following command:

:!gcc -o var var.c && ./var

Will output:

5 - 14.30
6 - 22.40
Rich Vogt
  • 93
  • 1
  • 9
1
#include<stdio.h>

int main()
{
    struct foo
    {
        int x;
        float y;
    } var1;
    struct foo var;
    struct foo* pvar;

    pvar = &var1;
    /* if pvar = &var; it directly 
       takes values stored in var, and if give  
       new > values like pvar->x = 6; pvar->y = 22.4; 
       it modifies the values of var  
       object..so better to give new reference. */
    var.x = 5;
    (&var)->y = 14.3;
    printf("%i - %.02f\n", var.x, (&var)->y);

    pvar->x = 6;
    pvar->y = 22.4;
    printf("%i - %.02f\n", pvar->x, pvar->y);

    return 0;
}
Tommy Andersen
  • 7,165
  • 1
  • 31
  • 50
Gopal Rao
  • 11
  • 1
1

The -> operator makes the code more readable than the * operator in some situations.

Such as: (quoted from the EDK II project)

typedef
EFI_STATUS
(EFIAPI *EFI_BLOCK_READ)(
  IN EFI_BLOCK_IO_PROTOCOL          *This,
  IN UINT32                         MediaId,
  IN EFI_LBA                        Lba,
  IN UINTN                          BufferSize,
  OUT VOID                          *Buffer
  );


struct _EFI_BLOCK_IO_PROTOCOL {
  ///
  /// The revision to which the block IO interface adheres. All future
  /// revisions must be backwards compatible. If a future version is not
  /// back wards compatible, it is not the same GUID.
  ///
  UINT64              Revision;
  ///
  /// Pointer to the EFI_BLOCK_IO_MEDIA data for this device.
  ///
  EFI_BLOCK_IO_MEDIA  *Media;

  EFI_BLOCK_RESET     Reset;
  EFI_BLOCK_READ      ReadBlocks;
  EFI_BLOCK_WRITE     WriteBlocks;
  EFI_BLOCK_FLUSH     FlushBlocks;

};

The _EFI_BLOCK_IO_PROTOCOL struct contains 4 function pointer members.

Suppose you have a variable struct _EFI_BLOCK_IO_PROTOCOL * pStruct, and you want to use the good old * operator to call it's member function pointer. You will end up with code like this:

(*pStruct).ReadBlocks(...arguments...)

But with the -> operator, you can write like this:

pStruct->ReadBlocks(...arguments...).

Which looks better?

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
smwikipedia
  • 61,609
  • 92
  • 309
  • 482
1
#include<stdio.h>
struct examp{
int number;
};
struct examp a,*b=&a;`enter code here`
main()
{
a.number=5;
/* a.number,b->number,(*b).number produces same output. b->number is mostly used in linked list*/
   printf("%d \n %d \n %d",a.number,b->number,(*b).number);
}

output is 5 5 5

prashanth
  • 11
  • 1
0

Dot is a dereference operator and used to connect the structure variable for a particular record of structure. Eg :

struct student
    {
      int s.no;
      Char name [];
      int age;
    } s1,s2;

main()
    {
      s1.name;
      s2.name;
    }

In such way we can use a dot operator to access the structure variable

Duncan C
  • 128,072
  • 22
  • 173
  • 272
divya
  • 9
  • 1
  • 6
    What value does this add? The example is a bit poor compared to the other answers which actually compare it against `->`. Also this question has been answered for 4.5 years already. – EWit Oct 08 '14 at 16:15