问题标签 [strict-aliasing]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c - 转换 ioctl 参数是否会破坏严格的别名规则?
我正在运行具有以下原型的Linux 3.2内核:ioctl
我注意到无论从相应用户空间函数传递给的实际数据类型如何,arg
它总是无符号长。ioctl
示例ioctl
通常显示以下实现(source):
请注意,需要query_arg_t *类型,因此应用了强制转换:(query_arg_t *)arg
但是,这是否违反了严格的别名规则,因为arg
类型为unsigned long而强制转换为query_arg_t *?
c++ - 这个短程序是合法的 C++ 吗?
在解决http://cppquiz.org上的测试时,我发现了这段有趣的代码:
我的问题是这个程序是合法的 C++ 还是不是?我担心严格的混叠。
c++ - char* 和 std::uint8_t* 之间的 reinterpret_cast - 安全吗?
现在我们有时都必须使用二进制数据。在 C++ 中,我们使用字节序列,从一开始char
就是我们的构建块。定义为sizeof
1,它是字节。char
并且默认使用所有库 I/O 函数。一切都很好,但总是有一点点担心,一些奇怪的东西会困扰一些人——一个字节中的位数是实现定义的。
所以在 C99 中,决定引入几个 typedef 让开发人员可以轻松地表达自己,固定宽度的整数类型。当然是可选的,因为我们不想损害可移植性。其中uint8_t
, 迁移到 C++11 作为std::uint8_t
, 一个固定宽度的 8 位无符号整数类型,对于真正想要使用 8 位字节的人来说是完美的选择。
因此,开发人员接受了新工具并开始构建库,这些库明确地声明他们接受 8 位字节序列,asstd::uint8_t*
或std::vector<std::uint8_t>
其他。
但是,也许经过深思熟虑,标准化委员会决定不要求实现,std::char_traits<std::uint8_t>
因此禁止开发人员轻松便携地将 s 实例化,例如,std::basic_fstream<std::uint8_t>
轻松地std::uint8_t
将 s 读取为二进制数据。或者也许,我们中的一些人不关心字节中的位数并且对此感到满意。
但不幸的是,两个世界发生冲突,有时您必须将数据作为char*
并将其传递给期望的库std::uint8_t*
。但是等等,你说,不是char
可变位并且std::uint8_t
固定为8吗?会不会导致数据丢失?
好吧,这有一个有趣的标准语。定义为恰好保存一个字节并且字节是内存的char
最低可寻址块,因此不能有位宽小于char
. 接下来,它被定义为能够保存 UTF-8 代码单元。这给了我们最小值 - 8 位。所以现在我们有一个需要 8 位宽的 typedef 和一个至少 8 位宽的类型。但是有替代品吗?是的,unsigned char
。请记住,签名char
是实现定义的。还有其他类型吗?谢天谢地,没有。所有其他整数类型的所需范围都在 8 位之外。
最后,std::uint8_t
是可选的,这意味着如果未定义使用此类型的库将不会编译。但是如果它编译呢?我可以非常自信地说,这意味着我们处于一个具有 8 位字节和CHAR_BIT == 8
.
一旦我们知道我们有 8 位字节,即std::uint8_t
实现为char
or unsigned char
,我们是否可以假设我们可以执行reinterpret_cast
from char*
to std::uint8_t*
,反之亦然?它是便携式的吗?
这就是我的标准阅读技巧让我失望的地方。我阅读了有关安全派生指针 ( [basic.stc.dynamic.safety]
) 的内容,据我所知,以下内容:
如果我们不碰它是安全的buffer2
。如我错了请纠正我。
因此,鉴于以下先决条件:
CHAR_BIT == 8
std::uint8_t
被定义为。
假设我们正在处理二进制数据并且可能缺少符号无关紧要,那么它是否可移植且安全char*
?std::uint8_t*
char
我将不胜感激对标准的参考和解释。
编辑:谢谢,杰里·科芬。我将添加来自标准的引用([basic.lval],§3.10/10):
如果程序尝试通过非下列类型之一的泛左值访问对象的存储值,则行为未定义:
...
— char 或 unsigned char 类型。
EDIT2:好的,更深入。std::uint8_t
不保证是unsigned char
. 它可以实现为扩展无符号整数类型,扩展无符号整数类型不包含在 §3.10/10 中。现在怎么办?
c++ - G ++:可以将 __attribute__((__may_alias__)) 用于指向类实例而不是类定义本身的指针吗?
我正在寻找以下问题的答案:是否may_alias
适合作为指向某个类对象的指针的属性Foo
?还是只能在班级级别使用?
考虑以下代码(它基于更复杂的真实示例):
有没有办法告诉 gcc 那个单指针may_alias
另一个?
顺便说一句,请注意,此类问题的 gcc 检测机制是不完善的,因此很容易让这个警告消失而没有真正解决问题。
考虑以下代码片段:
取消注释其中一行以查看编译器输出的差异。
c - 写指向管道的指针,是否有任何严格的别名或双关语类型问题?
我需要在一个程序中创建很多FIFO队列,用于同一进程中线程之间的通信。
我想我可以使用 pipe() 来达到这个目的,因为通过这种方式,我可以在从队列中获取节点的线程上使用 select 或 poll。
现在的问题是如何将指针放入队列,因为每个节点都是一个结构,我想将指针放入队列中,例如
typedef{ 结构数据包 *pkt; 结构信息*信息;整数序列;}节点;
那么 mynode 就是我想要的。
我的程序有什么问题吗?特别是严格的别名或双关语问题?谢谢!
c - 在不违反标准的情况下使用指向结构的指针对数组进行别名
阅读本文我了解到,如果它们具有兼容的成员,您可以对结构进行别名(即不违反标准),即给定以下结构:
以下将破坏别名规则:
但以下不会:
因为所讨论的“聚合类型”包含与我们正在转换为它的指针兼容的类型,即指向类型的指针uint32_t
可以转换为包含类型成员(或成员)的结构,uint32_t
而不会破坏别名规则。
首先,我是否理解正确?
其次,结构中(其他)变量的顺序和类型是否重要?说,如果Frizzly
定义如下:
在第二个示例中的强制转换之后,b
现在由不兼容 ( uint32_t
) 类型的内存支持。转换是否仍然有效(或者更确切地说,通过转换的指针访问值)?对任一元素的a
更改是否会改变(反之亦然)的第一个元素的值,i
就好像严格别名被禁用一样?
另外,如果上述内容有效,那么如果我有这样的结构怎么办:
如果我是正确的,以下演员将打破别名规则:
我可以通过将单个unsigned char
成员添加到结构中来使强制转换有效吗?换句话说,转换不兼容类型的指针通常会破坏别名规则,但是由于从指向包含类型成员的结构的X
指针转换为指向指针X
是一个例外,因此任何从指向 X 的指针到聚合-Y 的转换都可以只需将 X 类型的(可能是虚拟的)成员添加到 Y 中即可使其有效?
(我实际上并没有在编译器中测试上述代码片段。)
编辑:
我知道我的措辞和示例可能相当糟糕,所以我会尝试重新表述这个问题:如果我理解正确,那么指向结构的指针对类型为“X”的元素数组进行别名是合法的,只要该结构包含“X”类型的成员。现在,当取消引用结构的成员时,该成员是否必须是“X”类型,或者是对结构的所有成员制定的严格别名规则的例外,无论它们的类型如何,只要有一个成员合适的类型?
c++ - 为什么 GCC 和 Clang 不做这种别名优化?
我有一个朋友将“Base”类型的非基类对象强制转换为“Derived”类类型对象,其中“Derived”是“Base”的派生类,只添加函数,但没有数据。在下面的代码中,我确实x
向派生类添加了一个数据成员
通过严格的别名分析,GCC(也是 Clang)总是返回10
,而不是11
,因为在定义明确的代码b
中永远不能指向a
。但是,如果我删除B::x
(实际上是我朋友的代码中的情况),GCC 的输出汇编代码不会优化返回访问a.a
并重新加载内存中的值。g
因此,即使我认为它仍然具有未定义的行为,我朋友在 GCC 上调用“有效”的代码(如他所愿)
因此,在本质上相同的两种情况下,GCC 优化了一种情况而不优化另一种情况。是因为b
可以合法指向a
吗?还是因为 GCC 只是不想破坏现实世界的代码?
我测试了陈述的答案
如果您删除 B::x,则 B 满足 9p7 中标准布局类的要求,并且访问变得完美定义,因为这两种类型是布局兼容的,9.2p17。
有两个布局兼容的枚举
的汇编器输出g
返回1
, not 0
, 即使A
和B
布局兼容 (7.2p8)。
所以我的进一步问题是(引用一个答案):“具有完全相同布局的两个类可能被认为是“几乎相同”,并且它们被排除在优化之外。. 有人可以为 GCC 或 Clang 提供证明吗?
c - 结构可以为其自己的初始成员和唯一成员取别名吗?
例如,这段代码是否有效,或者它是否通过违反别名规则来调用未定义的行为?
我的兴趣是使用基于此的技术来开发一种可移植的方法来执行别名读取。
更新:这是预期的用例,有点不同,但当且仅当上述有效时,它才应该有效:
GCC 根据需要将其编译为单个 32 位加载,并且它似乎避免了如果p
实际指向的类型不是char
. 换句话说,它似乎充当了 GNU C__attribute__((__may_alias__))
属性的可移植替代品。但我不确定它是否真的很好定义......
c - void* 类型转换会破坏严格的混叠?
我写了一个这样的动态数组:
可以通过[]
操作访问动态数组。调整大小时,我可以__dynarray_header*)array - 1
用来检索容量和长度信息。
这个想法在小测试中有效。然而,GCC 警告要打破严格的混叠。
我还发现了一些没有-fno-strict-aliasing
编译器选项(带-O3
优化)的大型项目段错误。
我知道什么是严格混叠,以及为什么我的代码会破坏严格混叠。
我的问题是:有没有比我上面展示的更好的方法来实现支持[]
操作和动态调整大小的动态数组?
额外的:
使用此动态数组的演示程序:
c++ - 严格的别名似乎不一致
有几个来自严格别名的错误,所以我想我会尝试修复所有这些错误。仔细研究了它是什么,有时 GCC 似乎不会发出警告,而且有些事情是不可能实现的。至少根据我的理解,下面的每一个都被打破了。那么我的理解是错误的,是否有正确的方法来做所有这些事情,或者某些代码只需要在技术上打破规则并被系统测试很好地覆盖?
这些错误来自一些混合了 char 和 unsigned char 缓冲区的代码,例如:
将其更改为以下似乎可以解决问题,尽管它仍然涉及强制转换,所以我不确定为什么它现在有效并且没有警告:
还有很多其他地方似乎在没有警告的情况下工作
还有一些与...
非字符案例。这没有警告,即使它很糟糕,我该如何避免它(两种方法似乎都有效)?
查看其他 API 似乎也有各种情况,据我了解,这些情况违反了规则(没有遇到 Linux/GCC 特定的情况,但肯定会在某处出现)。
CoCreateInstance 有一个需要显式指针转换的 void** 输出参数。Direct3D 也有类似的。
LARGE_INTEGER 是一个联合,它可能对不同的成员进行读/写(例如,一些代码可能使用高/低,然后其他一些可能会读取 int64)。
我记得 CPython 实现非常高兴地将 PyObject* 转换为一堆其他的东西,这些东西在一开始就具有相同的内存布局。
我见过的很多哈希实现会将输入缓冲区转换为 uint32_t*,然后可能使用 uint8_t 来处理最后的 1-3 个字节。
我见过的几乎每个内存分配器实现都使用 char* 或 unsigned char*,然后必须将其强制转换为所需的类型(可能通过返回的 void*,但在分配内部至少它是一个 char)