Tag: strict aliasing

简单的结构inheritance和伪多态与vs严格别名

如果有人回答我的问题,请不要告诉我使用C ++ 。 所以,我在C中创建了一个使用面向对象方法的小型库。 我选择在C中使用两种主要的inheritance方法中较不常见的方法:将基类型的成员复制到派生类型的开头。 像这样的东西: struct base { int a; int b; char c; }; struct derived { int a; int b; char c; unsigned int d; void (*virtual_method)(int, char); }; 这种方法不如另一种方法(基类型的实例作为派生类型的第一个成员)受欢迎,因为 从技术上讲,没有标准保证基础和派生结构的第一个共同成员将具有相同的抵消。 但是,除了其中一个结构被打包而另一个结构没有被打包的情况之外,它们将在大多数(如果不是全部)已知编译器上具有相同的偏移量。 这种方法最严重的缺陷:它违反了严格的别名 。 将指向派生结构的指针转换为其基类型然后解除引用指针在技术上是未定义的行为。 但是,与其他方法相比,它也有其优点: 更少详细:访问已inheritance的派生结构的成员与访问尚未inheritance的结构相同,而不是转换为基类型, 然后访问所需的成员; 这实际上是真正的inheritance而不是构成 ; 虽然可能需要一些预处理器滥用,但它与其他方法一样容易实现; 我们可以得到一个实际多重inheritance的半生不熟的forms,我们可以从几个基类型inheritance,但只能转换为其中一个。 我一直在寻找使我的库编译和使用强制执行严格别名(如gcc )的编译器正确工作的可能性,而无需用户手动关闭它。 以下是我研究过的可能性: 工会。 遗憾的是,由于以下几个原因,这些是禁忌: 详细程度回归! 要遵循通过联合访问2个结构的第一个共同成员的标准规则,必须(从C99开始)明确使用联合来访问第一个共同成员。 我们需要特殊的语法来访问联合中每种类型的成员! 空间。 考虑一个inheritance层次结构。 […]

C ++(GCC)中的C99严格别名规则

据我了解,GCC支持C ++中的所有C99function。 但是如何在C ++代码中处理C99严格别名? 我知道在不相关的类型之间使用C转换进行转换不是严格别名安全的,并且可能生成错误的代码,但是C ++呢? 由于严格别名不是C ++标准的一部分(这是正确的吗?),GCC必须指定语义本身。 我认为const_cast和static_cast在相关类型之间const_cast ,因此它们是安全的,而reinterpret_cast可以破坏严格的别名规则。 这是正确的理解吗?

再一次:严格的别名规则和char *

我读得越多,我就越困惑。 相关问题的最后一个问题与我的问题最接近,但是我对所有关于对象生命周期的问题感到困惑,尤其是 – 只读或不读。 直截了当。 如我错了请纠正我。 这很好,gcc没有发出警告,我正试图“通过char* ”读取类型T ( uint32_t ): uint32_t num = 0x01020304; char* buff = reinterpret_cast( &num ); 但这是“坏”(也是一个警告),我正在尝试“反过来”: char buff[ 4 ] = { 0x1, 0x2, 0x3, 0x4 }; uint32_t num = *reinterpret_cast( buff ); 第二个如何与第一个不同,特别是当我们谈论重新排序指令(用于优化)时? 另外,添加const不会以任何方式改变这种情况。 或者这只是一个直接规则,它明确指出:“这可以在一个方向完成,但在另一个方向不能完成”? 我在标准中找不到任何相关内容(特别是在C ++ 11标准中搜索过)。 C和C ++是否相同(因为我读了一条评论,暗示它与2种语言不同)? 我使用union来“解决”这个问题,这仍然看起来不是 100%正常,因为标准并不保证(这表明我只能依赖于最后在union修改的值)。 所以,经过大量阅读,我现在更加困惑。 我想只有memcpy才是“好”的解决方案? 相关问题: 什么是严格别名规则? “解除引用类型 – […]

通过联合键入C和C ++中的结构

我用gcc和g ++编译了这个迂腐,我不会在任何一个中得到警告: #include #include #include struct a { struct a *next; int i; }; struct b { struct b *next; int i; }; struct c { int x, x2, x3; union { struct aa; struct bb; } u; }; void foo(struct b *bar) { bar->next->i = 9; return; } int main(int argc, char *argv[]) { […]

严格别名和叠加inheritance

考虑以下代码示例: #include typedef struct AA; struct A { int x; int y; }; typedef struct BB; struct B { int x; int y; int z; }; int main() { B b = {1,2,3}; A *ap = (A*)&b; *ap = (A){100,200}; //a clear http://port70.net/~nsz/c/c11/n1570.html#6.5p7 violation ap->x = 10; ap->y = 20; //lvalues of types int and […]

是无符号字符 ; 一个 ; 未定义的行为?

