0

In this question:

asker raise a question about #define offsetof(st, m) \ ((size_t) ( (char *)&((st *)(0))->m - (char *)0 )) deference null(0) pointer and there is no segment fault.

JaredPar's answer pointed out:

The -> operator is used above but it's not used to access the value. Instead it's used to grab the address of the value. Here is a non-macro code sample that should make it a bit clearer

SomeType *pSomeType = GetTheValue();
int* pMember = &(pSomeType->SomeIntMember);

The second line does not actually cause a dereference (implementation dependent). It simply returns the address of SomeIntMember within the pSomeType value.

My question is how to prove int* pMember = &(pSomeType->SomeIntMember); just assigns SomeIntMember's address to pMember without deferencing pSomeType.

Is there any iso c++ standard? or is there any method?

EDIT:

Although the question I posted is about c, I want c++ answer, so I tag this question c++.

If there is something in c++ standard, it is better.

Else, I hope to see something to prove JaredPar's conclusion, e.g., xaxxon posted the assembly, or how specific compiler implement.

If answers hold int* pMember = &(pSomeType->SomeIntMember); does make deference to pSomeType, then why offsetof's implemention(#define offsetof(st, m) ((size_t) ( (char *)&((st *)(0))->m - (char *)0 ))) is valid?

UPDATE:

Thanks for all the comments and answers, now I understand that #define offsetof(st, m) ((size_t) ( (char*)&((st*)(0))->m - (char)0)) is one of implementions in c, not in c++.

Also, I find msvc's implemention, #define offsetof(s,m) ((size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m))), but it is a little complex to me, can someone give an expression? Thanks in advance.

Chen Li
  • 4,824
  • 3
  • 28
  • 55
  • I'm pretty sure that one of those closing parenthesis is in the wrong place... – Mooing Duck Jul 30 '17 at 04:52
  • The second line gets the address of the SomeIntMember member of pSomeType. All its actually doing is a pointer offset from pSomeType base on where the compiler knows SomeIntMember is based on the complete type definition of SomeType. – xaxxon Jul 30 '17 at 05:10
  • 1
    https://godbolt.org/g/wSEBUU See how the first call uses the contents of RAX (that's the register holding the A*) directly (lines 16-18) but the second call offsets it by 4 (line 20) because that's how many bytes the b member is from the start of the object. – xaxxon Jul 30 '17 at 05:16
  • That question and answer regards a different language than your question. – molbdnilo Jul 30 '17 at 07:26
  • 1
    Are you asking about the language as defined by the standard? Or are you asking about what compilers actually do? – melpomene Jul 30 '17 at 07:30
  • 1
    The question you link to is a C question. C has different rules for `&` and `*` than C++. For example, C defines that `&*x` means `x` exactly, without a dereference, whereas C++ does not have that rule. – M.M Jul 30 '17 at 08:10
  • The question is unclear now. `&(pSomeType->SomeIntMember);` is completely different to `&((T *)0)->x`. (Assuming `pSomeType` was a valid pointer). What is your question really? – M.M Jul 30 '17 at 08:30
  • The `offsetof` macro is part of the standard library specifically because the hacks used would not be allowed in user code. When you build both the compiler and the header, you can make it "just work". Some compilers make the "magic" more visible as `#define offsetof(s, m) __builtin_offsetof(s, m)` – Bo Persson Jul 30 '17 at 10:30

2 Answers2

2

The -> operator does cause a dereference. a -> b is defined as (*a).b , if a is a pointer.

If people claim it is not a dereference, then they are either mistaken, or using some non-standard meaning of the word "dereference".

In the C++ standard, the formal name for * is the indirection operator. The word "dereference" isn't used as a verb; instead, the standard says that applying the * operator to a pointer yields an lvalue designating the object that was pointed to.

&(p->x) causes undefined behaviour if p is not a valid pointer.

Regarding the "offsetof" edit , the code in implementation headers is not subject to the rules of the language. They can contain magic and non-standard non-portable code.

M.M
  • 138,810
  • 21
  • 208
  • 365
0

As M.M's answer points out, the -> operator is a dereference (if the operand is a pointer). The confusion about whether this is dereferencing likely arises from the related notion of memory access.

To dereference a pointer is to get the object at the pointed to address. Or more precisely, given a pointer p of type T*, the expression *p is a lvalue of type T that refers to the pointed to object.

Memory access is when a read or write occurs, which corresponds to lvalue-to-rvalue conversion and assignment respectively. When none happens, no access to memory is made.

pSomeType->SomeIntMember    // is defined to be...
(*pSomeType).SomeIntMember

*pSomeType is a lvalue of SomeType, and therefore its member SomeIntMember is a lvalue of int.

Then its address is taken. No lvalue-to-rvalue conversion happens, and no assignment happens, therefore there is no memory access, as shown by @xaxxon's comment.

Passer By
  • 19,325
  • 6
  • 49
  • 96