Guaranteed memory layout for standard layout struct with a single array member of primitive type
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
Consider the following simple struct:
struct A
{
float data[16];
};
My question is:
Assuming a platform where float is a 32-bit IEEE754 floating point number (if that matters at all), does the C++ standard guarantee the expected memory layout for struct A? If not, what does it guarantee and/or what are the ways to enforce the guarantees?
By the expected memory layout I mean that the struct takes up 16*4=64 bytes in memory, each consecutive 4 bytes occupied by a single float from the data array. In other words, expected memory layout means the following test passes:
static_assert(sizeof(A) == 16 * sizeof(float));
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
(offsetof is legal here since A is standard layout, see below)
In case this bothers you, the test actually passes on wandbox with gcc 9 HEAD. I have never met a combination of a platform and compiler which would provide evidence that this test may fail, and I would love to learn about them in case they do exist.
Why would one even care:
- SSE-like optimizations require certain memory layout (and alignment, which I ignore in this question, since it can be dealt with using the standard
alignasspecifier). - Serialization of such a struct would simply boil down to a nice and portable
write_bytes(&x, sizeof(A)). - Some APIs (e.g. OpenGL, specifically, say, glUniformMatrix4fv) expect this exact memory layout. Of course, one could just pass the pointer to
dataarray to pass a single object of this type, but for a sequence of these (say, for uploading matrix-type vertex attributes) a specific memory layout is still needed.
What is actually guaranteed:
These are the things that, to my knowledge, can be expected from struct A:
- It is standard layout
- As a consequence of being standard-layout, a pointer to
Acan bereinterpret_castto a pointer to its first data member (which is, presumably,data[0]?), i.e. there is no padding before the first member.
The two remaining guarantees that are not (as to my knowledge) provided by the standard are:
- There is no padding in between elements of an array of primitive type (I am sure that this is false, but I failed to find a confirmative reference),
- There is no padding after the
dataarray insidestruct A.
c++ language-lawyer memory-layout standard-layout
|
show 2 more comments
Consider the following simple struct:
struct A
{
float data[16];
};
My question is:
Assuming a platform where float is a 32-bit IEEE754 floating point number (if that matters at all), does the C++ standard guarantee the expected memory layout for struct A? If not, what does it guarantee and/or what are the ways to enforce the guarantees?
By the expected memory layout I mean that the struct takes up 16*4=64 bytes in memory, each consecutive 4 bytes occupied by a single float from the data array. In other words, expected memory layout means the following test passes:
static_assert(sizeof(A) == 16 * sizeof(float));
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
(offsetof is legal here since A is standard layout, see below)
In case this bothers you, the test actually passes on wandbox with gcc 9 HEAD. I have never met a combination of a platform and compiler which would provide evidence that this test may fail, and I would love to learn about them in case they do exist.
Why would one even care:
- SSE-like optimizations require certain memory layout (and alignment, which I ignore in this question, since it can be dealt with using the standard
alignasspecifier). - Serialization of such a struct would simply boil down to a nice and portable
write_bytes(&x, sizeof(A)). - Some APIs (e.g. OpenGL, specifically, say, glUniformMatrix4fv) expect this exact memory layout. Of course, one could just pass the pointer to
dataarray to pass a single object of this type, but for a sequence of these (say, for uploading matrix-type vertex attributes) a specific memory layout is still needed.
What is actually guaranteed:
These are the things that, to my knowledge, can be expected from struct A:
- It is standard layout
- As a consequence of being standard-layout, a pointer to
Acan bereinterpret_castto a pointer to its first data member (which is, presumably,data[0]?), i.e. there is no padding before the first member.
The two remaining guarantees that are not (as to my knowledge) provided by the standard are:
- There is no padding in between elements of an array of primitive type (I am sure that this is false, but I failed to find a confirmative reference),
- There is no padding after the
dataarray insidestruct A.
c++ language-lawyer memory-layout standard-layout
3
The first of your two remaining guarantees is guaranteed by C++ 2017 (draft n4659) 11.3.4, “Arrays” [dcl.array]: “An object of array type contains a contiguously allocated non-empty set ofNsubobjects of typeT.” 1998 edition has identical text except with hyphenated “sub-objects” in 8.3.4.
– Eric Postpischil
13 hours ago
@EricPostpischil Thank you for clarification! What exactly does "contiguously allocated" mean in this context?
– lisyarus
13 hours ago
@lisyarus: It is “plain English,” or at least English as used by practitioners in the field—it is not formally defined in the standard. I am quite sure it means the bytes of the elements in the array are laid out in memory one after the other with no padding between elements.
– Eric Postpischil
13 hours ago
4
In C, the second of the remaining guarantees is not guaranteed, and there are some reasons a “difficult” C implementation might pad a structure containing a single array. For example, we can imagine an implementation would padstruct { char x[2]; }to four bytes if its target hardware had a strong bias toward four-byte word addressing of memory, and the implementation had decided to make all structures at least four-byte-aligned to satisfy the C standard’s requirement of one representation for all structure pointers. I expect C++ is similar but cannot speak confidently to it…
– Eric Postpischil
13 hours ago
… and note that is something of a “theoretical” possibility. Most likely,struct { float data[16]; }would not be given any trailing padding by any normal C or C++ implementation—there is no reason for it in any normal target platform. But, in the absence of an explicit specification in the C++ standard, the only way to guarantee it is for the project to require that any C++ implementation used to compile it satisfy this property. It could be tested with an assertion.
– Eric Postpischil
13 hours ago
|
show 2 more comments
Consider the following simple struct:
struct A
{
float data[16];
};
My question is:
Assuming a platform where float is a 32-bit IEEE754 floating point number (if that matters at all), does the C++ standard guarantee the expected memory layout for struct A? If not, what does it guarantee and/or what are the ways to enforce the guarantees?
By the expected memory layout I mean that the struct takes up 16*4=64 bytes in memory, each consecutive 4 bytes occupied by a single float from the data array. In other words, expected memory layout means the following test passes:
static_assert(sizeof(A) == 16 * sizeof(float));
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
(offsetof is legal here since A is standard layout, see below)
In case this bothers you, the test actually passes on wandbox with gcc 9 HEAD. I have never met a combination of a platform and compiler which would provide evidence that this test may fail, and I would love to learn about them in case they do exist.
Why would one even care:
- SSE-like optimizations require certain memory layout (and alignment, which I ignore in this question, since it can be dealt with using the standard
alignasspecifier). - Serialization of such a struct would simply boil down to a nice and portable
write_bytes(&x, sizeof(A)). - Some APIs (e.g. OpenGL, specifically, say, glUniformMatrix4fv) expect this exact memory layout. Of course, one could just pass the pointer to
dataarray to pass a single object of this type, but for a sequence of these (say, for uploading matrix-type vertex attributes) a specific memory layout is still needed.
What is actually guaranteed:
These are the things that, to my knowledge, can be expected from struct A:
- It is standard layout
- As a consequence of being standard-layout, a pointer to
Acan bereinterpret_castto a pointer to its first data member (which is, presumably,data[0]?), i.e. there is no padding before the first member.
The two remaining guarantees that are not (as to my knowledge) provided by the standard are:
- There is no padding in between elements of an array of primitive type (I am sure that this is false, but I failed to find a confirmative reference),
- There is no padding after the
dataarray insidestruct A.
c++ language-lawyer memory-layout standard-layout
Consider the following simple struct:
struct A
{
float data[16];
};
My question is:
Assuming a platform where float is a 32-bit IEEE754 floating point number (if that matters at all), does the C++ standard guarantee the expected memory layout for struct A? If not, what does it guarantee and/or what are the ways to enforce the guarantees?
By the expected memory layout I mean that the struct takes up 16*4=64 bytes in memory, each consecutive 4 bytes occupied by a single float from the data array. In other words, expected memory layout means the following test passes:
static_assert(sizeof(A) == 16 * sizeof(float));
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
(offsetof is legal here since A is standard layout, see below)
In case this bothers you, the test actually passes on wandbox with gcc 9 HEAD. I have never met a combination of a platform and compiler which would provide evidence that this test may fail, and I would love to learn about them in case they do exist.
Why would one even care:
- SSE-like optimizations require certain memory layout (and alignment, which I ignore in this question, since it can be dealt with using the standard
alignasspecifier). - Serialization of such a struct would simply boil down to a nice and portable
write_bytes(&x, sizeof(A)). - Some APIs (e.g. OpenGL, specifically, say, glUniformMatrix4fv) expect this exact memory layout. Of course, one could just pass the pointer to
dataarray to pass a single object of this type, but for a sequence of these (say, for uploading matrix-type vertex attributes) a specific memory layout is still needed.
What is actually guaranteed:
These are the things that, to my knowledge, can be expected from struct A:
- It is standard layout
- As a consequence of being standard-layout, a pointer to
Acan bereinterpret_castto a pointer to its first data member (which is, presumably,data[0]?), i.e. there is no padding before the first member.
The two remaining guarantees that are not (as to my knowledge) provided by the standard are:
- There is no padding in between elements of an array of primitive type (I am sure that this is false, but I failed to find a confirmative reference),
- There is no padding after the
dataarray insidestruct A.
c++ language-lawyer memory-layout standard-layout
c++ language-lawyer memory-layout standard-layout
edited 13 hours ago
lisyarus
asked 13 hours ago
lisyaruslisyarus
10.5k22952
10.5k22952
3
The first of your two remaining guarantees is guaranteed by C++ 2017 (draft n4659) 11.3.4, “Arrays” [dcl.array]: “An object of array type contains a contiguously allocated non-empty set ofNsubobjects of typeT.” 1998 edition has identical text except with hyphenated “sub-objects” in 8.3.4.
– Eric Postpischil
13 hours ago
@EricPostpischil Thank you for clarification! What exactly does "contiguously allocated" mean in this context?
– lisyarus
13 hours ago
@lisyarus: It is “plain English,” or at least English as used by practitioners in the field—it is not formally defined in the standard. I am quite sure it means the bytes of the elements in the array are laid out in memory one after the other with no padding between elements.
– Eric Postpischil
13 hours ago
4
In C, the second of the remaining guarantees is not guaranteed, and there are some reasons a “difficult” C implementation might pad a structure containing a single array. For example, we can imagine an implementation would padstruct { char x[2]; }to four bytes if its target hardware had a strong bias toward four-byte word addressing of memory, and the implementation had decided to make all structures at least four-byte-aligned to satisfy the C standard’s requirement of one representation for all structure pointers. I expect C++ is similar but cannot speak confidently to it…
– Eric Postpischil
13 hours ago
… and note that is something of a “theoretical” possibility. Most likely,struct { float data[16]; }would not be given any trailing padding by any normal C or C++ implementation—there is no reason for it in any normal target platform. But, in the absence of an explicit specification in the C++ standard, the only way to guarantee it is for the project to require that any C++ implementation used to compile it satisfy this property. It could be tested with an assertion.
– Eric Postpischil
13 hours ago
|
show 2 more comments
3
The first of your two remaining guarantees is guaranteed by C++ 2017 (draft n4659) 11.3.4, “Arrays” [dcl.array]: “An object of array type contains a contiguously allocated non-empty set ofNsubobjects of typeT.” 1998 edition has identical text except with hyphenated “sub-objects” in 8.3.4.
– Eric Postpischil
13 hours ago
@EricPostpischil Thank you for clarification! What exactly does "contiguously allocated" mean in this context?
– lisyarus
13 hours ago
@lisyarus: It is “plain English,” or at least English as used by practitioners in the field—it is not formally defined in the standard. I am quite sure it means the bytes of the elements in the array are laid out in memory one after the other with no padding between elements.
– Eric Postpischil
13 hours ago
4
In C, the second of the remaining guarantees is not guaranteed, and there are some reasons a “difficult” C implementation might pad a structure containing a single array. For example, we can imagine an implementation would padstruct { char x[2]; }to four bytes if its target hardware had a strong bias toward four-byte word addressing of memory, and the implementation had decided to make all structures at least four-byte-aligned to satisfy the C standard’s requirement of one representation for all structure pointers. I expect C++ is similar but cannot speak confidently to it…
– Eric Postpischil
13 hours ago
… and note that is something of a “theoretical” possibility. Most likely,struct { float data[16]; }would not be given any trailing padding by any normal C or C++ implementation—there is no reason for it in any normal target platform. But, in the absence of an explicit specification in the C++ standard, the only way to guarantee it is for the project to require that any C++ implementation used to compile it satisfy this property. It could be tested with an assertion.
– Eric Postpischil
13 hours ago
3
3
The first of your two remaining guarantees is guaranteed by C++ 2017 (draft n4659) 11.3.4, “Arrays” [dcl.array]: “An object of array type contains a contiguously allocated non-empty set of
N subobjects of type T.” 1998 edition has identical text except with hyphenated “sub-objects” in 8.3.4.– Eric Postpischil
13 hours ago
The first of your two remaining guarantees is guaranteed by C++ 2017 (draft n4659) 11.3.4, “Arrays” [dcl.array]: “An object of array type contains a contiguously allocated non-empty set of
N subobjects of type T.” 1998 edition has identical text except with hyphenated “sub-objects” in 8.3.4.– Eric Postpischil
13 hours ago
@EricPostpischil Thank you for clarification! What exactly does "contiguously allocated" mean in this context?
– lisyarus
13 hours ago
@EricPostpischil Thank you for clarification! What exactly does "contiguously allocated" mean in this context?
– lisyarus
13 hours ago
@lisyarus: It is “plain English,” or at least English as used by practitioners in the field—it is not formally defined in the standard. I am quite sure it means the bytes of the elements in the array are laid out in memory one after the other with no padding between elements.
– Eric Postpischil
13 hours ago
@lisyarus: It is “plain English,” or at least English as used by practitioners in the field—it is not formally defined in the standard. I am quite sure it means the bytes of the elements in the array are laid out in memory one after the other with no padding between elements.
– Eric Postpischil
13 hours ago
4
4
In C, the second of the remaining guarantees is not guaranteed, and there are some reasons a “difficult” C implementation might pad a structure containing a single array. For example, we can imagine an implementation would pad
struct { char x[2]; } to four bytes if its target hardware had a strong bias toward four-byte word addressing of memory, and the implementation had decided to make all structures at least four-byte-aligned to satisfy the C standard’s requirement of one representation for all structure pointers. I expect C++ is similar but cannot speak confidently to it…– Eric Postpischil
13 hours ago
In C, the second of the remaining guarantees is not guaranteed, and there are some reasons a “difficult” C implementation might pad a structure containing a single array. For example, we can imagine an implementation would pad
struct { char x[2]; } to four bytes if its target hardware had a strong bias toward four-byte word addressing of memory, and the implementation had decided to make all structures at least four-byte-aligned to satisfy the C standard’s requirement of one representation for all structure pointers. I expect C++ is similar but cannot speak confidently to it…– Eric Postpischil
13 hours ago
… and note that is something of a “theoretical” possibility. Most likely,
struct { float data[16]; } would not be given any trailing padding by any normal C or C++ implementation—there is no reason for it in any normal target platform. But, in the absence of an explicit specification in the C++ standard, the only way to guarantee it is for the project to require that any C++ implementation used to compile it satisfy this property. It could be tested with an assertion.– Eric Postpischil
13 hours ago
… and note that is something of a “theoretical” possibility. Most likely,
struct { float data[16]; } would not be given any trailing padding by any normal C or C++ implementation—there is no reason for it in any normal target platform. But, in the absence of an explicit specification in the C++ standard, the only way to guarantee it is for the project to require that any C++ implementation used to compile it satisfy this property. It could be tested with an assertion.– Eric Postpischil
13 hours ago
|
show 2 more comments
2 Answers
2
active
oldest
votes
One thing that is not guaranteed about the layout is endianness i.e. the order of bytes within a multi-byte object. write_bytes(&x, sizeof(A)) is not portable serialisation across systems with different endianness.
Acan bereinterpret_castto a pointer to its first data member (which is, presumably,data[0]?)
Correction: The first data member is data, which you can reinterpret cast with. And crucially, an array is not pointer-interconvertible with its first element, so you cannot reinterpret cast between them. The address however is guaranteed to be the same, so reinterpreting as data[0] should be fine after std::launder as far as I understand.
There is no padding in between elements of an array of primitive type
Arrays are guaranteed to be contiguous. sizeof of an object is specified in terms of padding required to place elements into an array. sizeof(T[10]) has exactly the size sizeof(T * 10). If there is padding between non-padding bits of adjacent elements, then that padding is at the end of the element itself.
Primitive type is not guaranteed to not have padding in general. For example, the x86 extended precision long double is 80 bits, padded to 128 bits.
char, signed char and unsigned char are guaranteed to not have padding bits. C standard (to which C++ delegates the specification in this case) guarantees that the fixed width intN_t and uintN_t aliases do not have padding bits. On systems where that is not possible, these fixed width types are not provided.
Just to be absolutely clear. Is your last paragraph is a direct counter example against 2nd unanswered question? I am asking from a compound type perspective, so for example structS {char a,b,c;};if padded to4*sizeof(char)could have padding at the end. And for that matter we cannot tell relative address of any member other thana, I think they can be reordered an padded as compiler see fit. Yup?
– luk32
13 hours ago
@luk32 There cannot possibly be a need for padding betweencharelements, as they have alignment of 1. Any sensible ABI would place the padding (if there is any) ofSat the end. But indeed, I don't know of explicit guarantee about that in the C++ standard.
– eerorika
12 hours ago
Could you, please, elaborate on this usage ofstd::launder?
– lisyarus
9 hours ago
@lisyarus en.cppreference.com/w/cpp/utility/launder
– eerorika
9 hours ago
@eerorika I apologize, but I struggle to understand the reason whystd::launderis needed here based on the cppreference article.
– lisyarus
9 hours ago
|
show 2 more comments
If a standard-layout class object has any non-static data members, its
address is the same as the address of its first non-static data member.
Otherwise, its address is the same as the address of its first base
class subobject (if any). [Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. — end note]
Hence, the standard guarantees that
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
An object of array type contains a contiguously allocated non-empty
set of N subobjects of type T.
Hence, the following are true
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55650798%2fguaranteed-memory-layout-for-standard-layout-struct-with-a-single-array-member-o%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
One thing that is not guaranteed about the layout is endianness i.e. the order of bytes within a multi-byte object. write_bytes(&x, sizeof(A)) is not portable serialisation across systems with different endianness.
Acan bereinterpret_castto a pointer to its first data member (which is, presumably,data[0]?)
Correction: The first data member is data, which you can reinterpret cast with. And crucially, an array is not pointer-interconvertible with its first element, so you cannot reinterpret cast between them. The address however is guaranteed to be the same, so reinterpreting as data[0] should be fine after std::launder as far as I understand.
There is no padding in between elements of an array of primitive type
Arrays are guaranteed to be contiguous. sizeof of an object is specified in terms of padding required to place elements into an array. sizeof(T[10]) has exactly the size sizeof(T * 10). If there is padding between non-padding bits of adjacent elements, then that padding is at the end of the element itself.
Primitive type is not guaranteed to not have padding in general. For example, the x86 extended precision long double is 80 bits, padded to 128 bits.
char, signed char and unsigned char are guaranteed to not have padding bits. C standard (to which C++ delegates the specification in this case) guarantees that the fixed width intN_t and uintN_t aliases do not have padding bits. On systems where that is not possible, these fixed width types are not provided.
Just to be absolutely clear. Is your last paragraph is a direct counter example against 2nd unanswered question? I am asking from a compound type perspective, so for example structS {char a,b,c;};if padded to4*sizeof(char)could have padding at the end. And for that matter we cannot tell relative address of any member other thana, I think they can be reordered an padded as compiler see fit. Yup?
– luk32
13 hours ago
@luk32 There cannot possibly be a need for padding betweencharelements, as they have alignment of 1. Any sensible ABI would place the padding (if there is any) ofSat the end. But indeed, I don't know of explicit guarantee about that in the C++ standard.
– eerorika
12 hours ago
Could you, please, elaborate on this usage ofstd::launder?
– lisyarus
9 hours ago
@lisyarus en.cppreference.com/w/cpp/utility/launder
– eerorika
9 hours ago
@eerorika I apologize, but I struggle to understand the reason whystd::launderis needed here based on the cppreference article.
– lisyarus
9 hours ago
|
show 2 more comments
One thing that is not guaranteed about the layout is endianness i.e. the order of bytes within a multi-byte object. write_bytes(&x, sizeof(A)) is not portable serialisation across systems with different endianness.
Acan bereinterpret_castto a pointer to its first data member (which is, presumably,data[0]?)
Correction: The first data member is data, which you can reinterpret cast with. And crucially, an array is not pointer-interconvertible with its first element, so you cannot reinterpret cast between them. The address however is guaranteed to be the same, so reinterpreting as data[0] should be fine after std::launder as far as I understand.
There is no padding in between elements of an array of primitive type
Arrays are guaranteed to be contiguous. sizeof of an object is specified in terms of padding required to place elements into an array. sizeof(T[10]) has exactly the size sizeof(T * 10). If there is padding between non-padding bits of adjacent elements, then that padding is at the end of the element itself.
Primitive type is not guaranteed to not have padding in general. For example, the x86 extended precision long double is 80 bits, padded to 128 bits.
char, signed char and unsigned char are guaranteed to not have padding bits. C standard (to which C++ delegates the specification in this case) guarantees that the fixed width intN_t and uintN_t aliases do not have padding bits. On systems where that is not possible, these fixed width types are not provided.
Just to be absolutely clear. Is your last paragraph is a direct counter example against 2nd unanswered question? I am asking from a compound type perspective, so for example structS {char a,b,c;};if padded to4*sizeof(char)could have padding at the end. And for that matter we cannot tell relative address of any member other thana, I think they can be reordered an padded as compiler see fit. Yup?
– luk32
13 hours ago
@luk32 There cannot possibly be a need for padding betweencharelements, as they have alignment of 1. Any sensible ABI would place the padding (if there is any) ofSat the end. But indeed, I don't know of explicit guarantee about that in the C++ standard.
– eerorika
12 hours ago
Could you, please, elaborate on this usage ofstd::launder?
– lisyarus
9 hours ago
@lisyarus en.cppreference.com/w/cpp/utility/launder
– eerorika
9 hours ago
@eerorika I apologize, but I struggle to understand the reason whystd::launderis needed here based on the cppreference article.
– lisyarus
9 hours ago
|
show 2 more comments
One thing that is not guaranteed about the layout is endianness i.e. the order of bytes within a multi-byte object. write_bytes(&x, sizeof(A)) is not portable serialisation across systems with different endianness.
Acan bereinterpret_castto a pointer to its first data member (which is, presumably,data[0]?)
Correction: The first data member is data, which you can reinterpret cast with. And crucially, an array is not pointer-interconvertible with its first element, so you cannot reinterpret cast between them. The address however is guaranteed to be the same, so reinterpreting as data[0] should be fine after std::launder as far as I understand.
There is no padding in between elements of an array of primitive type
Arrays are guaranteed to be contiguous. sizeof of an object is specified in terms of padding required to place elements into an array. sizeof(T[10]) has exactly the size sizeof(T * 10). If there is padding between non-padding bits of adjacent elements, then that padding is at the end of the element itself.
Primitive type is not guaranteed to not have padding in general. For example, the x86 extended precision long double is 80 bits, padded to 128 bits.
char, signed char and unsigned char are guaranteed to not have padding bits. C standard (to which C++ delegates the specification in this case) guarantees that the fixed width intN_t and uintN_t aliases do not have padding bits. On systems where that is not possible, these fixed width types are not provided.
One thing that is not guaranteed about the layout is endianness i.e. the order of bytes within a multi-byte object. write_bytes(&x, sizeof(A)) is not portable serialisation across systems with different endianness.
Acan bereinterpret_castto a pointer to its first data member (which is, presumably,data[0]?)
Correction: The first data member is data, which you can reinterpret cast with. And crucially, an array is not pointer-interconvertible with its first element, so you cannot reinterpret cast between them. The address however is guaranteed to be the same, so reinterpreting as data[0] should be fine after std::launder as far as I understand.
There is no padding in between elements of an array of primitive type
Arrays are guaranteed to be contiguous. sizeof of an object is specified in terms of padding required to place elements into an array. sizeof(T[10]) has exactly the size sizeof(T * 10). If there is padding between non-padding bits of adjacent elements, then that padding is at the end of the element itself.
Primitive type is not guaranteed to not have padding in general. For example, the x86 extended precision long double is 80 bits, padded to 128 bits.
char, signed char and unsigned char are guaranteed to not have padding bits. C standard (to which C++ delegates the specification in this case) guarantees that the fixed width intN_t and uintN_t aliases do not have padding bits. On systems where that is not possible, these fixed width types are not provided.
edited 12 hours ago
answered 13 hours ago
eerorikaeerorika
90k664136
90k664136
Just to be absolutely clear. Is your last paragraph is a direct counter example against 2nd unanswered question? I am asking from a compound type perspective, so for example structS {char a,b,c;};if padded to4*sizeof(char)could have padding at the end. And for that matter we cannot tell relative address of any member other thana, I think they can be reordered an padded as compiler see fit. Yup?
– luk32
13 hours ago
@luk32 There cannot possibly be a need for padding betweencharelements, as they have alignment of 1. Any sensible ABI would place the padding (if there is any) ofSat the end. But indeed, I don't know of explicit guarantee about that in the C++ standard.
– eerorika
12 hours ago
Could you, please, elaborate on this usage ofstd::launder?
– lisyarus
9 hours ago
@lisyarus en.cppreference.com/w/cpp/utility/launder
– eerorika
9 hours ago
@eerorika I apologize, but I struggle to understand the reason whystd::launderis needed here based on the cppreference article.
– lisyarus
9 hours ago
|
show 2 more comments
Just to be absolutely clear. Is your last paragraph is a direct counter example against 2nd unanswered question? I am asking from a compound type perspective, so for example structS {char a,b,c;};if padded to4*sizeof(char)could have padding at the end. And for that matter we cannot tell relative address of any member other thana, I think they can be reordered an padded as compiler see fit. Yup?
– luk32
13 hours ago
@luk32 There cannot possibly be a need for padding betweencharelements, as they have alignment of 1. Any sensible ABI would place the padding (if there is any) ofSat the end. But indeed, I don't know of explicit guarantee about that in the C++ standard.
– eerorika
12 hours ago
Could you, please, elaborate on this usage ofstd::launder?
– lisyarus
9 hours ago
@lisyarus en.cppreference.com/w/cpp/utility/launder
– eerorika
9 hours ago
@eerorika I apologize, but I struggle to understand the reason whystd::launderis needed here based on the cppreference article.
– lisyarus
9 hours ago
Just to be absolutely clear. Is your last paragraph is a direct counter example against 2nd unanswered question? I am asking from a compound type perspective, so for example struct
S {char a,b,c;}; if padded to 4*sizeof(char) could have padding at the end. And for that matter we cannot tell relative address of any member other than a, I think they can be reordered an padded as compiler see fit. Yup?– luk32
13 hours ago
Just to be absolutely clear. Is your last paragraph is a direct counter example against 2nd unanswered question? I am asking from a compound type perspective, so for example struct
S {char a,b,c;}; if padded to 4*sizeof(char) could have padding at the end. And for that matter we cannot tell relative address of any member other than a, I think they can be reordered an padded as compiler see fit. Yup?– luk32
13 hours ago
@luk32 There cannot possibly be a need for padding between
char elements, as they have alignment of 1. Any sensible ABI would place the padding (if there is any) of S at the end. But indeed, I don't know of explicit guarantee about that in the C++ standard.– eerorika
12 hours ago
@luk32 There cannot possibly be a need for padding between
char elements, as they have alignment of 1. Any sensible ABI would place the padding (if there is any) of S at the end. But indeed, I don't know of explicit guarantee about that in the C++ standard.– eerorika
12 hours ago
Could you, please, elaborate on this usage of
std::launder?– lisyarus
9 hours ago
Could you, please, elaborate on this usage of
std::launder?– lisyarus
9 hours ago
@lisyarus en.cppreference.com/w/cpp/utility/launder
– eerorika
9 hours ago
@lisyarus en.cppreference.com/w/cpp/utility/launder
– eerorika
9 hours ago
@eerorika I apologize, but I struggle to understand the reason why
std::launder is needed here based on the cppreference article.– lisyarus
9 hours ago
@eerorika I apologize, but I struggle to understand the reason why
std::launder is needed here based on the cppreference article.– lisyarus
9 hours ago
|
show 2 more comments
If a standard-layout class object has any non-static data members, its
address is the same as the address of its first non-static data member.
Otherwise, its address is the same as the address of its first base
class subobject (if any). [Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. — end note]
Hence, the standard guarantees that
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
An object of array type contains a contiguously allocated non-empty
set of N subobjects of type T.
Hence, the following are true
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
add a comment |
If a standard-layout class object has any non-static data members, its
address is the same as the address of its first non-static data member.
Otherwise, its address is the same as the address of its first base
class subobject (if any). [Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. — end note]
Hence, the standard guarantees that
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
An object of array type contains a contiguously allocated non-empty
set of N subobjects of type T.
Hence, the following are true
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
add a comment |
If a standard-layout class object has any non-static data members, its
address is the same as the address of its first non-static data member.
Otherwise, its address is the same as the address of its first base
class subobject (if any). [Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. — end note]
Hence, the standard guarantees that
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
An object of array type contains a contiguously allocated non-empty
set of N subobjects of type T.
Hence, the following are true
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
If a standard-layout class object has any non-static data members, its
address is the same as the address of its first non-static data member.
Otherwise, its address is the same as the address of its first base
class subobject (if any). [Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. — end note]
Hence, the standard guarantees that
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
An object of array type contains a contiguously allocated non-empty
set of N subobjects of type T.
Hence, the following are true
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
edited 12 hours ago
answered 13 hours ago
YashasYashas
7111726
7111726
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55650798%2fguaranteed-memory-layout-for-standard-layout-struct-with-a-single-array-member-o%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
3
The first of your two remaining guarantees is guaranteed by C++ 2017 (draft n4659) 11.3.4, “Arrays” [dcl.array]: “An object of array type contains a contiguously allocated non-empty set of
Nsubobjects of typeT.” 1998 edition has identical text except with hyphenated “sub-objects” in 8.3.4.– Eric Postpischil
13 hours ago
@EricPostpischil Thank you for clarification! What exactly does "contiguously allocated" mean in this context?
– lisyarus
13 hours ago
@lisyarus: It is “plain English,” or at least English as used by practitioners in the field—it is not formally defined in the standard. I am quite sure it means the bytes of the elements in the array are laid out in memory one after the other with no padding between elements.
– Eric Postpischil
13 hours ago
4
In C, the second of the remaining guarantees is not guaranteed, and there are some reasons a “difficult” C implementation might pad a structure containing a single array. For example, we can imagine an implementation would pad
struct { char x[2]; }to four bytes if its target hardware had a strong bias toward four-byte word addressing of memory, and the implementation had decided to make all structures at least four-byte-aligned to satisfy the C standard’s requirement of one representation for all structure pointers. I expect C++ is similar but cannot speak confidently to it…– Eric Postpischil
13 hours ago
… and note that is something of a “theoretical” possibility. Most likely,
struct { float data[16]; }would not be given any trailing padding by any normal C or C++ implementation—there is no reason for it in any normal target platform. But, in the absence of an explicit specification in the C++ standard, the only way to guarantee it is for the project to require that any C++ implementation used to compile it satisfy this property. It could be tested with an assertion.– Eric Postpischil
13 hours ago