Am I invoking undefined behavior when casting back to my pointer? - c++

I've been trying to figure out if this is an optimisation bug as it only seems to affect stack variables, and I wonder if there's some incorrect assumptions being made. I have this type which converts to and from a relative offset, and it has been working fine while using reinterpret_cast, but now I'm moving to static_cast, it's starting to cause issues in optimised builds. I need to get away from reinterpret_cast for safety certification reasons, so I don't have the option of keeping it as it is.
#include <iostream>
template <typename T>
class Ptr
{
public:
Ptr(const T* ptr = nullptr) : m_offset(GetOffset(ptr)) {}
T& operator*() const noexcept { return *GetPtr(); }
T* get() const noexcept { return GetPtr(); }
bool operator==(const T *ptr) const {
// comment this back in and it stops failing
//std::cout << "{op==" << get() << " == " << ptr << "}";
return get() == ptr;
}
private:
std::ptrdiff_t m_offset = 0;
inline T* GetPtr() const
{
auto offset = m_offset;
auto const_void_address = static_cast<const void*>(&m_offset);
auto const_char_address = static_cast<const char*>(const_void_address);
auto offset_address = const_cast<char*>(const_char_address);
auto final_address = static_cast<void*>(offset_address - offset);
return static_cast<T*>(final_address);
}
std::ptrdiff_t GetOffset(const void* ptr) const
{
auto void_address = static_cast<const void*>(&m_offset);
auto offset_address = static_cast<const char*>(void_address);
auto ptr_address = static_cast<const char*>(ptr);
return offset_address - ptr_address;
}
};
std::ostream& operator<<(std::ostream &stream, const Ptr<int> &rp) {
stream << rp.get();
return stream;
}
int main() {
int data = 123;
Ptr<int> rp(&data);
std::cout << "data " << data << " # " << &data << std::endl;
std::cout << "rp " << *rp << " get " << rp.get() << std::endl;
std::cout << (rp == &data) << std::endl;
std::cout << "(rp.get() == &data) = " << (rp.get() == &data) << std::endl;
std::cout << "(rp == &data) = " << (rp == &data) << std::endl;
return 0;
}
with optimisations turned on, I get output like this:
data 123 # 0x7ffe79544a34
rp 123 get 0x7ffe79544a34
0
(rp.get() == &data) = 0
(rp == &data) = 0
Which includes some output which is clearly inconsistent with itself.
I've tested this on GCC 8,9, and 11.2.
As soon as I drop back to -O0, it's fine.
If I uncomment the std::cout inside the operator==, it's fine.
If I go back to reinterpret_cast (return reinterpret_cast<T*>(reinterpret_cast<std::ptrdiff_t>(&m_offset) - offset);) it's fine.
If I allocate data as a pointer and initialise rp from that, it's fine too.
It seems to behave as I expect under clang (8 through 13 seem fine)
This feels like I am either not understanding where the UB is coming from, or there's a compiler optimisation bug here.
EDIT / UPDATE:
After looking at this is more detail I think the only solution is to do type punning in a semi-safe way, so I have tried this solution and it appears to work. (it appears, what I am now doing is part of C++20 and called bit_cast, so maybe this is valid?)
inline T* GetPtr() const
{
auto offset = m_offset;
intptr_t realAddress;
auto address_of_m_offset = &m_offset;
std::memcpy(&realAddress, &address_of_m_offset, sizeof( realAddress));
realAddress -= m_offset;
T *outValue;
std::memcpy(&outValue, &realAddress, sizeof( outValue));
return outValue;
}
std::ptrdiff_t GetOffset(const void* ptr) const
{
auto address_of_m_offset = &m_offset;
intptr_t myAddress;
std::memcpy(&myAddress, &address_of_m_offset, sizeof(myAddress));
intptr_t realAddress;
std::memcpy(&realAddress, &ptr, sizeof(realAddress));
return static_cast<ptrdiff_t>(myAddress - realAddress);
}
This no longer seems to cause a problem with GCC. I hear that std::memcpy is meant to be used for objects of same type where otherwise we would have used reinterpret_cast, so this makes sense to me.

