考虑以下简单结构:
struct A
{
float data[16];
};
Run Code Online (Sandbox Code Playgroud)
假设平台上float
有32位IEEE754浮点数(如果很重要),那么C ++标准是否可以保证预期的内存布局struct A
?如果不是,它将提供什么保证和/或执行保证的方式是什么?
由预期存储器布局我的意思是该结构占用16*4=64
在存储器字节,每个连续4
字节占用由单个float
从data
阵列。换句话说,预期的内存布局意味着以下测试通过:
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));
Run Code Online (Sandbox Code Playgroud)
(offsetof
这里是合法的,因为A
是标准布局,请参见下文)
万一这困扰您,测试实际上会通过 gcc 9 HEAD在wandbox上通过。我从未遇到过平台和编译器的结合,它们会提供证据证明该测试可能会失败,并且我很想了解它们的存在。
alignas
说明符来处理它)。write_bytes(&x, sizeof(A))
。data
数组以传递这种类型的单个对象,但是对于其中的一系列对象(例如,用于上传矩阵类型的顶点属性),仍然需要特定的内存布局。 …根据这个答案,从C++ 17开始,即使指针具有正确的地址,并且正确的类型解除引用它也会导致未定义的行为.
alignas(int) unsigned char buffer[2*sizeof(int)];
auto p1=new(buffer) int{};
auto p2=new(p1+1) int{};
*(p1+1)=10; // UB since c++17
Run Code Online (Sandbox Code Playgroud)
原因是指针值p1+1
是一个指针过去的对象.可以使用以下示例将此示例恢复为已定义的行为std::launder
:
*std::launder(p1+1)=10; // still UB?
Run Code Online (Sandbox Code Playgroud)
其次,在下列情况下它是否也有用?
alignas(int) unsigned char buffer[3*sizeof(int)];
auto pi = new (buffer) int{};
auto pc = reinterpret_cast<unsigned char*>(pi);//not a "pointer to" an element of buffer
//since buffer[0] and *pc
//are not pointer interconvertible
//pc+2*sizeof(int) would be UB
auto pc_valid = std::launder(pc) //pc_valid is a pointer to an element of buffer …
Run Code Online (Sandbox Code Playgroud) 我可以把T
和一个包裹T
在union
并检查他们,因为我喜欢?
union Example {
T value;
struct Wrapped {
T wrapped;
} wrapper;
};
Run Code Online (Sandbox Code Playgroud)
// for simplicity T = int
Example ex;
ex.value = 12;
cout << ex.wrapper.wrapped; // ?
Run Code Online (Sandbox Code Playgroud)
C++ 11标准只保证保存对常见初始序列的检查,但value
不是struct
.我猜答案是否定的,因为包装类型甚至不能保证与其解包对应的内存兼容,并且访问非活动成员只能在常见的初始序列上明确定义.
这个问题followes这一个
我们来考虑这个示例代码:
struct sso
{
union{
struct {
char* ptr;
char size_r[8];
} large_str;
char short_str[16];
};
bool is_short_str() const{
return *std::launder(short_str+15)=='\0'; //UB?
}
};
Run Code Online (Sandbox Code Playgroud)
如果short_str
不是取消引用指针的活动成员而std::launder
不是UB.让我们考虑ABI已经明确指定,并且我们知道size_r [7]与short_str [15]位于同一地址.并std::launder(short_str+15)
返回一个指针size_r[7]
的时候short_str
是不是工会的活跃成员?
Nota:我认为情况就是这样,因为[ptr.launder]/3
如果对象Y位于Y所占用的存储区内,则指向存储的字节可以到达,如果Y是指针可互换的对象,则指向对象Y,或者如果Y是数组元素,则指向立即封闭的数组对象.
我正在尝试创建一些POD值的本地数组(例如double
),max_size
在编译时已知固定,然后读取运行size
时值(size <= max_size
)并处理size
该数组中的第一个元素.
现在的问题是,为什么不编译器消除堆读取和写入时arr
,并size
放置到同一个struct
/ class
,而不是那里的情况arr
和size
是独立的局部变量?
这是我的代码:
#include <cstddef>
constexpr std::size_t max_size = 64;
extern void process_value(double& ref_value);
void test_distinct_array_and_size(std::size_t size)
{
double arr[max_size];
std::size_t arr_size = size;
for (std::size_t i = 0; i < arr_size; ++i)
process_value(arr[i]);
}
void test_array_and_size_in_local_struct(std::size_t size)
{
struct
{
double arr[max_size];
std::size_t size;
} array_wrapper;
array_wrapper.size = size;
for (std::size_t i = 0; i …
Run Code Online (Sandbox Code Playgroud) [1]
\n\n是否有任何情况不需要将p0593r6添加到 C++20 ( \xc2\xa7 6.7.2.11对象模型[intro.object] ) std::launder
,而需要 C++17 中的相同用例std::launder
,或者它们是完全正交?
[2]
\n\n[ptr::launder]规范中的示例是:
\n\nstruct X { int n; };\nconst X *p = new const X{3};\nconst int a = p->n;\nnew (const_cast<X*>(p)) const X{5}; // p does not point to new object ([basic.life]) because its type is const\nconst int b = p->n; // undefined behavior\nconst int c = std::launder(p)->n; // OK\n
Run Code Online (Sandbox Code Playgroud)\n\n@Nicol Bolas在这个 SO 答案中给出了另一个例子,使用指向有效存储但类型不同的指针:
\n\n …这段代码合适吗?由于某种原因,使用2维数组作为1维弃用了吗?
char tab1[3][3];
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
tab1[i][j] = (char)i;
printf("%c", ((char*)tab1)[3]); // == tab1[1][0]
Run Code Online (Sandbox Code Playgroud) 考虑到 C++17 中引入的语义变化(例如在具有正确地址和类型的指针自 C++17 以来是否仍然始终是有效指针中进行了讨论) ,XOR 链接列表以一种对我来说看起来很可疑的方式使用指针算术。)。它们现在会导致未定义的行为吗?如果是这样,可以通过洗涤来拯救它们吗?
编辑:
维基百科文章仅包含有关指针和整数之间转换的简短注释。我默认(现在明确声明)指针首先被转换为足够大小的整数类型以适合它们,然后对整数进行异或。因此,操作理论中列出的 XOR 属性保证只有从指针获得一次的整数才会被转换回它们。根据标准,从指针到整数的实际映射可以是任意注入。除此之外我不依赖任何假设。
标准是否允许使用它们并访问仍然存在的对象?C++17 之前?从 C++17 开始?
如果你知道两条信息:
然后你逻辑上拥有引用该对象所需的一切:
#include <iostream>
using namespace std;
int main()
{
int x = 1, y = 2;
int* p = (&x) + 1;
if ((long)&y == (long)p)
cout << "p now contains &y\n";
if (*p == y)
cout << "it also dereference to y\n";
}
Run Code Online (Sandbox Code Playgroud)
但是,根据C++标准,这是不合法的.它适用于我尝试过的几个编译器,但它是未定义的行为.
问题是:为什么?
c++ ×9
c++17 ×3
arrays ×2
pointers ×2
unions ×2
boost ×1
c++20 ×1
compilation ×1
optimization ×1
stdlaunder ×1
struct ×1
types ×1