I'm reading this book, and I found this code snippet in Chapter 14.
struct kobject *cdev_get(struct cdev *p)
struct module *owner = p->owner;
struct kobject *kobj;
if (owner && !try_module_get(owner))
kobj = kobject_get(&p->kobj);
p->owner // (*p).owner
&p->kobj // (*(&p)).kobj
p being defined as
struct cdev *p,
p is very much a "memory address" but it also has a type attached to it.
Since the expression
*ptr means "the object pointed to by
ptr", that also has the type attached, so you can logically do
ptr->member is identical to
(*ptr).member, it too is valid.
Bottom line is, your contention that "pointers [aren't] much more than memory addresses" is correct. But they are a little bit more :-)
In terms of
&ptr->member, do not read that as
Instead, as per C precedence rules, it is actually
&(ptr->member), which means the address of the member of that structure. And the actual member is not evaluated here, it's exactly the same as
&x not actually evaluating
x, instead simply giving its address.
These precedence rules are actually specified by the ISO C standard (C11 in this case). From
6.5 Expressions, footnote
The syntax specifies the precedence of operators in the evaluation of an expression, which is the same as the order of the major subclauses of this subclause, highest precedence first.
6.5.2 Postfix operators (the bit covering
->) comes before
6.5.3 Unary operators (the bit covering
&), that means
-> evaluates first.