I encountered a problem, passing struct member to a template function. The function's goal is to take the address and size of the member. Here is simple example:
This is the struct. It has packed attribute.
struct TestStruct {
unsigned char elem1;
unsigned char elem2;
uint64_t elem3;
char buf[10000];
int elem4;
unsigned char elem5;
}
__attribute__ ((packed));
this is the template function, which should get a member's address
template<typename T>
void addData(const T &val)
{
printf ("address inside func: %p \n",&val);
}
int main(int argc, char *argv[])
{
TestStruct testdata;
testdata.elem4 = 0;
printf ("struct address is: %p \n",&testdata);
printf ("elem4 address is: %p \n",&testdata.elem4);
addData(testdata.elem4);
return 0;
}
The problem: When attribute ((packed)); is set (like in the example) the template function receives wrong address of the member:
Output:
struct address is: 0x7fff735bb4e0
elem4 address is: 0x7fff735bdbfa
address inside func: 0x7fff735bb4dc
If I remove the "packed" attribute, everything is OK. There is no error and no warning (even with -Wall -Wextra), but not the right address is passed to the function.
I read this:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36566
and found that there is an issue, getting references to packed-struct members. Interesting enough, replacing const T& with T& in my template function, produces the error message:
error: cannot bind packed field ‘testdata.TestStruct::elem4’ to ‘int&’
So, I have 2 questions:
Why cannot packed-struct members be passed as const references, when their address can be passed as pointer
What happens in the const T& case? There is no error, no warning, but the incorrect address is passed to the function. As we know, the address of reference is the address of the variable, the reference points to.
Both of your questions are answered at the link you posted.
1. Why cannot packed-struct members be passed as const references, when their address can be passed as pointer
Gabriel M. Beddingfield wrote in his comment:
All assignments of obj.s to type short& and short* are incorrect, and ideally they would all result in compiler errors.
The C++ spec (C++03, Sects. 3.9, 3.9.1, 3.9.2) are very clear that T and "pointer to T" have implementation-specific alignment requirements. If you have a "pointer to T" then you may assume that it meets the alignment requirements. I'm sure the C spec has similar language.
I can only add to this the corresponding quote from C++14 standard ([basic.align]/1):
Object types have alignment requirements (3.9.1, 3.9.2) which place restrictions on the addresses at which an object of that type may be allocated. An alignment is an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated. An object type imposes an alignment requirement on every object of that type; stricter alignment can be requested using the alignment specifier
The bottom line of it is that even taking an addres of a packed struct member should be an error.
2. What happens in the const T& case? There is no error, no warning, but the incorrect address is passed to the function. As we know, the address of reference is the address of the variable, the reference points to.
Jonathan Wakely wrote:
A const-reference causes a temporary to be created, you didn't bind to the packed field
The bottom line is that it's not a bug itself that you can't bind a non-const reference to a packed struct field, a bug is that at the same time you can take an address of it. The compiler should either allow or disallow both.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With