Tag: 严格别名

通过指针访问是否会更改严格的别名语义?

有了这些定义: struct My_Header { uintptr_t bits; } struct Foo_Type { struct My_Header header; int x; } struct Foo_Type *foo = …; struct Bar_Type { struct My_Header header; float x; } struct Bar_Type *bar = …; 说这个C代码( “案例一” )是否正确: foo->header.bits = 1020; …实际上与此代码在语义上不同( “case two” ): struct My_Header *alias = &foo->header; alias->bits = 1020; 我的理解是它们应该是不同的: 情况一考虑分配不能影响Bar_Type中的标题。 […]

将数据作为副作用提供有效类型吗?

假设我有一大块动态分配的数据: void* allocate (size_t n) { void* foo = malloc(n); … return foo; } 我希望将foo指向的数据用作特殊类型type_t 。 但是我想稍后这样做,而不是在分配期间。 为了给分配的数据一个有效的类型 ,我可以这样做: void* allocate (size_t n) { void* foo = malloc(n); (void) *(type_t*)foo; … return foo } 根据C11 6.5 / 6,这个左值访问应该是有效类型type_t : 对于没有声明类型的对象的所有其他访问,对象的有效类型只是用于访问的左值的类型。 但是,行(void) *(type_t*)foo; 不包含任何副作用,因此编译器应该可以自由地优化它,我不希望它生成任何实际的机器代码。 我的问题是:上面的安全措施是什么? 是否将数据作为副作用提供有效类型? 或者通过优化代码,编译器是否也会优化掉有效类型的选择? 也就是说,使用上面的左值访问技巧,如果我现在调用上面这样的函数: int* i = allocate(sizeof(int)); *i = something; 这是否会导致严格的别名违规UB,或者是现在的有效类型int […]

结构可以为其自己的初始和唯一成员设置别名吗?

例如,此代码是否有效,还是通过违反别名规则来调用未定义的行为? int x; struct s { int i; } y; x = 1; y = *(struct s *)&x; printf(“%d\n”, yi); 我的兴趣在于使用基于此的技术来开发用于执行别名读取的可移植方法。 更新:这是预期的用例,略有不同,但当且仅当上述内容有效时才有效: static inline uint32_t read32(const unsigned char *p) { struct a { char r[4]; }; union b { struct ar; uint32_t x; } tmp; tmp.r = *(struct a *)p; return tmp.x; } GCC根据需要将其编译为单个32位负载,并且它似乎避免了如果p实际指向除char之外的类型可能发生的混叠问题。 换句话说,它似乎充当了GNU […]

malloc-free-malloc和strict-aliasing

我最近一直试图理解严格别名的一个特定方面,我想我已经制作了尽可能小的有趣代码。 (对我来说很有趣,就是!) 更新:根据目前为止的答案,很明显我需要澄清这个问题。 从某个角度来看,这里的第一个列表是“明显”定义的行为。 真正的问题是遵循这个逻辑到自定义分配器和自定义内存池。 如果我在开始时malloc一大块内存,然后编写我自己的my_malloc和my_free使用那个单个大块,那么UB是否因为它没有使用官方free ? 我会坚持使用C,有点随意。 我得到的印象是更容易谈论,C标准更清晰一点。 int main() { uint32_t *p32 = malloc(4); *p32 = 0; free(p32); uint16_t *p16 = malloc(4); p16[0] = 7; p16[1] = 7; free(p16); } 第二个malloc可能会返回与第一个malloc相同的地址(因为它之间是free )。 这意味着它正在访问具有两种不同类型的相同内存,这违反了严格的别名。 那么上面肯定是未定义的行为(UB)? (为简单起见,我们假设malloc总是成功的。我可以添加对malloc的返回值的检查,但这会使问题混乱) 如果它不是UB,为什么? 标准中是否有明确的exception,它表示允许malloc和free (以及calloc / realloc / …)“删除”与特定地址关联的类型,允许进一步访问“压印”新类型地址? 如果malloc / free是特殊的,那么这是否意味着我不能合法地编写我自己的分配器来克隆malloc的行为? 我确信有很多项目都有自定义分配器 – 它们都是UB吗? 自定义分配器 因此,如果我们决定必须定义这样的自定义分配器行为,则意味着严格别名规则本质上是“不正确的”。 我会更新它,说只要你不再使用旧类型的指针,就可以通过不同(’new’)类型的指针写入 (不读取 )。 […]

为什么POSIX与ISO C标准相矛盾

见http://pubs.opengroup.org/onlinepubs/009696699/basedefs/sys/socket.h.html ( http://pubs.opengroup.org/onlinepubs/9699919799来自第7期 – 从2013年开始,仍然是相同的!) sockaddr_storage应该转换为其他结构类型,但据我所知,这与ANSI和ISO C标准别名规则相矛盾。 (可以通过指向不兼容类型的指针访问对象,但可以通过3个char类型访问任何内容,并且结构及其第一个成员可以互换。) 我知道在C标准化之前很久就存在使用套接字的做法,但POSIX应该符合ISO C,实际上它与其手册中的标准相矛盾。 (即使在较新版本的POSIX中。) 他们为什么一开始就这样做? 他们为什么不改变它?

通过示例了解限制限定符

