-1

I am a little bit confused, why this code works:

struct product{
    double price;
    int quantity;
    char name[1];
}p2;
printf("%d",p2.quantity);

While this doesn't work:

struct product *p3=&p2;
printf("%d",p3.quantity);

I mean both p2 and p3 are pointers to the same object so why we need to write printf("%d",p3->quantity); in the second case to let it work.

Defender
  • 17
  • 4
  • 1
    p2 is not a pointer. – brhans Jun 26 '19 at 21:38
  • 1
    `p2` is not a pointer, only `&p2` will result in one. – Stephan Lechner Jun 26 '19 at 21:38
  • 2
    I think you need the C primer: the `.` dot is for a `struct` member. The `->` arrow is for a pointer target. – Weather Vane Jun 26 '19 at 21:38
  • How p2 is not a pointer then what it is? I read that when creating a pointer we use * @brhans – Defender Jun 26 '19 at 21:39
  • 3
    Possible duplicate of [Arrow Operator vs. Dot Operator](https://stackoverflow.com/questions/10036381/arrow-operator-vs-dot-operator): The 'arrow' operator is "syntactic sugar". `bar->member` is the same as `(*bar).member`. The former is just more readable. So you *can* use `printf("%d",(*p3).quantity);`, should you wish to ;) – paulsm4 Jun 26 '19 at 21:40
  • If `p` is a struct, you access its members using a dot, like this: `p.quantity`. If `ps` is a pointer to a struct, you access its members using `->`, like this: `ps->quantity`. But if you don't have a solid understanding of pointers (and structs, for that matter) this may not make much sense. But know this: pointers and structs are completely different, orthogonal concepts. You can (and often do) have one without the other. You can use ordinary structs all day long with no pointers in sight, and you can have pointers to all sorts of things other than structs. – Steve Summit Jun 26 '19 at 21:42
  • @paulsm4 Still I don't understand. Both p2 and p3 are pointers so... – Defender Jun 26 '19 at 21:44
  • 5
    No. "p2" is *NOT* a pointer. Q: "How p2 is not a pointer then what it is?" A: p3 ;) A struct is a struct, a pointer to a struct is a pointer ... and never the twain shall meet ;) – paulsm4 Jun 26 '19 at 21:45
  • @Defender how is p2 a pointer? – Water Jun 26 '19 at 21:47
  • `p2` is a `struct product`. `p3` is a pointer to a `struct product`. `&p2` is a pointer to the specific `struct product` instance `p2` which you can use to assign to `p3` (if you want to). – brhans Jun 26 '19 at 21:48
  • Isn't that similar to C#? Every class object (struct) have a pointer to it? Isn't everything have a pointer to it? – Defender Jun 26 '19 at 21:50
  • 1
    @Defender: C doesn't have classes, nor does it create pointers for everything. A `struct` type is simply a collection of data. `p2` is an *instance* of type `struct product` - it doesn't point to anything, it *is* the thing. If you examine it in memory, you'll see space for a `double` followed by an `int` followed by a `char`. – John Bode Jun 26 '19 at 22:01
  • Maybe you're confused because array variables decay to pointers. But that's only true for arrays, not structs or any other type. – Barmar Jun 26 '19 at 22:13

6 Answers6

2

To answer your confusion about C "pointers":

  • C != C#.

  • C doesn't have .Net reference objects.

  • And although you can take the address of any C object (a char, an int, a struct, etc) to turn it into a pointer, that object isn't necessarily itself a pointer.

Look here:

Difference between variable and data object in C language?

In C, an object is anything that takes up storage. C 2011 online draft.

An lvalue is an expression that designates an object such that the contents of that object may be read or modified (basically, any expression that can be the target of an assignment is an lvalue). While the C standard doesn't define the term variable, you can basically think of it as any identifier that designates an object...

A pointer is any expression whose value is the location of an object. A pointer variable is an object that stores a pointer value.

paulsm4
  • 114,292
  • 17
  • 138
  • 190
1

C has value types, including for struct aggregates. An expression that has some struct type isn't a reference to a structure, but that structure itself.

In your program, p2 isn't a pointer, but the name of a variable (storage location) which holds the entire structure directly. The value of p2 is that structure itself.

p3 holds a pointer to the structure; p3's value is a data type which indicates the location of value in memory.

The C language uses different operators for referencing members through a pointer to a struct/union versus directly.

That doesn't have to be the case. The compiler has enough information that p2.quantity and p3.quantity could equally work. However, it is simply not so. Decades ago, Dennis Ritchie designed things so that if p is a pointer to a structure, then accessing a member requires dereferencing p first as *p to obtain the structure value that p points to, and then use member selection on that value: (*p).member. This requires parentheses because the postfix . operator has a higher precedence than the unary *. Because that is verbose, Ritchie, or maybe someone else, invented a shorthand notation: the -> operator. The operator is syntactic sugar: p->member means the same thing (*p).member. That person either didn't realize that p.member could just be made to work (dot applied to a struct/union pointer can select the member), or more likely rejected that design (probably due to the ideology that "pointer dereferencing should be loudly visible in the code").

Usually when we see z = x.y; in a C program, we don't worry about memory safety, but z = x->y raises a red flag: is x a valid pointer here? Thus the designer had a point. The -> operator highlights that a pointer is being dereferenced, which is dangerous in a language with manual memory management like C.

Some other languages in this family have explicit dereferencing. In Pascal, if ptr is a pointer to a record, then the syntax for getting a member is ptr^.memb. You cannot just use ptr.memb. However, note how in Pascal the pointer dereference ptr^ is a postfix operator, so there is no precedence issue here; no parentheses are required as in (ptr^).memb.