来自C标准的未定义行为的示例之一(J.2): – 数组下标超出范围,即使一个对象显然可以使用给定的下标访问(如左边的表达式a [1] [7],给出声明int a [4] [5])(6.5.6) 如果声明从int a[4][5]更改为unsigned char a[4][5] ,访问a[1][7]仍会导致未定义的行为? 我的意见是,它没有,但我从其他人那里听到了不同意见,我想看看其他一些想成为SO专家的想法。 我的推理: 根据6.2.6.1第4段和第6.5段第7段的通常解释,对象a的表示是sizeof (unsigned char [4][5])*CHAR_BIT位,可以作为unsigned char [20]类型的数组访问unsigned char [20]与物体重叠。 a[1]将unsigned char [5]作为左值,但在表达式中使用(作为[]运算符的操作数,或等效地作为*(a[1]+7) +运算符的操作数) ,它衰减到unsigned char *类型的指针。 a[1]值也是指向unsigned char [20]forms的a的“表示”的字节的指针。 以这种方式解释,在a[1]添加7是有效的。

使用void *键入punning而不破坏C99中的严格别名规则

我最近遇到了严格的别名规则,但是我无法理解如何使用void *来执行类型惩罚而不违反规则。 我知道这违反了规则: int x = 0xDEADBEEF; short *y = (short *)&x; *y = 42; int z = x; 而且我知道我可以安全地使用C99中的联合进行类型惩罚: union{ int x; short y; } data; data.x = 0xDEADBEEF; data.y = 42; int z = data.x; 但是如何在C99中使用void *来安全地执行类型惩罚呢? 以下是正确的: int x = 0xDEADBEEF; void * helper = (void *)&x; short *y = (short *)helper; […]

通过结构别名arrays

我正在阅读ISO / IEC 9899:TC2中第6.5段的第7段。 它通过以下方式宽恕对对象的左值访问: 一种聚合或联合类型,包括其成员中的上述类型之一(包括递归地,子聚合或包含联合的成员), 请参阅文档,了解“前面提到的”类型,但它们肯定包含对象的有效类型。 它的部分标注为: 此列表的目的是指定对象可能或可能不具有别名的情况。 我读到这个说(例如)以下是明确定义的: #include #include typedef struct { unsigned int x; } s; int main(void){ unsigned int array[3] = {73,74,75}; s* sp=(s*)&array; sp->x=80; printf(“%d\n”,array[0]); return EXIT_SUCCESS; } 该程序应输出80。 我不是在提倡这是一个好的(或非常有用的)想法,并承认我在某种程度上解释它是因为我无法想到其他意味着什么也不能相信它是一个毫无意义的句子! 也就是说,我看不出有理由禁止它。 我们所知道的是该位置的对齐和内存内容与sp->x兼容,为什么不呢? 它似乎甚至可以说我是否加了(比方说) double y; 在结构的末尾,我仍然可以通过sp->x访问array[0] 。 然而,即使数组大于sizeof(s)任何访问sp->y尝试都是’所有下注’未定义的行为。 可能我礼貌地要求人们说出那句话宽恕而不是进入一个扁平的旋转喊“严格混淆UB严格别名UB”似乎经常是这些事情的方式。

uint32_t和uint8_t的联合未定义的行为?

在这个答案的评论中,据说使用如下的联合将整数分割成它们的字节将是未定义的行为。 在那个地方给出的代码是相似的,虽然与此不相同,如果我更改了代码的未定义行为相关方面,请注意。 union addr { uint8_t addr8[4]; uint32_t addr32; }; 到目前为止,我认为这将是一个很好的方法来做像addr = {127, 0, 0, 1}; 并得到相应的uint32_t作为回报。 (我承认根据我的系统的字节顺序,这可能产生不同的结果。但问题仍然存在。) 这是未定义的行为吗? 如果是这样,为什么? (我不知道C ++中的UB是什么意思是访问非活动的联盟成员。 ) C99 在这一点上,C99显然非常接近C ++ 03。 C ++ 03 在联合中,最多一个数据成员可以在任何时间处于活动状态,也就是说,最多一个数据成员的值可以随时存储在并集中。 C ++ 03,第9.5(1)节,第162页 然而 如果POD-union包含几个共享一个共同初始序列的POD结构,则允许检查任何POD结构成员的公共初始序列同上。 如果两个POD-struct类型具有相同数量的非静态数据成员,则它们是布局兼容的,并且相应的非静态数据成员(按顺序)具有布局兼容类型 C ++ 03,第9.2(14)节,第157页 如果两种类型T1和T2是相同类型,则T1和T2是布局兼容类型。 C ++ 03,第3.9(11)节,第53页 结论 因为uint8_t[4]和uint32_t不是同一类型(我猜,一个严格的别名 )(加上两个都不是POD结构/联合)上面的确是UB? C ++ 11 请注意,聚合类型不包含联合类型,因为具有联合类型的对象一次只能包含一个成员。 C ++ 11,脚注46,第42页

C中的严格别名

关于类型惩罚的问题:为什么这段代码会破坏严格的别名规则: int main() { int a = 1; short j; printf(“%i\n”, j = *((short*)&a)); return 0; } 这不是: int main() { int a = 1; short j; int *p; p=&a; printf(“%i\n”, j = *((short*)p)); return 0; } 由gcc -fstrict-aliasing构建。 谢谢!