当询问C中常见的未定义行为时,灵魂比我提到的严格别名规则更加开明.
他们在说什么?
我早先使用过工会; 今天,当我读到这篇文章并开始知道这段代码时,我感到震惊
union ARGB
{
uint32_t colour;
struct componentsTag
{
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t a;
} components;
} pixel;
pixel.colour = 0xff040201; // ARGB::colour is the active member from now on
// somewhere down the line, without any edit to pixel
if(pixel.components.a) // accessing the non-active member ARGB::components
Run Code Online (Sandbox Code Playgroud)
实际上是未定义的行为即从工会成员读取而不是最近编写的那个导致未定义的行为.如果这不是工会的预期用途,那是什么?有人可以详细解释一下吗?
更新:
事后我想澄清一些事情.
如果标准布局联合包含多个共享公共初始序列的标准布局结构,并且如果此标准布局联合类型的对象包含其中一个标准布局结构,则允许检查任何标准布局结构的公共初始序列.标准布局结构成员.§9.2/ 19:如果相应的成员具有布局兼容类型且两个成员都不是位字段,或者两者都是具有相同宽度的位字段,则一个或多个初始序列的两个标准布局结构共享一个公共初始序列成员.
C89/90在未指明的行为(附件J)中称之为,而K&R的书称其实施已定义.来自K&R的报价:
这是联合的目的 - 一个可以合法地保存几种类型中的任何一种的变量.[...]只要用法一致:检索到的类型必须是最近存储的类型.程序员有责任跟踪当前存储在联合中的类型; 如果将某些内容存储为一种类型并将其提取为另一种类型,则结果将依赖于实现. …
我有一个问题,了解使用GCC的工会可以做什么和不可以做什么.我阅读了有关它的问题(特别是这里和这里),但他们关注C++标准,我觉得C++标准和实践(常用的编译器)之间存在不匹配.
特别是,我最近在阅读有关编译标志-fstrict-aliasing的GCC在线文档中发现了令人困惑的信息.它说:
-fstrict走样
允许编译器采用适用于正在编译的语言的最严格的别名规则.对于C(和C++),这将根据表达式的类型激活优化.特别地,假设一种类型的对象永远不会与不同类型的对象驻留在相同的地址,除非类型几乎相同.例如,a
unsigned intcan可以是aint,但不是avoid*或adouble.字符类型可以别名为任何其他类型.特别注意这样的代码:Run Code Online (Sandbox Code Playgroud)union a_union { int i; double d; }; int f() { union a_union t; t.d = 3.0; return t.i; }从不同的工会成员阅读的做法比最近写的那个(称为"打字式")很常见.即使使用-fstrict-aliasing,只要通过union类型访问内存,就允许类型为punning.因此,上面的代码按预期工作.
这是我认为我从这个例子和我的疑虑中理解的:
1)别名仅适用于相似类型或char
1)的后果:别名 - 正如文字暗示的那样 - 是你有一个值和两个成员来访问它(即相同的字节);
怀疑:当它们具有相同的字节大小时,两种类型是相似的吗?如果没有,什么是类似的类型?
1)对于非相似类型(无论这意味着什么)的后果,别名不起作用;
2)类型双关语是指我们读的不同于我们写的成员; 它是常见的,只要通过union类型访问内存,它就可以正常工作;
怀疑:在类型相似的特定情况下别名是什么类型?
我感到困惑,因为它表示unsigned int和double不相似,所以别名不起作用; 然后在示例中它是int和double之间的别名,它清楚地表明它按预期工作,但称之为类型 - 惩罚:不是因为类型是或不相似,而是因为它是从一个不写的成员读取.但是从一个没有写的成员那里读取的是我所理解的混淆(正如这个词所暗示的那样).我迷路了.
问题: 有人可以澄清别名和类型惩罚之间的区别,这两种技术的用途是如何在GCC中发挥作用的?编译器标志有什么作用?
uint64_t n; // two 32-bit integers
return ( (uint32_t)(n >> 32) == (uint32_t)n );
Run Code Online (Sandbox Code Playgroud)
将32个最高有效位与uint64_t的32个最低有效位进行原子比较的最快方法是什么?
我认为一个可怕的解决方案是:获取自旋锁,读取32 LSB,读取32 MSB,比较得到结果,释放自旋锁,返回结果.有没有办法做到这一点,而不必采取螺旋锁?
我的代码是这样的
// using_a_union.cpp
#include <stdio.h>
union NumericType
{
int iValue;
long lValue;
double dValue;
};
int main()
{
union NumericType Values = { 10 }; // iValue = 10
printf("%d\n", Values.iValue);
Values.dValue = 3.1416;
printf("%d\n", Values.iValue); // garbage value
}
Run Code Online (Sandbox Code Playgroud)
在我尝试打印Values.iValue后,为什么会获得垃圾值Values.dValue = 3.1416?我以为内存布局会像这样.会发生什么Values.iValue,并
Values.lValue;当我给你的东西Values.dValue?
我能够打印出int的地址和值,但不能打印出union的字符.为什么会这样
#include <iostream>
using namespace std;
union Endian
{
int i;
char c[sizeof(int)];
int j;
};
int main(int argc, char *argv[]) {
Endian e;
e.i = 20;
cout << &e.j;
cout << &e.i;
cout << &e.c[0]; //Why can't I print this address
cout << e.c[1]; // Why can't I print this value
}
Run Code Online (Sandbox Code Playgroud)
O/P:0x7fff5451ab68 0x7fff5451ab68