restrict关键字的行为在C99中由6.7.3.1定义: 设D是普通标识符的声明,它提供了一种将对象P指定为类型T的限制限定指针的方法。 如果D出现在块内并且没有存储类extern,则让B表示该块。 如果D出现在函数定义的参数声明列表中,则让B表示关联的块。 否则,让B表示main的块(或者在独立环境中的程序启动时调用任何函数的块)。 在下文中,指针表达式E被称为基于对象P if(在评估E之前执行B中的某个序列点)修改P以指向其先前指向的数组对象的副本将改变E.119的值)注意”based”仅针对具有指针类型的表达式定义。 在每次执行B期间,让L为任何具有&L基于P的左值。如果L用于访问它指定的对象X的值,并且X也被修改(通过任何方式),则以下要求适用:T不应该是const限定的。 用于访问X值的每个其他左值也应具有基于P的地址。出于本子条款的目的,每次修改X的访问也应被视为修改P. 如果P被赋予指针表达式E的值,该指针表达式E基于与块B2相关联的另一个受限指针对象P2,则B2的执行应在执行B之前开始,或者B2的执行应在该执行之前结束。分配。 如果不满足这些要求,则行为未定义。 就像其他人一样,我很难理解这个定义的所有复杂性。 作为这个问题的答案,我希望看到第4段中每个要求违反要求的一些好例子。 本文: http://web.archive.org/web/20120225055041/http://developers.sun.com/solaris/articles/cc_restrict.html 在“编译器可能假设……”方面做得很好。 扩展该模式并将编译器可以做出的假设以及它们如何无法保持,每个示例都很棒。

修复取消引用类型惩罚指针将破坏严格别名

我正在尝试使用GCC编译特定程序时修复两个警告。 警告是: 警告:解除引用类型惩罚指针将破坏严格别名规则[-Wstrict-aliasing] 而这两个罪魁祸首是: unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf)); 和 *((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset); incoming_buf和outgoing_buf定义如下: char incoming_buf[LIBIRC_DCC_BUFFER_SIZE]; char outgoing_buf[LIBIRC_DCC_BUFFER_SIZE]; 这似乎与我一直在研究的那个警告的其他例子略有不同。 我宁愿修复问题而不是禁用严格别名检查。 有很多建议要使用联合 – 这个案例可能是一个合适的联合体?

是否错误地指定了严格别名规则?

如前所述,forms的联合 union some_union { type_a member_a; type_b member_b; … }; n个成员在重叠存储中包含n + 1个对象:一个对象用于union本身,一个对象用于每个union成员。 很明显,您可以按任何顺序自由地读取和写入任何工会成员,即使读取的工会成员不是最后写入的工会成员。 严格别名规则永远不会被违反,因为您访问存储的左值具有正确的有效类型。 脚注95 进一步支持了这一点,脚注95解释了类型双关语是否是联盟的预期用途。 严格别名规则启用的优化的典型示例是此函数: int strict_aliasing_example(int *i, float *f) { *i = 1; *f = 1.0; return (*i); } 编译器可以优化到类似的东西 int strict_aliasing_example(int *i, float *f) { *i = 1; *f = 1.0; return (1); } 因为它可以安全地假设写入*f不会影响*i的值。 但是,当我们将两个指针传递给同一个联盟的成员时会发生什么? 考虑这个例子,假设一个典型的平台,其中float是IEEE 754单精度浮点数, int是32位二进制补码整数: int […]

UB是否会抛弃const和读取值?

澄清:我的问题是: UB是否使用int类型的左值来访问有效类型为const int的对象? 这个问题有两个代码示例,它们使用类型为int的左值来访问有效类型为const int的对象,我的目的是尽可能少地分散对象。 如果除了这个特定问题之外还有任何其他UB来源,请发表评论,我将尝试更新代码示例。 以下是讨论的具体代码示例: #include #include int main() { const int c = 5; printf(“%d\n”, *(int *)&c); } 我认为它可能是UB的原因是严格的别名规则似乎说它是UB: C11 6.5 / 7 对象的存储值只能由具有以下类型之一的左值表达式访问: 与对象的有效类型兼容的类型, 与对象的有效类型兼容的类型的限定版本, … 这里对象的有效类型 (6.5 / 6)是const int 。 第一个要点: int和const int不兼容类型 (6.2.7 / 1,6.7.3 / 10)。 第二个要点: int似乎不是const int的限定版本,因为我们没有通过添加限定符来生成它。 但是6.2.5 / 26不清楚: 每个非限定类型都有几个类型的限定版本,对应于const,volatile和restrict限定符中的一个,两个或全部三个的组合。 类型的限定或非限定版本是属于相同类型类别且具有相同表示和对齐要求的不同类型。 派生类型不是由派生类型的限定符(如果有)限定的。 它没有定义“ const […]

我是否正确理解C / C ++严格别名?

我读过这篇关于C / C ++严格别名的文章 。 我认为这同样适用于C ++。 据我所知,严格别名用于重新排列代码以进行性能优化。 这就是为什么两个不同(在C ++情况下不相关)类型的指针不能引用相同的内存位置的原因。 这是否意味着只有在修改内存时才会出现问题? 除了内存对齐可能存在的问题。 例如,处理网络协议或反序列化。 我有一个字节数组,动态分配和数据包结构正确对齐。 我可以reinterpret_cast它reinterpret_cast为reinterpret_cast到我的数据包结构吗? char const* buf = …; // dynamically allocated unsigned int i = *reinterpret_cast(buf + shift); // [shift] satisfies alignment requirements