Your undefined behaviour is in GetOffset.
This is how pointer subtraction is defined by the standard:
When two pointer expressions P and Q are subtracted, the type of the result is an implementation-defined signed integral type; this type shall be the same type that is defined as std::ptrdiff in the <cstddef> header ([support.types.layout])
If P and Q both evaluate to null pointer values, the result is 0.
Otherwise, if P and Q both point to, respectively, array elements i and j of the same array object x, the expression P - Q has the value i - j.
Otherwise, the behavior is undefined.
And here, P (which has the address of m_object) and Q (which has the address of data) aren't elements of the same array, so this is undefined behaviour.
Addition and subtraction of pointers and integers is also defined in terms of array elements:
When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.
If P evaluates to a null pointer value and J evaluates to 0, the result is a null pointer value.
Otherwise, if P points to an array element i of an array object x with n elements ([dcl.array]), the expression P + J and J + P (Where J has value j point to the (possibly-hypothetical) array element i+j of x if 0≤i+j≤n and the expression P - J points to the (possibly-hypothetical) array element i-j of x if 0≤i-j≤n.
Otherwise, the behavior is undefined.
The pointer subtraction happens at offset_address - offset, where P is the address of m_offset and offset is probably some positive number. m_offset is the first element of the array, so i-j < 0
So, the compiler can see that GetPtr returns a pointer relative to the m_offset (in the char[sizeof(Ptr<int>)] array that aliases the object), so it cannot equal the address of data (without UB), and thus an optimizer can replace (rp.get() == &data) with false.
When you use ptrdiff_t, no such restriction on addition or subtraction exists. And though the standard doesn't guarantee that reinterpret_cast<char*>(reinterpret_cast<intptr_t>(char_pointer) + n) == char_pointer + n (pointers mapping linearly as you would expect) this is what happens when compiling with gcc on common architectures.

Related

C++ use address of member as an array [duplicate]

Regardless of how 'bad' the code is, and assuming that alignment etc are not an issue on the compiler/platform, is this undefined or broken behavior?
If I have a struct like this :-
struct data
{
int a, b, c;
};
struct data thing;
Is it legal to access a, b and c as (&thing.a)[0], (&thing.a)[1], and (&thing.a)[2]?
In every case, on every compiler and platform I tried it on, with every setting I tried it 'worked'. I'm just worried that the compiler might not realize that b and thing[1] are the same thing and stores to 'b' might be put in a register and thing[1] reads the wrong value from memory (for example). In every case I tried it did the right thing though. (I realize of course that doesn't prove much)
This is not my code; it's code I have to work with, I'm interested in whether this is bad code or broken code as the different affects my priorities for changing it a great deal :)
Tagged C and C++ . I'm mostly interested in C++ but also C if it is different, just for interest.
It is illegal 1. That's an Undefined behavior in C++.
You are taking the members in an array fashion, but here is what the C++ standard says (emphasis mine):
[dcl.array/1]: ...An object of array type contains a contiguously allocated non-empty set of N
subobjects of type T...
But, for members, there's no such contiguous requirement:
[class.mem/17]: ...;Implementation alignment requirements might cause two adjacent
members not to be allocated immediately after each other...
While the above two quotes should be enough to hint why indexing into a struct as you did isn't a defined behavior by the C++ standard, let's pick one example: look at the expression (&thing.a)[2] - Regarding the subscript operator:
[expr.post//expr.sub/1]:
A postfix expression followed by an expression in square brackets is a
postfix expression. One of the expressions shall be a glvalue of type
“array of T” or a prvalue of type “pointer to T” and the other shall
be a prvalue of unscoped enumeration or integral type. The result is
of type “T”. The type “T” shall be a completely-defined object type.66
The expression E1[E2] is identical (by definition) to ((E1)+(E2))
Digging into the bold text of the above quote: regarding adding an integral type to a pointer type (note the emphasis here)..
[expr.add/4]: When an expression that has integral type is added to or subtracted from a
pointer, the result has the type of the pointer operand. If the
expression P points to element x[i] of an array object x
with n elements, the expressions P + J and J + P (where J has
the value j) point to the (possibly-hypothetical) element x[i + j]
if 0 ≤ i + j ≤ n; otherwise, the behavior is undefined. ...
Note the array requirement for the if clause; else the otherwise in the above quote. The expression (&thing.a)[2] obviously doesn't qualify for the if clause; Hence, Undefined Behavior.
On a side note: Though I have extensively experimented the code and its variations on various compilers and they don't introduce any padding here, (it works); from a maintenance view, the code is extremely fragile. you should still assert that the implementation allocated the members contiguously before doing this. And stay in-bounds :-). But its still Undefined behavior....
Some viable workarounds (with defined behavior) have been provided by other answers.
As rightly pointed out in the comments, [basic.lval/8], which was in my previous edit doesn't apply. Thanks #2501 and #M.M.
1: See #Barry's answer to this question for the only one legal case where you can access thing.a member of the struct via this parttern.
No. In C, this is undefined behavior even if there is no padding.
The thing that causes undefined behavior is out-of-bounds access1. When you have a scalar (members a,b,c in the struct) and try to use it as an array2 to access the next hypothetical element, you cause undefined behavior, even if there happens to be another object of the same type at that address.
However you may use the address of the struct object and calculate the offset into a specific member:
struct data thing = { 0 };
char* p = ( char* )&thing + offsetof( thing , b );
int* b = ( int* )p;
*b = 123;
assert( thing.b == 123 );
This has to be done for each member individually, but can be put into a function that resembles an array access.
1 (Quoted from: ISO/IEC 9899:201x 6.5.6 Additive operators 8)
If the result points one past the last element of the array object, it
shall not be used as the operand of a unary * operator that is evaluated.
2 (Quoted from: ISO/IEC 9899:201x 6.5.6 Additive operators 7)
For the purposes of these operators, a pointer to an object that is not an element of an
array behaves the same as a pointer to the first element of an array of length one with the
type of the object as its element type.
In C++ if you really need it - create operator[]:
struct data
{
int a, b, c;
int &operator[]( size_t idx ) {
switch( idx ) {
case 0 : return a;
case 1 : return b;
case 2 : return c;
default: throw std::runtime_error( "bad index" );
}
}
};
data d;
d[0] = 123; // assign 123 to data.a
it is not only guaranteed to work but usage is simpler, you do not need to write unreadable expression (&thing.a)[0]
Note: this answer is given in assumption that you already have a structure with fields, and you need to add access via index. If speed is an issue and you can change the structure this could be more effective:
struct data
{
int array[3];
int &a = array[0];
int &b = array[1];
int &c = array[2];
};
This solution would change size of structure so you can use methods as well:
struct data
{
int array[3];
int &a() { return array[0]; }
int &b() { return array[1]; }
int &c() { return array[2]; }
};
For c++: If you need to access a member without knowing its name, you can use a pointer to member variable.
struct data {
int a, b, c;
};
typedef int data::* data_int_ptr;
data_int_ptr arr[] = {&data::a, &data::b, &data::c};
data thing;
thing.*arr[0] = 123;
In ISO C99/C11, union-based type-punning is legal, so you can use that instead of indexing pointers to non-arrays (see various other answers).
ISO C++ doesn't allow union-based type-punning. GNU C++ does, as an extension, and I think some other compilers that don't support GNU extensions in general do support union type-punning. But that doesn't help you write strictly portable code.
With current versions of gcc and clang, writing a C++ member function using a switch(idx) to select a member will optimize away for compile-time constant indices, but will produce terrible branchy asm for runtime indices. There's nothing inherently wrong with switch() for this; this is simply a missed-optimization bug in current compilers. They could compiler Slava' switch() function efficiently.
The solution/workaround to this is to do it the other way: give your class/struct an array member, and write accessor functions to attach names to specific elements.
struct array_data
{
int arr[3];
int &operator[]( unsigned idx ) {
// assert(idx <= 2);
//idx = (idx > 2) ? 2 : idx;
return arr[idx];
}
int &a(){ return arr[0]; } // TODO: const versions
int &b(){ return arr[1]; }
int &c(){ return arr[2]; }
};
We can have a look at the asm output for different use-cases, on the Godbolt compiler explorer. These are complete x86-64 System V functions, with the trailing RET instruction omitted to better show what you'd get when they inline. ARM/MIPS/whatever would be similar.
# asm from g++6.2 -O3
int getb(array_data &d) { return d.b(); }
mov eax, DWORD PTR [rdi+4]
void setc(array_data &d, int val) { d.c() = val; }
mov DWORD PTR [rdi+8], esi
int getidx(array_data &d, int idx) { return d[idx]; }
mov esi, esi # zero-extend to 64-bit
mov eax, DWORD PTR [rdi+rsi*4]
By comparison, #Slava's answer using a switch() for C++ makes asm like this for a runtime-variable index. (Code in the previous Godbolt link).
int cpp(data *d, int idx) {
return (*d)[idx];
}
# gcc6.2 -O3, using `default: __builtin_unreachable()` to promise the compiler that idx=0..2,
# avoiding an extra cmov for idx=min(idx,2), or an extra branch to a throw, or whatever
cmp esi, 1
je .L6
cmp esi, 2
je .L7
mov eax, DWORD PTR [rdi]
ret
.L6:
mov eax, DWORD PTR [rdi+4]
ret
.L7:
mov eax, DWORD PTR [rdi+8]
ret
This is obviously terrible, compared to the C (or GNU C++) union-based type punning version:
c(type_t*, int):
movsx rsi, esi # sign-extend this time, since I didn't change idx to unsigned here
mov eax, DWORD PTR [rdi+rsi*4]
In C++, this is mostly undefined behavior (it depends on which index).
From [expr.unary.op]:
For purposes of pointer
arithmetic (5.7) and comparison (5.9, 5.10), an object that is not an array element whose address is taken in
this way is considered to belong to an array with one element of type T.
The expression &thing.a is thus considered to refer to an array of one int.
From [expr.sub]:
The expression E1[E2] is identical (by definition) to *((E1)+(E2))
And from [expr.add]:
When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i + j] if 0 <= i + j <= n; otherwise, the behavior is undefined.
(&thing.a)[0] is perfectly well-formed because &thing.a is considered an array of size 1 and we're taking that first index. That is an allowed index to take.
(&thing.a)[2] violates the precondition that 0 <= i + j <= n, since we have i == 0, j == 2, n == 1. Simply constructing the pointer &thing.a + 2 is undefined behavior.
(&thing.a)[1] is the interesting case. It doesn't actually violate anything in [expr.add]. We're allowed to take a pointer one past the end of the array - which this would be. Here, we turn to a note in [basic.compound]:
A value of a pointer type that is a pointer to or past the end of an object represents the address of the
first byte in memory (1.7) occupied by the object53 or the first byte in memory after the end of the storage
occupied by the object, respectively. [ Note: A pointer past the end of an object (5.7) is not considered to
point to an unrelated object of the object’s type that might be located at that address.
Hence, taking the pointer &thing.a + 1 is defined behavior, but dereferencing it is undefined because it does not point to anything.
This is undefined behavior.
There are lots of rules in C++ that attempt to give the compiler some hope of understanding what you are doing, so it can reason about it and optimize it.
There are rules about aliasing (accessing data through two different pointer types), array bounds, etc.
When you have a variable x, the fact that it isn't a member of an array means that the compiler can assume that no [] based array access can modify it. So it doesn't have to constantly reload the data from memory every time you use it; only if someone could have modified it from its name.
Thus (&thing.a)[1] can be assumed by the compiler to not refer to thing.b. It can use this fact to reorder reads and writes to thing.b, invalidating what you want it to do without invalidating what you actually told it to do.
A classic example of this is casting away const.
const int x = 7;
std::cout << x << '\n';
auto ptr = (int*)&x;
*ptr = 2;
std::cout << *ptr << "!=" << x << '\n';
std::cout << ptr << "==" << &x << '\n';
here you typically get a compiler saying 7 then 2 != 7, and then two identical pointers; despite the fact that ptr is pointing at x. The compiler takes the fact that x is a constant value to not bother reading it when you ask for the value of x.
But when you take the address of x, you force it to exist. You then cast away const, and modify it. So the actual location in memory where x is has been modified, the compiler is free to not actually read it when reading x!
The compiler may get smart enough to figure out how to even avoid following ptr to read *ptr, but often they are not. Feel free to go and use ptr = ptr+argc-1 or somesuch confusion if the optimizer is getting smarter than you.
You can provide a custom operator[] that gets the right item.
int& operator[](std::size_t);
int const& operator[](std::size_t) const;
having both is useful.
Heres a way to use a proxy class to access elements in a member array by name. It is very C++, and has no benefit vs. ref-returning accessor functions, except for syntactic preference. This overloads the -> operator to access elements as members, so to be acceptable, one needs to both dislike the syntax of accessors (d.a() = 5;), as well as tolerate using -> with a non-pointer object. I expect this might also confuse readers not familiar with the code, so this might be more of a neat trick than something you want to put into production.
The Data struct in this code also includes overloads for the subscript operator, to access indexed elements inside its ar array member, as well as begin and end functions, for iteration. Also, all of these are overloaded with non-const and const versions, which I felt needed to be included for completeness.
When Data's -> is used to access an element by name (like this: my_data->b = 5;), a Proxy object is returned. Then, because this Proxy rvalue is not a pointer, its own -> operator is auto-chain-called, which returns a pointer to itself. This way, the Proxy object is instantiated and remains valid during evaluation of the initial expression.
Contruction of a Proxy object populates its 3 reference members a, b and c according to a pointer passed in the constructor, which is assumed to point to a buffer containing at least 3 values whose type is given as the template parameter T. So instead of using named references which are members of the Data class, this saves memory by populating the references at the point of access (but unfortunately, using -> and not the . operator).
In order to test how well the compiler's optimizer eliminates all of the indirection introduced by the use of Proxy, the code below includes 2 versions of main(). The #if 1 version uses the -> and [] operators, and the #if 0 version performs the equivalent set of procedures, but only by directly accessing Data::ar.
The Nci() function generates runtime integer values for initializing array elements, which prevents the optimizer from just plugging constant values directly into each std::cout << call.
For gcc 6.2, using -O3, both versions of main() generate the same assembly (toggle between #if 1 and #if 0 before the first main() to compare): https://godbolt.org/g/QqRWZb
#include <iostream>
#include <ctime>
template <typename T>
class Proxy {
public:
T &a, &b, &c;
Proxy(T* par) : a(par[0]), b(par[1]), c(par[2]) {}
Proxy* operator -> () { return this; }
};
struct Data {
int ar[3];
template <typename I> int& operator [] (I idx) { return ar[idx]; }
template <typename I> const int& operator [] (I idx) const { return ar[idx]; }
Proxy<int> operator -> () { return Proxy<int>(ar); }
Proxy<const int> operator -> () const { return Proxy<const int>(ar); }
int* begin() { return ar; }
const int* begin() const { return ar; }
int* end() { return ar + sizeof(ar)/sizeof(int); }
const int* end() const { return ar + sizeof(ar)/sizeof(int); }
};
// Nci returns an unpredictible int
inline int Nci() {
static auto t = std::time(nullptr) / 100 * 100;
return static_cast<int>(t++ % 1000);
}
#if 1
int main() {
Data d = {Nci(), Nci(), Nci()};
for(auto v : d) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << d->b << "\n";
d->b = -5;
std::cout << d[1] << "\n";
std::cout << "\n";
const Data cd = {Nci(), Nci(), Nci()};
for(auto v : cd) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << cd->c << "\n";
//cd->c = -5; // error: assignment of read-only location
std::cout << cd[2] << "\n";
}
#else
int main() {
Data d = {Nci(), Nci(), Nci()};
for(auto v : d.ar) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << d.ar[1] << "\n";
d->b = -5;
std::cout << d.ar[1] << "\n";
std::cout << "\n";
const Data cd = {Nci(), Nci(), Nci()};
for(auto v : cd.ar) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << cd.ar[2] << "\n";
//cd.ar[2] = -5;
std::cout << cd.ar[2] << "\n";
}
#endif
If reading values is enough, and efficiency is not a concern, or if you trust your compiler to optimize things well, or if struct is just that 3 bytes, you can safely do this:
char index_data(const struct data *d, size_t index) {
assert(sizeof(*d) == offsetoff(*d, c)+1);
assert(index < sizeof(*d));
char buf[sizeof(*d)];
memcpy(buf, d, sizeof(*d));
return buf[index];
}
For C++ only version, you would probably want to use static_assert to verify that struct data has standard layout, and perhaps throw exception on invalid index instead.
It is illegal, but there is a workaround:
struct data {
union {
struct {
int a;
int b;
int c;
};
int v[3];
};
};
Now you can index v:

Is this a violation of strict aliasing rules? [duplicate]

Regardless of how 'bad' the code is, and assuming that alignment etc are not an issue on the compiler/platform, is this undefined or broken behavior?
If I have a struct like this :-
struct data
{
int a, b, c;
};
struct data thing;
Is it legal to access a, b and c as (&thing.a)[0], (&thing.a)[1], and (&thing.a)[2]?
In every case, on every compiler and platform I tried it on, with every setting I tried it 'worked'. I'm just worried that the compiler might not realize that b and thing[1] are the same thing and stores to 'b' might be put in a register and thing[1] reads the wrong value from memory (for example). In every case I tried it did the right thing though. (I realize of course that doesn't prove much)
This is not my code; it's code I have to work with, I'm interested in whether this is bad code or broken code as the different affects my priorities for changing it a great deal :)
Tagged C and C++ . I'm mostly interested in C++ but also C if it is different, just for interest.
It is illegal 1. That's an Undefined behavior in C++.
You are taking the members in an array fashion, but here is what the C++ standard says (emphasis mine):
[dcl.array/1]: ...An object of array type contains a contiguously allocated non-empty set of N
subobjects of type T...
But, for members, there's no such contiguous requirement:
[class.mem/17]: ...;Implementation alignment requirements might cause two adjacent
members not to be allocated immediately after each other...
While the above two quotes should be enough to hint why indexing into a struct as you did isn't a defined behavior by the C++ standard, let's pick one example: look at the expression (&thing.a)[2] - Regarding the subscript operator:
[expr.post//expr.sub/1]:
A postfix expression followed by an expression in square brackets is a
postfix expression. One of the expressions shall be a glvalue of type
“array of T” or a prvalue of type “pointer to T” and the other shall
be a prvalue of unscoped enumeration or integral type. The result is
of type “T”. The type “T” shall be a completely-defined object type.66
The expression E1[E2] is identical (by definition) to ((E1)+(E2))
Digging into the bold text of the above quote: regarding adding an integral type to a pointer type (note the emphasis here)..
[expr.add/4]: When an expression that has integral type is added to or subtracted from a
pointer, the result has the type of the pointer operand. If the
expression P points to element x[i] of an array object x
with n elements, the expressions P + J and J + P (where J has
the value j) point to the (possibly-hypothetical) element x[i + j]
if 0 ≤ i + j ≤ n; otherwise, the behavior is undefined. ...
Note the array requirement for the if clause; else the otherwise in the above quote. The expression (&thing.a)[2] obviously doesn't qualify for the if clause; Hence, Undefined Behavior.
On a side note: Though I have extensively experimented the code and its variations on various compilers and they don't introduce any padding here, (it works); from a maintenance view, the code is extremely fragile. you should still assert that the implementation allocated the members contiguously before doing this. And stay in-bounds :-). But its still Undefined behavior....
Some viable workarounds (with defined behavior) have been provided by other answers.
As rightly pointed out in the comments, [basic.lval/8], which was in my previous edit doesn't apply. Thanks #2501 and #M.M.
1: See #Barry's answer to this question for the only one legal case where you can access thing.a member of the struct via this parttern.
No. In C, this is undefined behavior even if there is no padding.
The thing that causes undefined behavior is out-of-bounds access1. When you have a scalar (members a,b,c in the struct) and try to use it as an array2 to access the next hypothetical element, you cause undefined behavior, even if there happens to be another object of the same type at that address.
However you may use the address of the struct object and calculate the offset into a specific member:
struct data thing = { 0 };
char* p = ( char* )&thing + offsetof( thing , b );
int* b = ( int* )p;
*b = 123;
assert( thing.b == 123 );
This has to be done for each member individually, but can be put into a function that resembles an array access.
1 (Quoted from: ISO/IEC 9899:201x 6.5.6 Additive operators 8)
If the result points one past the last element of the array object, it
shall not be used as the operand of a unary * operator that is evaluated.
2 (Quoted from: ISO/IEC 9899:201x 6.5.6 Additive operators 7)
For the purposes of these operators, a pointer to an object that is not an element of an
array behaves the same as a pointer to the first element of an array of length one with the
type of the object as its element type.
In C++ if you really need it - create operator[]:
struct data
{
int a, b, c;
int &operator[]( size_t idx ) {
switch( idx ) {
case 0 : return a;
case 1 : return b;
case 2 : return c;
default: throw std::runtime_error( "bad index" );
}
}
};
data d;
d[0] = 123; // assign 123 to data.a
it is not only guaranteed to work but usage is simpler, you do not need to write unreadable expression (&thing.a)[0]
Note: this answer is given in assumption that you already have a structure with fields, and you need to add access via index. If speed is an issue and you can change the structure this could be more effective:
struct data
{
int array[3];
int &a = array[0];
int &b = array[1];
int &c = array[2];
};
This solution would change size of structure so you can use methods as well:
struct data
{
int array[3];
int &a() { return array[0]; }
int &b() { return array[1]; }
int &c() { return array[2]; }
};
For c++: If you need to access a member without knowing its name, you can use a pointer to member variable.
struct data {
int a, b, c;
};
typedef int data::* data_int_ptr;
data_int_ptr arr[] = {&data::a, &data::b, &data::c};
data thing;
thing.*arr[0] = 123;
In ISO C99/C11, union-based type-punning is legal, so you can use that instead of indexing pointers to non-arrays (see various other answers).
ISO C++ doesn't allow union-based type-punning. GNU C++ does, as an extension, and I think some other compilers that don't support GNU extensions in general do support union type-punning. But that doesn't help you write strictly portable code.
With current versions of gcc and clang, writing a C++ member function using a switch(idx) to select a member will optimize away for compile-time constant indices, but will produce terrible branchy asm for runtime indices. There's nothing inherently wrong with switch() for this; this is simply a missed-optimization bug in current compilers. They could compiler Slava' switch() function efficiently.
The solution/workaround to this is to do it the other way: give your class/struct an array member, and write accessor functions to attach names to specific elements.
struct array_data
{
int arr[3];
int &operator[]( unsigned idx ) {
// assert(idx <= 2);
//idx = (idx > 2) ? 2 : idx;
return arr[idx];
}
int &a(){ return arr[0]; } // TODO: const versions
int &b(){ return arr[1]; }
int &c(){ return arr[2]; }
};
We can have a look at the asm output for different use-cases, on the Godbolt compiler explorer. These are complete x86-64 System V functions, with the trailing RET instruction omitted to better show what you'd get when they inline. ARM/MIPS/whatever would be similar.
# asm from g++6.2 -O3
int getb(array_data &d) { return d.b(); }
mov eax, DWORD PTR [rdi+4]
void setc(array_data &d, int val) { d.c() = val; }
mov DWORD PTR [rdi+8], esi
int getidx(array_data &d, int idx) { return d[idx]; }
mov esi, esi # zero-extend to 64-bit
mov eax, DWORD PTR [rdi+rsi*4]
By comparison, #Slava's answer using a switch() for C++ makes asm like this for a runtime-variable index. (Code in the previous Godbolt link).
int cpp(data *d, int idx) {
return (*d)[idx];
}
# gcc6.2 -O3, using `default: __builtin_unreachable()` to promise the compiler that idx=0..2,
# avoiding an extra cmov for idx=min(idx,2), or an extra branch to a throw, or whatever
cmp esi, 1
je .L6
cmp esi, 2
je .L7
mov eax, DWORD PTR [rdi]
ret
.L6:
mov eax, DWORD PTR [rdi+4]
ret
.L7:
mov eax, DWORD PTR [rdi+8]
ret
This is obviously terrible, compared to the C (or GNU C++) union-based type punning version:
c(type_t*, int):
movsx rsi, esi # sign-extend this time, since I didn't change idx to unsigned here
mov eax, DWORD PTR [rdi+rsi*4]
In C++, this is mostly undefined behavior (it depends on which index).
From [expr.unary.op]:
For purposes of pointer
arithmetic (5.7) and comparison (5.9, 5.10), an object that is not an array element whose address is taken in
this way is considered to belong to an array with one element of type T.
The expression &thing.a is thus considered to refer to an array of one int.
From [expr.sub]:
The expression E1[E2] is identical (by definition) to *((E1)+(E2))
And from [expr.add]:
When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i + j] if 0 <= i + j <= n; otherwise, the behavior is undefined.
(&thing.a)[0] is perfectly well-formed because &thing.a is considered an array of size 1 and we're taking that first index. That is an allowed index to take.
(&thing.a)[2] violates the precondition that 0 <= i + j <= n, since we have i == 0, j == 2, n == 1. Simply constructing the pointer &thing.a + 2 is undefined behavior.
(&thing.a)[1] is the interesting case. It doesn't actually violate anything in [expr.add]. We're allowed to take a pointer one past the end of the array - which this would be. Here, we turn to a note in [basic.compound]:
A value of a pointer type that is a pointer to or past the end of an object represents the address of the
first byte in memory (1.7) occupied by the object53 or the first byte in memory after the end of the storage
occupied by the object, respectively. [ Note: A pointer past the end of an object (5.7) is not considered to
point to an unrelated object of the object’s type that might be located at that address.
Hence, taking the pointer &thing.a + 1 is defined behavior, but dereferencing it is undefined because it does not point to anything.
This is undefined behavior.
There are lots of rules in C++ that attempt to give the compiler some hope of understanding what you are doing, so it can reason about it and optimize it.
There are rules about aliasing (accessing data through two different pointer types), array bounds, etc.
When you have a variable x, the fact that it isn't a member of an array means that the compiler can assume that no [] based array access can modify it. So it doesn't have to constantly reload the data from memory every time you use it; only if someone could have modified it from its name.
Thus (&thing.a)[1] can be assumed by the compiler to not refer to thing.b. It can use this fact to reorder reads and writes to thing.b, invalidating what you want it to do without invalidating what you actually told it to do.
A classic example of this is casting away const.
const int x = 7;
std::cout << x << '\n';
auto ptr = (int*)&x;
*ptr = 2;
std::cout << *ptr << "!=" << x << '\n';
std::cout << ptr << "==" << &x << '\n';
here you typically get a compiler saying 7 then 2 != 7, and then two identical pointers; despite the fact that ptr is pointing at x. The compiler takes the fact that x is a constant value to not bother reading it when you ask for the value of x.
But when you take the address of x, you force it to exist. You then cast away const, and modify it. So the actual location in memory where x is has been modified, the compiler is free to not actually read it when reading x!
The compiler may get smart enough to figure out how to even avoid following ptr to read *ptr, but often they are not. Feel free to go and use ptr = ptr+argc-1 or somesuch confusion if the optimizer is getting smarter than you.
You can provide a custom operator[] that gets the right item.
int& operator[](std::size_t);
int const& operator[](std::size_t) const;
having both is useful.
Heres a way to use a proxy class to access elements in a member array by name. It is very C++, and has no benefit vs. ref-returning accessor functions, except for syntactic preference. This overloads the -> operator to access elements as members, so to be acceptable, one needs to both dislike the syntax of accessors (d.a() = 5;), as well as tolerate using -> with a non-pointer object. I expect this might also confuse readers not familiar with the code, so this might be more of a neat trick than something you want to put into production.
The Data struct in this code also includes overloads for the subscript operator, to access indexed elements inside its ar array member, as well as begin and end functions, for iteration. Also, all of these are overloaded with non-const and const versions, which I felt needed to be included for completeness.
When Data's -> is used to access an element by name (like this: my_data->b = 5;), a Proxy object is returned. Then, because this Proxy rvalue is not a pointer, its own -> operator is auto-chain-called, which returns a pointer to itself. This way, the Proxy object is instantiated and remains valid during evaluation of the initial expression.
Contruction of a Proxy object populates its 3 reference members a, b and c according to a pointer passed in the constructor, which is assumed to point to a buffer containing at least 3 values whose type is given as the template parameter T. So instead of using named references which are members of the Data class, this saves memory by populating the references at the point of access (but unfortunately, using -> and not the . operator).
In order to test how well the compiler's optimizer eliminates all of the indirection introduced by the use of Proxy, the code below includes 2 versions of main(). The #if 1 version uses the -> and [] operators, and the #if 0 version performs the equivalent set of procedures, but only by directly accessing Data::ar.
The Nci() function generates runtime integer values for initializing array elements, which prevents the optimizer from just plugging constant values directly into each std::cout << call.
For gcc 6.2, using -O3, both versions of main() generate the same assembly (toggle between #if 1 and #if 0 before the first main() to compare): https://godbolt.org/g/QqRWZb
#include <iostream>
#include <ctime>
template <typename T>
class Proxy {
public:
T &a, &b, &c;
Proxy(T* par) : a(par[0]), b(par[1]), c(par[2]) {}
Proxy* operator -> () { return this; }
};
struct Data {
int ar[3];
template <typename I> int& operator [] (I idx) { return ar[idx]; }
template <typename I> const int& operator [] (I idx) const { return ar[idx]; }
Proxy<int> operator -> () { return Proxy<int>(ar); }
Proxy<const int> operator -> () const { return Proxy<const int>(ar); }
int* begin() { return ar; }
const int* begin() const { return ar; }
int* end() { return ar + sizeof(ar)/sizeof(int); }
const int* end() const { return ar + sizeof(ar)/sizeof(int); }
};
// Nci returns an unpredictible int
inline int Nci() {
static auto t = std::time(nullptr) / 100 * 100;
return static_cast<int>(t++ % 1000);
}
#if 1
int main() {
Data d = {Nci(), Nci(), Nci()};
for(auto v : d) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << d->b << "\n";
d->b = -5;
std::cout << d[1] << "\n";
std::cout << "\n";
const Data cd = {Nci(), Nci(), Nci()};
for(auto v : cd) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << cd->c << "\n";
//cd->c = -5; // error: assignment of read-only location
std::cout << cd[2] << "\n";
}
#else
int main() {
Data d = {Nci(), Nci(), Nci()};
for(auto v : d.ar) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << d.ar[1] << "\n";
d->b = -5;
std::cout << d.ar[1] << "\n";
std::cout << "\n";
const Data cd = {Nci(), Nci(), Nci()};
for(auto v : cd.ar) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << cd.ar[2] << "\n";
//cd.ar[2] = -5;
std::cout << cd.ar[2] << "\n";
}
#endif
If reading values is enough, and efficiency is not a concern, or if you trust your compiler to optimize things well, or if struct is just that 3 bytes, you can safely do this:
char index_data(const struct data *d, size_t index) {
assert(sizeof(*d) == offsetoff(*d, c)+1);
assert(index < sizeof(*d));
char buf[sizeof(*d)];
memcpy(buf, d, sizeof(*d));
return buf[index];
}
For C++ only version, you would probably want to use static_assert to verify that struct data has standard layout, and perhaps throw exception on invalid index instead.
It is illegal, but there is a workaround:
struct data {
union {
struct {
int a;
int b;
int c;
};
int v[3];
};
};
Now you can index v:

Is it legal to index into a struct?

Regardless of how 'bad' the code is, and assuming that alignment etc are not an issue on the compiler/platform, is this undefined or broken behavior?
If I have a struct like this :-
struct data
{
int a, b, c;
};
struct data thing;
Is it legal to access a, b and c as (&thing.a)[0], (&thing.a)[1], and (&thing.a)[2]?
In every case, on every compiler and platform I tried it on, with every setting I tried it 'worked'. I'm just worried that the compiler might not realize that b and thing[1] are the same thing and stores to 'b' might be put in a register and thing[1] reads the wrong value from memory (for example). In every case I tried it did the right thing though. (I realize of course that doesn't prove much)
This is not my code; it's code I have to work with, I'm interested in whether this is bad code or broken code as the different affects my priorities for changing it a great deal :)
Tagged C and C++ . I'm mostly interested in C++ but also C if it is different, just for interest.
It is illegal 1. That's an Undefined behavior in C++.
You are taking the members in an array fashion, but here is what the C++ standard says (emphasis mine):
[dcl.array/1]: ...An object of array type contains a contiguously allocated non-empty set of N
subobjects of type T...
But, for members, there's no such contiguous requirement:
[class.mem/17]: ...;Implementation alignment requirements might cause two adjacent
members not to be allocated immediately after each other...
While the above two quotes should be enough to hint why indexing into a struct as you did isn't a defined behavior by the C++ standard, let's pick one example: look at the expression (&thing.a)[2] - Regarding the subscript operator:
[expr.post//expr.sub/1]:
A postfix expression followed by an expression in square brackets is a
postfix expression. One of the expressions shall be a glvalue of type
“array of T” or a prvalue of type “pointer to T” and the other shall
be a prvalue of unscoped enumeration or integral type. The result is
of type “T”. The type “T” shall be a completely-defined object type.66
The expression E1[E2] is identical (by definition) to ((E1)+(E2))
Digging into the bold text of the above quote: regarding adding an integral type to a pointer type (note the emphasis here)..
[expr.add/4]: When an expression that has integral type is added to or subtracted from a
pointer, the result has the type of the pointer operand. If the
expression P points to element x[i] of an array object x
with n elements, the expressions P + J and J + P (where J has
the value j) point to the (possibly-hypothetical) element x[i + j]
if 0 ≤ i + j ≤ n; otherwise, the behavior is undefined. ...
Note the array requirement for the if clause; else the otherwise in the above quote. The expression (&thing.a)[2] obviously doesn't qualify for the if clause; Hence, Undefined Behavior.
On a side note: Though I have extensively experimented the code and its variations on various compilers and they don't introduce any padding here, (it works); from a maintenance view, the code is extremely fragile. you should still assert that the implementation allocated the members contiguously before doing this. And stay in-bounds :-). But its still Undefined behavior....
Some viable workarounds (with defined behavior) have been provided by other answers.
As rightly pointed out in the comments, [basic.lval/8], which was in my previous edit doesn't apply. Thanks #2501 and #M.M.
1: See #Barry's answer to this question for the only one legal case where you can access thing.a member of the struct via this parttern.
No. In C, this is undefined behavior even if there is no padding.
The thing that causes undefined behavior is out-of-bounds access1. When you have a scalar (members a,b,c in the struct) and try to use it as an array2 to access the next hypothetical element, you cause undefined behavior, even if there happens to be another object of the same type at that address.
However you may use the address of the struct object and calculate the offset into a specific member:
struct data thing = { 0 };
char* p = ( char* )&thing + offsetof( thing , b );
int* b = ( int* )p;
*b = 123;
assert( thing.b == 123 );
This has to be done for each member individually, but can be put into a function that resembles an array access.
1 (Quoted from: ISO/IEC 9899:201x 6.5.6 Additive operators 8)
If the result points one past the last element of the array object, it
shall not be used as the operand of a unary * operator that is evaluated.
2 (Quoted from: ISO/IEC 9899:201x 6.5.6 Additive operators 7)
For the purposes of these operators, a pointer to an object that is not an element of an
array behaves the same as a pointer to the first element of an array of length one with the
type of the object as its element type.
In C++ if you really need it - create operator[]:
struct data
{
int a, b, c;
int &operator[]( size_t idx ) {
switch( idx ) {
case 0 : return a;
case 1 : return b;
case 2 : return c;
default: throw std::runtime_error( "bad index" );
}
}
};
data d;
d[0] = 123; // assign 123 to data.a
it is not only guaranteed to work but usage is simpler, you do not need to write unreadable expression (&thing.a)[0]
Note: this answer is given in assumption that you already have a structure with fields, and you need to add access via index. If speed is an issue and you can change the structure this could be more effective:
struct data
{
int array[3];
int &a = array[0];
int &b = array[1];
int &c = array[2];
};
This solution would change size of structure so you can use methods as well:
struct data
{
int array[3];
int &a() { return array[0]; }
int &b() { return array[1]; }
int &c() { return array[2]; }
};
For c++: If you need to access a member without knowing its name, you can use a pointer to member variable.
struct data {
int a, b, c;
};
typedef int data::* data_int_ptr;
data_int_ptr arr[] = {&data::a, &data::b, &data::c};
data thing;
thing.*arr[0] = 123;
In ISO C99/C11, union-based type-punning is legal, so you can use that instead of indexing pointers to non-arrays (see various other answers).
ISO C++ doesn't allow union-based type-punning. GNU C++ does, as an extension, and I think some other compilers that don't support GNU extensions in general do support union type-punning. But that doesn't help you write strictly portable code.
With current versions of gcc and clang, writing a C++ member function using a switch(idx) to select a member will optimize away for compile-time constant indices, but will produce terrible branchy asm for runtime indices. There's nothing inherently wrong with switch() for this; this is simply a missed-optimization bug in current compilers. They could compiler Slava' switch() function efficiently.
The solution/workaround to this is to do it the other way: give your class/struct an array member, and write accessor functions to attach names to specific elements.
struct array_data
{
int arr[3];
int &operator[]( unsigned idx ) {
// assert(idx <= 2);
//idx = (idx > 2) ? 2 : idx;
return arr[idx];
}
int &a(){ return arr[0]; } // TODO: const versions
int &b(){ return arr[1]; }
int &c(){ return arr[2]; }
};
We can have a look at the asm output for different use-cases, on the Godbolt compiler explorer. These are complete x86-64 System V functions, with the trailing RET instruction omitted to better show what you'd get when they inline. ARM/MIPS/whatever would be similar.
# asm from g++6.2 -O3
int getb(array_data &d) { return d.b(); }
mov eax, DWORD PTR [rdi+4]
void setc(array_data &d, int val) { d.c() = val; }
mov DWORD PTR [rdi+8], esi
int getidx(array_data &d, int idx) { return d[idx]; }
mov esi, esi # zero-extend to 64-bit
mov eax, DWORD PTR [rdi+rsi*4]
By comparison, #Slava's answer using a switch() for C++ makes asm like this for a runtime-variable index. (Code in the previous Godbolt link).
int cpp(data *d, int idx) {
return (*d)[idx];
}
# gcc6.2 -O3, using `default: __builtin_unreachable()` to promise the compiler that idx=0..2,
# avoiding an extra cmov for idx=min(idx,2), or an extra branch to a throw, or whatever
cmp esi, 1
je .L6
cmp esi, 2
je .L7
mov eax, DWORD PTR [rdi]
ret
.L6:
mov eax, DWORD PTR [rdi+4]
ret
.L7:
mov eax, DWORD PTR [rdi+8]
ret
This is obviously terrible, compared to the C (or GNU C++) union-based type punning version:
c(type_t*, int):
movsx rsi, esi # sign-extend this time, since I didn't change idx to unsigned here
mov eax, DWORD PTR [rdi+rsi*4]
In C++, this is mostly undefined behavior (it depends on which index).
From [expr.unary.op]:
For purposes of pointer
arithmetic (5.7) and comparison (5.9, 5.10), an object that is not an array element whose address is taken in
this way is considered to belong to an array with one element of type T.
The expression &thing.a is thus considered to refer to an array of one int.
From [expr.sub]:
The expression E1[E2] is identical (by definition) to *((E1)+(E2))
And from [expr.add]:
When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i + j] if 0 <= i + j <= n; otherwise, the behavior is undefined.
(&thing.a)[0] is perfectly well-formed because &thing.a is considered an array of size 1 and we're taking that first index. That is an allowed index to take.
(&thing.a)[2] violates the precondition that 0 <= i + j <= n, since we have i == 0, j == 2, n == 1. Simply constructing the pointer &thing.a + 2 is undefined behavior.
(&thing.a)[1] is the interesting case. It doesn't actually violate anything in [expr.add]. We're allowed to take a pointer one past the end of the array - which this would be. Here, we turn to a note in [basic.compound]:
A value of a pointer type that is a pointer to or past the end of an object represents the address of the
first byte in memory (1.7) occupied by the object53 or the first byte in memory after the end of the storage
occupied by the object, respectively. [ Note: A pointer past the end of an object (5.7) is not considered to
point to an unrelated object of the object’s type that might be located at that address.
Hence, taking the pointer &thing.a + 1 is defined behavior, but dereferencing it is undefined because it does not point to anything.
This is undefined behavior.
There are lots of rules in C++ that attempt to give the compiler some hope of understanding what you are doing, so it can reason about it and optimize it.
There are rules about aliasing (accessing data through two different pointer types), array bounds, etc.
When you have a variable x, the fact that it isn't a member of an array means that the compiler can assume that no [] based array access can modify it. So it doesn't have to constantly reload the data from memory every time you use it; only if someone could have modified it from its name.
Thus (&thing.a)[1] can be assumed by the compiler to not refer to thing.b. It can use this fact to reorder reads and writes to thing.b, invalidating what you want it to do without invalidating what you actually told it to do.
A classic example of this is casting away const.
const int x = 7;
std::cout << x << '\n';
auto ptr = (int*)&x;
*ptr = 2;
std::cout << *ptr << "!=" << x << '\n';
std::cout << ptr << "==" << &x << '\n';
here you typically get a compiler saying 7 then 2 != 7, and then two identical pointers; despite the fact that ptr is pointing at x. The compiler takes the fact that x is a constant value to not bother reading it when you ask for the value of x.
But when you take the address of x, you force it to exist. You then cast away const, and modify it. So the actual location in memory where x is has been modified, the compiler is free to not actually read it when reading x!
The compiler may get smart enough to figure out how to even avoid following ptr to read *ptr, but often they are not. Feel free to go and use ptr = ptr+argc-1 or somesuch confusion if the optimizer is getting smarter than you.
You can provide a custom operator[] that gets the right item.
int& operator[](std::size_t);
int const& operator[](std::size_t) const;
having both is useful.
Heres a way to use a proxy class to access elements in a member array by name. It is very C++, and has no benefit vs. ref-returning accessor functions, except for syntactic preference. This overloads the -> operator to access elements as members, so to be acceptable, one needs to both dislike the syntax of accessors (d.a() = 5;), as well as tolerate using -> with a non-pointer object. I expect this might also confuse readers not familiar with the code, so this might be more of a neat trick than something you want to put into production.
The Data struct in this code also includes overloads for the subscript operator, to access indexed elements inside its ar array member, as well as begin and end functions, for iteration. Also, all of these are overloaded with non-const and const versions, which I felt needed to be included for completeness.
When Data's -> is used to access an element by name (like this: my_data->b = 5;), a Proxy object is returned. Then, because this Proxy rvalue is not a pointer, its own -> operator is auto-chain-called, which returns a pointer to itself. This way, the Proxy object is instantiated and remains valid during evaluation of the initial expression.
Contruction of a Proxy object populates its 3 reference members a, b and c according to a pointer passed in the constructor, which is assumed to point to a buffer containing at least 3 values whose type is given as the template parameter T. So instead of using named references which are members of the Data class, this saves memory by populating the references at the point of access (but unfortunately, using -> and not the . operator).
In order to test how well the compiler's optimizer eliminates all of the indirection introduced by the use of Proxy, the code below includes 2 versions of main(). The #if 1 version uses the -> and [] operators, and the #if 0 version performs the equivalent set of procedures, but only by directly accessing Data::ar.
The Nci() function generates runtime integer values for initializing array elements, which prevents the optimizer from just plugging constant values directly into each std::cout << call.
For gcc 6.2, using -O3, both versions of main() generate the same assembly (toggle between #if 1 and #if 0 before the first main() to compare): https://godbolt.org/g/QqRWZb
#include <iostream>
#include <ctime>
template <typename T>
class Proxy {
public:
T &a, &b, &c;
Proxy(T* par) : a(par[0]), b(par[1]), c(par[2]) {}
Proxy* operator -> () { return this; }
};
struct Data {
int ar[3];
template <typename I> int& operator [] (I idx) { return ar[idx]; }
template <typename I> const int& operator [] (I idx) const { return ar[idx]; }
Proxy<int> operator -> () { return Proxy<int>(ar); }
Proxy<const int> operator -> () const { return Proxy<const int>(ar); }
int* begin() { return ar; }
const int* begin() const { return ar; }
int* end() { return ar + sizeof(ar)/sizeof(int); }
const int* end() const { return ar + sizeof(ar)/sizeof(int); }
};
// Nci returns an unpredictible int
inline int Nci() {
static auto t = std::time(nullptr) / 100 * 100;
return static_cast<int>(t++ % 1000);
}
#if 1
int main() {
Data d = {Nci(), Nci(), Nci()};
for(auto v : d) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << d->b << "\n";
d->b = -5;
std::cout << d[1] << "\n";
std::cout << "\n";
const Data cd = {Nci(), Nci(), Nci()};
for(auto v : cd) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << cd->c << "\n";
//cd->c = -5; // error: assignment of read-only location
std::cout << cd[2] << "\n";
}
#else
int main() {
Data d = {Nci(), Nci(), Nci()};
for(auto v : d.ar) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << d.ar[1] << "\n";
d->b = -5;
std::cout << d.ar[1] << "\n";
std::cout << "\n";
const Data cd = {Nci(), Nci(), Nci()};
for(auto v : cd.ar) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << cd.ar[2] << "\n";
//cd.ar[2] = -5;
std::cout << cd.ar[2] << "\n";
}
#endif
If reading values is enough, and efficiency is not a concern, or if you trust your compiler to optimize things well, or if struct is just that 3 bytes, you can safely do this:
char index_data(const struct data *d, size_t index) {
assert(sizeof(*d) == offsetoff(*d, c)+1);
assert(index < sizeof(*d));
char buf[sizeof(*d)];
memcpy(buf, d, sizeof(*d));
return buf[index];
}
For C++ only version, you would probably want to use static_assert to verify that struct data has standard layout, and perhaps throw exception on invalid index instead.
It is illegal, but there is a workaround:
struct data {
union {
struct {
int a;
int b;
int c;
};
int v[3];
};
};
Now you can index v:

C++11: reinterpreting array of structs as array of struct's member

Consider the following type:
struct S
{
char v;
};
Given an array of const S, is it possible to, in a standard conformant way, reinterpret it as an array of const char whose elements correspond to the value of the member v for each of the original array's elements, and vice-versa? For example:
const S a1[] = { {'a'}, {'4'}, {'2'}, {'\0'} };
const char* a2 = reinterpret_cast< const char* >(a1);
for (int i = 0; i < 4; ++i)
std::cout << std::boolalpha << (a1[i].v == a2[i]) << ' ';
Is the code above portable and would it print true true true true? If not, is there any other way of achieving this?
Obviously, it is possible to create a new array and initialize it with the member v of each element of the original array, but the whole idea is to avoid creating a new array.
Trivially, no - the struct may have padding. And that flat out breaks any reinterpretation as an array.
Formally the struct may have padding so that its size is greater than 1.
I.e., formally you can't reinterpret_cast and have fully portable code, except for ¹an array of only one item.
But for the in-practice, some years ago someone asked if there was now any compiler that by default would give sizeof(T) > 1 for struct T{ char x; };. I have yet to see any example. So in practice one can just static_assert that the size is 1, and not worry at all that this static_assert will fail on some system.
I.e.,
S const a1[] = { {'a'}, {'4'}, {'2'}, {'\0'} };
static_assert( sizeof( S ) == 1, "!" );
char const* const a2 = reinterpret_cast<char const*>( a1 );
for( int i = 0; i < 4; ++i )
{
assert( a1[i].v == a2[i] );
}
Since it's possible to interpret the C++14 and later standards in a way where the indexing has Undefined Behavior, based on a peculiar interpretation of "array" as referring to some original array, one might instead write this code in a more awkward and verbose but guaranteed valid way:
// I do not recommend this, but it's one way to avoid problems with some compiler that's
// based on an unreasonable, impractical interpretation of the C++14 standard.
#include <assert.h>
#include <new>
auto main() -> int
{
struct S
{
char v;
};
int const compiler_specific_overhead = 0; // Redefine per compiler.
// With value 0 for the overhead the internal workings here, what happens
// in the machine code, is the same as /without/ this verbose work-around
// for one impractical interpretation of the standard.
int const n = 4;
static_assert( sizeof( S ) == 1, "!" );
char storage[n + compiler_specific_overhead];
S* const a1 = ::new( storage ) S[n];
assert( (void*)a1 == storage + compiler_specific_overhead );
for( int i = 0; i < n; ++i ) { a1[i].v = "a42"[i]; } // Whatever
// Here a2 points to items of the original `char` array, hence no indexing
// UB even with impractical interpretation of the C++14 standard.
// Note that the indexing-UB-free code from this point, is exactly the same
// source code as the first code example that some claim has indexing UB.
char const* const a2 = reinterpret_cast<char const*>( a1 );
for( int i = 0; i < n; ++i )
{
assert( a1[i].v == a2[i] );
}
}
Notes:
¹ The standard guarantees that there's no padding at the start of the struct.
The pointer arithmetic in a2[i] is undefined, see C++14 5.7 [expr.add] p7:
For addition or subtraction, if the expressions P or Q have type "pointer to cv T", where T and the array element type are not similar (4.5), the behavior is undefined. [ Note: In particular, a pointer to a base class cannot be used for pointer arithmetic when the array contains objects of a derived class type. — end note ]
Because of this rule, even if there is no padding and the sizes match, type-based alias analysis allows the compiler to assume that a1[i] and a2[i] do not overlap (because the pointer arithmetic is only valid if a2 really is an array of char not just something with the same size and alignment, and if it's really an array of char it must be a separate object from an array of S).
I think I'd be inclined to use a compile-time transformation if the source data is constant:
#include <iostream>
#include <array>
struct S
{
char v;
};
namespace detail {
template<std::size_t...Is>
constexpr auto to_cstring(const S* p, std::index_sequence<Is...>)
{
return std::array<char, sizeof...(Is)> {
p[Is].v...
};
}
}
template<std::size_t N>
constexpr auto to_cstring(const S (&arr)[N])
{
return detail::to_cstring(arr, std::make_index_sequence<N>());
}
int main()
{
const /*expr if you wish*/ S a1[] = { {'a'}, {'4'}, {'2'}, {'\0'} };
const /*expr if you wish*/ auto a2 = to_cstring(a1);
for (int i = 0; i < 4; ++i)
std::cout << std::boolalpha << (a1[i].v == a2[i]) << ' ';
}
output:
true true true true
even when the data is not a constexpr, gcc and clang are pretty good at constant folding complex sequences like this.

Why are bool values compared bitwise in C++ rather than by their meaning?

I would expect the following code to produce equality, but bool values are shown to be different.
#include <iostream>
union crazyBool
{
unsigned char uc;
bool b;
};
int main()
{
crazyBool a, b;
a.uc = 1;
b.uc = 5;
if(a.b == b.b)
{
std::cout << "==" << std::endl;
}
else
{
std::cout << "!=" << std::endl;
}
bool x, y;
void *xVP = &x, *yVP = &y;
unsigned char *xP = static_cast<unsigned char*>(xVP);
unsigned char *yP = static_cast<unsigned char*>(yVP);
(*xP) = (unsigned char)1;
(*yP) = (unsigned char)5;
if(x == y)
{
std::cout << "==" << std::endl;
}
else
{
std::cout << "!=" << std::endl;
}
return 0;
}
Note that here we are not only changing the value through union (which was pointed out as being undefined), but also accessing memory directly via void pointer.
If you assign to a member of a union, that member becomes the active member.
Reading any member other than the active one is undefined behavior, meaning that anything could happen. There are a few specific exceptions to this rule, depending on the version of the C++ standard your compiler is following; but none of these exceptions apply in your case.
(One of the exceptions is that if the union has multiple members of class type where the classes have the same initial members, these shared members can be read through any of the union's members.)
Edit to address the question clarification:
The standard defines bool as having the values true and false, no others. (C++11, 3.9.1/6) It never defines what it means to copy some other bit pattern over the storage (which is what you are doing). Since it doesn't define it, the behavior is undefined too.
Yes, when converting an integer to a bool 0 becomes false and anything else becomes true, but that is just a conversion rule, not a statement about bool's representation.