Kaz
  • 55,781
  • 9
  • 100
  • 149
0

There is obviously a confusion here regarding what is a pointer and what is not. I will rewrite the code a little to highlight the difference -

struct product{
    double price;
    int quantity;
    char name[1];
};

struct product p2;
struct product *p3;

p3 = &p2;

This code is equivalent to what you have. Now notice p3 is declared with * (the type is struct product *). This means that p3 is a pointer.

p2 is declared with the type struct product (which is not a pointer). It is a struct.

You already know that we use . when using members from struct and -> when getting members from a pointer.

Coming to &p2. The & operator returns a pointer to and object. So &p2 is of a type struct product* (pointer) and hence can be assigned to p3 which is also a pointer.

Because of this you can also do (&p2)->quantity (notice the ->) because (&p2) is a pointer.

Finally, just like the & operator we also have the * which returns the object from a pointer.

So you can use (*p3).quantity (notice the . instead of ->). Here p3 is a pointer, but the * operator returns a struct and now you can use ..

Ajay Brahmakshatriya
  • 8,993
  • 3
  • 26
  • 49
  • Here: https://www.tutorialspoint.com/cprogramming/c_operators.htm I read that * is Pointer to a variable. while & Returns the address of a variable. – Defender Jun 26 '19 at 22:01
  • Yes, `*` is used for declaring pointer types. There is no `*` in the declaration of `p2`. So it is not a pointer. – Ajay Brahmakshatriya Jun 26 '19 at 22:14
0

I am a little bit confused, why this code works:

struct product{
    double price;
    int quantity;
    char name[1];
}p2;
printf("%d",p2.quantity);

While this doesn't work:

struct product *p3=&p2;
printf("%d",p3.quantity);

p2 is declared as a struct product. That's a structure object itself, not a pointer to one. One uses the . operator to access its members, as with p2.quantity in the first snippet.

On the other hand, p3 is declared as a pointer to type struct product, which is to say as a struct product *. It is initialized to point to the object identified by p2 -- that's what &p2 is, a pointer value representing the address of p2. The . is not applicable to pointers, hence your second snippet is wrong, but you may use the -> operator with a pointer to a structure or union to access the members of the pointed-to object.

I mean both p2 and p3 are pointers to the same object

They absolutely are not. The initialization p3 = &p2 sets p3 to point to p2, not to be equal to p2. This is the essential difference between p2's type, struct product, and p3's type struct product *.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
0

The . member selection operator requires that its left operand have a struct or union type. The expression p2 has type struct product, so it's a valid operand for the . operator. The expression p3 has type struct product * (pointer to struct product), so it is not a valid operand for the . operator.

The -> member selection operator requires that its left operand have a pointer to struct or union type. The expression p3 has type struct product *, so it's a valid operand for the -> operator. The -> performs an implicit deference of the pointer - a->b is equivalent to writing (*a).b.

The expression p2 is not a pointer - it does not have pointer type, so it is not a valid operand for the -> operator. p2 doesn't point to anything, it is the thing.

Here's a bit of code I wrote to illustrate the difference between p2 and p3 - dumper is a little utility I wrote to dump the contents of objects in memory:

#include <stdio.h>
#include "dumper.h"
#include <stdint.h>

int main( void )
{
  struct product
  {
    double price;
    int quantity;
    char name[1];
  };

  struct product p2 = {1.00, 1, .name[0] = 'A'}; 
  struct product *p3 = &p2;

  char *names[] = {"p2", "p3"};
  void *addrs[] = {&p2, &p3};
  size_t sizes[] = { sizeof p2, sizeof p3};

  dumper( names, addrs, sizes, 2, stdout );

  return 0;
}

Here's the output:

       Item         Address   00   01   02   03
       ----         -------   --   --   --   --
         p2  0x7ffeec515a68   00   00   00   00    ....
             0x7ffeec515a6c   00   00   f0   3f    ...?
             0x7ffeec515a70   01   00   00   00    ....
             0x7ffeec515a74   41   00   00   00    A...

         p3  0x7ffeec515a60   68   5a   51   ec    hZQ.
             0x7ffeec515a64   fe   7f   00   00    ....

The struct object p2 lives at address 0x7ffeec515a68 and contains a double object (0x3ff0000000000000, which is the binary representation of the floating point value 1.0, starting at address 0x7ffeec515a68), followed by an int object (0x00000001, starting at address 0x7ffeec515a70) followed by a single char object (0x41, which is the character code for 'A', starting at address 0x7ffeec515a74).

The pointer object p3 lives at address 0x7ffeec515a60 and contains the address of p2 (0x7ffeec515a68).

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

In the language described by the 1974 C Reference Manual, the -> operator would take the value of the left operand as a byte address, add the offset of the field named by the right operand, and treat whatever is at the resulting address as an object of the field's type", while the "." operator would do likewise but with the address of the left operand. Although C compilers have for decades required that the left operand to . be a struct or union containing the named field, and the left operand of -> be a pointer to a structure or union containing the named field, the 1974 C Reference Manual imposed no such requirements, instead defining the behavior of those operators in ways which didn't depend upon the type of the left operand. According to that manual, if there existed some structure type S with a field named foo [note that multiple structure types could only have fields with matching names if they were part of a Common Initial Sequence] then 1234->foo would be processed in a way equivalent to ((S volatile *)1234)->foo.

Note that in the 1974 language, it would be possible for both foo.bar and foo->bar to have different valid meanings. Outside of contrived situations it would be rare for both to be useful, but if both operations used the same operator it wouldn't always be possible for a compiler to pick the useful one.

supercat
  • 77,689
  • 9
  • 166
  • 211