Null pointer address?

I came across the macro below

#define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT)) 

I kind of can't digest it, because in C ++, when I try to read a null pointer, I expect unexpected behavior ... but how is it that it can have an address? What does the zero address mean?

+7
source share
8 answers

For the macro: It assumes that there is an object of type TYPE at address 0 and returns the address of the element, which is actually a member offset in the structure.

This answer explains why this behavior is undefined. I think this is the most important quote:

If E1 is of type "pointer to class X", then the expression E1->E2 converted to the equivalent form (*(E1)).E2 ; *(E1) will lead to undefined behavior with strict interpretation, and .E2 convert it to r, which makes it undefined for weak interpretations.

what takes place here. While others believe that this is true. It is important to note that this will lead to the correct result for many compilers.

+10
source
 #define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT)) 

very similar to the pretty general definition of the standard offsetof() macro defined in <stddef.h> (in C) or <cstddef> (in C ++).

0 is a null pointer constant. Listing TYPE * yields a null pointer of type TYPE * . Note that the language does not guarantee (or even imply) that the null pointer is set to 0, although this happens very often.

So, (TYPE *)0 is, in fact, the address of an object of type TYPE , located at any address pointed to by a null pointer, and ((TYPE *)0)->ELEMENT)) is a member of the ELEMENT this object.

The & operator takes the address of this ELEMENT member, and the listing converts that address to type size_t .

Now, if the null pointer points to address 0, then a (non-existent) object of type TYPE begins with address 0, and the address of the ELEMENT member of this object is located at address, offset by a certain number of bytes from address 0. Assuming that the transformation defined by the implementation from TYPE * up to size_t , behaves in a straightforward manner (something else that is not guaranteed by the language), the result of the whole expression will be the offset of the ELEMENT member in an object of type TYPE .

It all depends on several undefined or undefined behavior. In most modern systems, a null pointer is implemented as a pointer to address 0, addresses (pointer values) are presented as if they were integers indicating the index of a particular byte in a space of monolithic addressing, and the conversion from a pointer to an integer of the same size simply reinterprets bit. In a system with these characteristics, the OFFSETOF macro is likely to work, and the implementation can choose a similar definition for the standard OFFSETOF macro. (Code that part of the implementation can take advantage of the implementation or undefined, and you do not need to port them.)

On systems that do not have these characteristics, this OFFSETOF macro may not work - and the implementation must use a different method to implement OFFSETOF . Therefore, OFFSETOF is part of the standard library; it cannot be implemented portable, but it can always be somehow implemented for any system. And some implementations use compiler magic, such as gcc __builtin_offsetof .

In practice, it makes no sense to define your own OFFSETOF macro like this, since any corresponding C or C ++ implementation will provide a working OFFSETOF macro in its standard library.

+7
source

This does not cause the dereference of the pointer, but returns the offset of the element in the structure.

for example for

 typedef struct { char a; char b;} someStruct; 

Calling OFFSETOF(someStruct, b) will return 1 (assuming it is packed, etc., etc.).

This is the same as doing this:

 someStruct str; offset = (size_t)&(str.b) - (size_t)&str; 

except that with OFFSETOF you don't need to create a dummy variable.

This is necessary when you need to find the offset of a member of a class / structure / union for any reason.

** Change **

For all hasty downvoters who think that the “standard does not allow this” - please read the standard again. In this case, the behavior is very well defined.

** Other editing **

I believe that none of the downvoters noticed that the first parameter is type . I am sure that if you think a little more than half a second, then for understanding you will understand your mistake. If not - well, this will not be the first that a bunch of ignorant downvoters have suppressed the correct answer.

+5
source

This is one hellish macro piling up undefined behavior ...

What he is trying to do: get the offset of the struct member.

How is he trying to do this:

  • Use a null pointer (value 0 in code)
  • Let's take an element (let the compiler calculate its address, starting from 0)
  • Take the address of the element (using & )
  • Enter the address in size_t

There are two problems:

  • Calling a null pointer is undefined behavior, so technically anything can happen.
  • Casting a pointer to size_t not something that needs to be done (the problem is that the pointer is not guaranteed to match)

How can I do that:

  • Use real object
  • Calculate Address Difference

In code:

 #define OFFSETOF(Object, Member) \ ((diffptr_t)((char*)(&Object.Member) - (char*)(&Object)) 

However, this requires an object, so it is not suitable for your purposes.

How to do it :

 #include <cstddef> #define OFFSETOF(Struct, Member) offsetof(Struct, Member) 

But there would be little point ... right?

For the curious, the definition might be something like this: __builtin_offsetof(st, m) (from Wikipedia ). Some compilers implement it with zero parsing, but they are compilers and, therefore, know that they relate to this case safely; this is not portable ... and does not have to be from the moment you turn on the compiler, you also switch the implementation of the C library.

+3
source

The purpose of OFFSETOF is to return the distance between the address of a member and the address of the population to which it belongs.

If the compiler does not change the layout of the object depending on its location, this “distance” is constant, and therefore the address from which you start does not matter. 0, in this case it's just an address like any other.

According to the C ++ standard, access to an invalid address is "undefined behavior", but:

  • If this part of the compiler support library (this is the actual “OFFSETOF” code in CRT coming with VS2003!), It may not be so “undefined” (for a well-known compiler and platform, this behavior is known to the support library developer: of course, this should be considered as "specific platform code", but on another platform there will probably be different versions of libraries)

  • In any case, you do not "act" on the element (therefore, "access" is not performed), just by doing some simple pointer arithmetic. Thnk as a general daemon like "If at location 0 there is an object, its alleged member ELEMENT will start al location 6. hence 6 is an offset." The fact that there is no such real object does not matter.

  • By the way, this macro fails (with a segmentation error!) If ELEMENT is inherited by TYPE using the virtual database, because to find the location of the virtual database you need to access some information about the runtime - usually part of a v-table object - the location cannot be detected because the address of the object is not a "real" address. This explains why the standard refers to the fact that "dereferencing an invalid pointer is undefined behavior."


TO DOWNLOADS:

I provide a specific platform for a specific platform. Before taking a picture, please demonstrate that what I said is wrong.

+3
source

Highlighting a null pointer (as the macro does) is undefined behavior. You do not need to write and use such a macro, unless the implementation gives you a special additional guarantee.

The C standard library defines the offsetof macro; many implementations use something similar to this. The implementation can do this because it knows what the compiler generates in this case, and this will cause problems or not. Implementing a standard library can use many things that you cannot.

+3
source

but. The action is valid, an exception will not be thrown because you are not trying to access the memory pointed to by the pointer.
B. null pointer is a normal normal pointer indicating that the object is at address 0 (address 0 is by definition an invalid address for real objects), but the pointer itself is valid.

So this macro is average: if an object of type TYPE starts at address 0, where will its ELEMENT be in memory? in other words, what is the offset from ELEMENT to the start of the TYPE object.

+2
source

littleadv had the intention of building just the right. The explanation is a bit: you drew a structure pointer pointing to the address 0x0 and playing out its elements. The address you are pointing to is now at 0x0 + regardless of the offset of the element. Now you pass this value to size_t and get the offset of the element.

I'm not sure how portable this design is.

+1
source

All Articles