当前位置: 首页 > 知识库问答 >
问题:

严格别名冲突和内存地址

崔博延
2023-03-14

我试图理解C和C的严格别名规则。我已经问了很多关于这个问题的问题,并做了一些阅读,但我只想澄清一些事情。

// void* can alias any other type:
int anInt;
void* pToVoid = (void*)&anInt; // Allowed
// So can char*
char* pToChar = (char*)&anInt; // Allowed

指向任何类型的指针都可以别名void*,这就是为什么我们可以这样做:

int* myNewInt = (int*)malloc(sizeof(int));

但是:

(问题1)任何指针类型都可以别名char指针吗?

    char myChars[4];
    int* pInt = (int*)myChars; // Is this allowed?
// I'm guessing so because this is how we create buffers
    float* pFloat = (float*) pInt; // I know this is strict aliasing violation

问题2:此外,当将任何指针类型别名为char或void指针类型时,我们需要确保正确的对齐方式,对吗?在堆栈上不能保证char或char数组在我们从新或malloc获得它时会对齐,对吧?

我的第三个问题是,当您投射指针或指针别名相同的内存时,是否违反了严格的别名规则?例如:

struct MyStruct
{
    int myInt;
    float myFloat;
};

int main()
{
    MyStruct myStructObj;
    float* pFloat = &myStructObj.myInt; // This is aliasing the wrong type, not allowed
// However if I move the float* then it no longer aliases the wrong type
    pFloat += 1;
// Now the pointer points to the right type. However is it now too late? My program
// has UB because I first aliased the pointer in the first place?
// On the other hand I assume this is allowed though:
   float pFloat = (float*)(((char*)&myStructObj.myInt) + sizeof(int));
// This way the float pointer never aliases the int, the int pointer is 
// first cast to char*, then char* to float*, which I assume is allowed.
}

换句话说,访问相同内存或分配不同指针类型的严格别名规则是什么?因为如果它只是关于内存访问,那么我将float*赋值给int*的例子很好,因为我先移动它,对吗?

编辑:已经指出C和C的别名规则是不同的,因此我将其标记为关于C。

共有2个答案

宋宇
2023-03-14

问题:< br >在C #中,别名完全与寄存器的使用有关。浮点值保存在与整数不同的寄存器中并不罕见。实际上,当编译器缓存一个值时,它必须将其绑定到一个寄存器。如果将相同的值作为不同的类型加载,最终可能会有多个寄存器绑定到相同的值(比如,一个寄存器使用int值,一个寄存器使用float版本)。< br >解决这个问题的一种方法是告诉编译器总是写入内存(禁用一些寄存器缓存-使代码更慢)< br >另一种方法是向编译器promise它可以以任何它认为合适的方式缓存,并且您永远不会更改类型。

实用程序< br >现代的解决方案是(spans)在旧的html" target="_blank">编译器gsl::span上创建一个库来支持这个解决方案。两种解决方案都支持“as_bytes”函数。

安全处理此类问题的现代方法是将内存建模为 std::bytes(旧代码中的 gsl::bytes)。由于此答案显示 std::byte 指针具有与 char*? 相同的混叠含义,因此标准指定 std::byte 从不容易出现混叠问题。此解决方案确实强制您显式复制,这可能是设计使然。

更新:
您的问题:

    < li >是的,任何指向char的指针都可以避免别名问题(但最好是byte) < li >如果您将对象创建为它们自己的类型,并生成字节跨度,那么对齐就不是问题。字节没有对齐。 < li >当不同类型的指针指向同一个内存时,就会违反别名。如上所述,这有创建两个缓存值的风险,这两个缓存值可能会变得不同步。

更新:刚刚发现https://en.cppreference.com/w/cpp/numeric/bit_cast.酷

其他
对不起,我刚刚意识到我错过了你的问题。将任意指针强制转换为char,转换为基本类型(如int),并不能保证有效。但这与别名无关。这与CPU有关。它将在x86/x64上运行,因为CPU支持这一点。并非所有CPU都这样。但是,从字符/字节指针复制总是有效的。

宋子辰
2023-03-14

