gpt4 book ai didi

c++ - mmap 和 C++ 严格的别名规则

转载 作者:搜寻专家 更新时间:2023-10-31 00:28:24 24 4
gpt4 key购买 nike

考虑一个符合 POSIX.1-2008 标准的操作系统,让 fd 成为一个有效的文件描述符(对于打开的文件、读取模式、足够的数据...)。以下代码符合 C++11 标准*(忽略错误检查):

void* map = mmap(NULL, sizeof(int)*10, PROT_READ, MAP_PRIVATE, fd, 0);
int* foo = static_cast<int*>(map);

现在,下面的指令是否违反了严格的别名规则?

int bar = *foo;

根据标准:

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

  • the dynamic type of the object,
  • a cv-qualified version of the dynamic type of the object,
  • a type similar (as defined in 4.4) to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
  • a char or unsigned char type.

map/foo 指向的对象的动态类型是什么?那甚至是一个对象吗?标准说:

The lifetime of an object of type T begins when: storage with the proper alignment and size for type T is obtained, and if the object has non-trivial initialization, its initialization is complete.

这是否意味着映射内存包含10个int对象(假设初始地址对齐)?但如果它是真的,这是否也适用于这段代码(这显然打破了严格的别名)?

char baz[sizeof(int)];
int* p=reinterpret_cast<int*>(&baz);
*p=5;

奇怪的是,这是否意味着声明 baz 会启动任何(正确对齐的)大小为 4 的对象的生命周期?


一些上下文:我正在映射一个文件,其中包含我希望直接访问的一大块数据。由于这个 block 很大,我想避免 memcpy-ing 到临时对象。


* nullptr 可以在这里代替 NULL 吗,它是否隐式转换为 NULL?是否引用了标准?

最佳答案

我相信简单的转换确实违反了严格的别名。令人信服地争论高于我的薪水等级,所以这是一种解决方法的尝试:

template<class T>
T* launder_raw_pod_at( void* ptr ) {
static_assert( std::is_pod<T>::value, "this only works with plain old data" );
char buff[sizeof(T)];
std::memcpy( buff, ptr, sizeof(T) );
T* r = ::new(ptr) T;
std::memcpy( ptr, buff, sizeof(T) );
return r;
}

我相信上面的代码对内存有零可观察到的副作用,并返回一个指向位置 ptr 的合法 T* 的指针。

检查您的编译器是否将上述代码优化为 noop。为此,它必须在真正基础的层面上理解 memcpy,并且构造一个T 不必对那里的内存做任何事情。

At least clang 4.0.0 can optimize this operation away .

我们所做的是首先将字节复制。然后我们使用 placement new 来在那里创建一个 T。最后,我们将字节复制回来。

我们有一个合法创建的 T,其中包含我们想要的字节。

但是复制和返回都是到本地缓冲区,所以它没有可观察到的效果。

对象的构造,如果是一个pod,也不必接触字节;从技术上讲,字节是未定义的。但是聪明的编译器会说“什么都不做”。

因此编译器可以计算出所有这些操作都可以在运行时跳过。同时,我们在抽象机中正确地创建了一个在该位置具有正确字节的对象。 (假设它有有效的对齐方式!但这不是这段代码的问题。)

关于c++ - mmap 和 C++ 严格的别名规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45310822/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com