Structure pointer offsets and safety in C ++

This question concerns pointers obtained using pointer arithmetic with structure offsets.

Consider the following program:

#include <cstddef> #include <iostream> #include <new> struct A { float a; double b; int c; }; static constexpr auto off_c = offsetof(A, c); int main() { A * a = new A{0.0f, 0.0, 5}; char * a_storage = reinterpret_cast<char *>(a); int * c = reinterpret_cast<int *>(a_storage + off_c)); std::cout << *c << std::endl; delete a; } 

This program works and gives the expected results for compilers that I tested using the default settings and the C ++ 11 standard.

(a closely related program where instead of reinterpret_cast we use void * instead of char * and static_cast , and void * not generally accepted. gcc 5.4 gives a warning about pointer arithmetic with void pointers, and clang 6.0 says that pointer arithmetic with void * is an error .)

Does this program support correct behavior in accordance with the C ++ standard?

The answer depends on whether relaxation has been implemented or strict pointer security ( [basic.stc.dynamic.safety] )?

+5
source share
3 answers

There are no fundamental errors in the code.

If A not the usual old, then it is above UB (before C ++ 17) and conditionally supported (after C ++ 17).

You might want to replace char* and int* with auto* , but that's the style.

Note that member pointers do this in exactly the same way in a safe manner. Most compilers implement a member pointer ... as a member offset in a type. However, they work everywhere even on non-substructures.

In addition: I do not see a guarantee that offsetof constexpr in the standard .;)

Replace anyway:

 static constexpr auto off_c = offsetof(A, c); 

with

 static constexpr auto off_c = &A::c; 

and

  auto* a_storage = static_cast<char *>(a); auto* c = reinterpret_cast<int *>(a_storage + off_c)); 

with

  auto* c = &(a->*off_c); 

to do it in a C ++ way.

+8
source

This is safe in your specific example, but only because your structure is a standard layout that you can double check with std::is_standard_layout<> .

Trying to apply this to a structure, for example:

 struct A { float a; double b; int c; std::string str; }; 

It would be illegal even if the line passed beyond part of the corresponding structure.

Edit

Here's what they do with abt: in 3.7.4.3 [basic.stc.dynamic.safety], it says that a pointer is safely displayed only if (conditions), and if we have strict pointer security, the pointer is invalid if it doesn't come from such a place. In 5.7 pointer arithmetic, he says that I can do regular arithmetic inside an array, but I don't see anything there, saying that offset offset arithmetic is fine. I am trying to find out if this does not match the way I think, or if the arithmetic with the structure correction does not correspond to the hypothetical "strict" implants or if I read 5.7 incorrect (n4296)

When you execute an arithmetic pointer, you execute it in a char array whose size is not less than sizeof(A) , so it's fine.

Then, when you return to the second member, you fall under (2.4):

- the result of the correct conversion of the pointer (4.10, 5.4) to the safe value of the pointer;

+4
source

you should study your assumptions.

Assumption No. 1) offsetof gives the correct offset in bytes. This is guaranteed only if the class is considered a β€œstandard layout”, which has a number of limitations, for example, no virtual methods, avoids multiple inheritance, etc. In this case, it should be good, but in general you cannot be sure.

Assumption No. 2) A char is the same size as a byte. In C, this is by definition, so you are safe.

Assumption No. 3). offsetof gives the correct offset from the pointer to the class, and not from the beginning of the data. This is basically the same as # 1, but vtable can certainly be a problem. Again, it only works with the standard layout.

+1
source

Source: https://habr.com/ru/post/1271650/


All Articles