严格别名冲突是指通过不兼容的句柄访问数据。在您的代码中,您永远不会访问数据。转换指针只是转换指针的值,与别名冲突没有任何关系。

void*可以作为任何其他类型的别名:

是的,您可以将任何其他指针值转换为void*类型的指针。

任何指针类型都可以别名char指针吗?

指针必须指向与其所指向的类型对齐的内存位置。例如C11 6.3.2.3p7和C draft expr#static。cast-13和C draft expr#reinterpret.cast-7。当指针未对齐时,我看到结果在C中未定义,在C中没有指定。

float* pFloat = (float*) pInt; // I know this is strict aliasing violation

不,它不是,您不访问指针后面的数据。假设指针正确对齐(可能不是)和 sizeof(float) == sizeof(myChars) (可能不是):现在,如果你要做例如 *pFloat = 1.0;,那么你实际上会访问数据,然后你可能最终会与别名违规有任何关系。我认为使用新字符[]或马洛克的结果来铸造浮点数*是UB(严格的别名违规)吗?很好地回答了所有情况。

从new或malloc中获取char或char数组时,不能保证栈上的char或char数组是对齐的,对吗?

是否在强制转换指针或指针别名相同内存时违反了严格的别名规则?

不,转换指针不会访问数据。不,指向同一位置的两个指针不会访问指针后面的数据。

访问同一内存或分配不同指针类型是否有严格的别名规则?

只是关于访问。

因为如果它只是关于内存访问,那么我将浮点数*分配给int*的示例很好,因为我先移动它,对吧?

因此,有不同的规则会影响它。不能保证我的结构::myFloat从一开始就处于大小(int) - 编译器可以在结构成员之间插入填充。使用宏的偏移量

 类似资料:
  • 我正在阅读关于reinterpret_cast的笔记,它是混淆现象(http://en.cppreference.com/w/cpp/language/reinterpret_cast)。 我写了代码: 我认为这些规则在这里不适用: T2是对象的(可能是cv限定的)动态类型 T2和T1都是指向相同类型T3的指针(可能是多级的,可能在每个级别都是cv限定的)(因为C11) T2是一个聚合类型或并集类

  • 我的理解是,由于所谓的“严格混淆现象规则”,以下代码在C中具有未定义的行为。 特别是,C编译器可能会完全忽略赋值,因为它可以假定

  • 本文向大家介绍C#关联别名以解决冲突,包括了C#关联别名以解决冲突的使用技巧和注意事项,需要的朋友参考一下 示例 如果您正在使用可能具有相同名称类的多个名称空间(例如System.Random和UnityEngine.Random),则可以使用别名来指定Random来自一个或另一个名称空间的别名,而不必在调用中使用整个名称空间。 例如: 这将导致编译器不确定Random将新变量视为哪个。相反,您可

  • 问题内容: 我犯了如下错误: 但是现在我想使用内置函数。如您所见,listname和内置函数之间存在命名冲突。 如何在不重新启动Python Shell的情况下将变量作为内置函数使用? 问题答案: 使用或(取决于上下文),或再次简单地删除()。 无需进口: 存在是CPython实现细节;在模块中,它是一个模块,在其他任何地方,它都是模块字典。Jython,IronPython和PyPy可能选择完全

  • Postgres 10和11的插入说明: 关于冲突[冲突目标]冲突行动 我有一张桌子: 而我想做的 但是我得到一个错误: ON CONFLICT DO UPDATE需要推理规范或约束名称提示:例如,ON CONFLICT(column_name) 为什么我必须提供一个确定的目标?如何提供主键或其他列集?

  • 嵌入结构体内部可能拥有相同的成员名,成员重名时会发生什么?下面通过例子来讲解。 代码说明如下: 第 7 行和第 11 行分别定义了两个拥有 a int 字段的结构体。 第 15 行的结构体嵌入了 A 和 B 的结构体。 第 21 行实例化 C 结构体。 第 22 行按常规的方法,访问嵌入结构体 A 中的 a 字段,并赋值 1。 第 23 行可以正常输出实例化 C 结构体。 接着,将第 22 行修改