我能够打印整数的地址和值,但不能打印联盟的字符。为什么会这样呢
#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
}
O / P:0x7fff5451ab68 0x7fff5451ab68
答案 0 :(得分:19)
免责声明:OP的标签非常模糊,因此这个答案使用代码作为参考框架,即C ++(使用iostream
,引入std
命名空间,{{ 1}})。强>
您以不恰当的方式使用cout
。但我们稍后会再回过头来看。
union
您的代码首先将union用作e.i = 20;
整数。哪个没关系。但是你之后所做的并不是一个好主意。首先,你做了两件有些可接受的事情:
i
您查询了联合中两个cout << &e.j;
cout << &e.i;
的地址,这个地址很小,因为它们共享存储空间,因此共享第一个字节的地址。
int
现在,这是你越过界限的地方。您现在正在执行隐式指针算法和解引用方面的索引到cout << &e.c[0]; //Why can't I print this address
cout << e.c[1]; // Why can't I print this value
数组,即使您尝试获取第一个元素的地址,也可能评估一个不是最后一个元素的元素在联盟中设定。所以,这是一个很大的禁忌。
此外,char[]
基本上是&e.c[0]
,它将被char*
“拦截”并被视为C风格的字符串。它不会将其视为一个简单的地址。
cout
未定义的行为。 “但是,但是!”,我听到你们有些人说。是的,它是C ++中的UB。在C99(6.5 / 7)中有效,并且几乎没有通过脚注和一些胶带。这是一个简单的问题,LightnessRacesInSpace和Mysticial已经在这个答案和其他人的评论中解释过。
是的,您可以将任何类型的变量转换为char数组,并将其弄乱,无论出于何种目的。但是在C ++中通过工会打字是非法的,没有任何问题和借口。是的,它可能会奏效。是的,如果你不为它烦恼,你可以继续使用它。但根据C ++标准,这显然是非法的。
除非该成员是您为其指定值的联合的最后一个成员,否则您不应检索其值。就这么简单。
C ++中的联盟有一个目的,如下所述。它们还可以具有成员函数和访问说明符。他们不能拥有虚拟功能或静态成员。它们既不能用作基类,也不能从某些东西继承。而且它们不能用于打字。这在C ++中是非法的。
进一步阅读!
工会是:
工会不是:
即使是MSDN,它也是正确的:
union是用户定义的数据或类类型,在任何给定时间, 只包含其成员列表中的一个对象(尽管如此) object可以是数组或类类型。)
这是什么意思?这意味着您可以按照以下方式定义某些内容:
cout << e.c[1]; // Why can't I print this value
这个想法是所有人都坐在记忆中的同一个空间里。从给定实现中的最大数据类型推断出联合的存储。平台在这里有很多自由。自由规格无法涵盖。不是C.不是C ++。
你一定不能以union stuff {
int i;
double d;
float f;
} m;
的身份写入联盟,然后将其作为int
(或其他任何内容)的方式阅读,作为一种奇怪的牛仔reinterpret_cast的方式。< / p>
float
的使用仅用于举例和简单。
std::cout
m.i = 5;
std::cout << m.f; // NO. NO. NO. Please, no.
注意没有“交火”。这是预期用途。超薄存储器存储三个不同时间使用的三个变量,没有战斗。
错误概念是如何上升的?一些非常糟糕的人有一天醒来,我敢打赌他们中的一个是3D程序员,并考虑过这样做:
m.i = 5;
std::cout << m.i;
// Now I'm done with i, I have no intention of using it
// If I do, I'll make sure I properly set it.
m.f = 3.0f;
std::cout << m.f; // No "cowboy-interpreting", defined.
// I've got an idea, but I need it to be an int.
m.i = 3; // m.f and m.d are here-by invalidated.
int lol = 5;
m.i += lol;
毫无疑问,他有一个“高贵的想法”,可以作为浮动数组和单独的xyzw成员访问4元组。现在,你知道为什么这在工会方面是错误的,但在这里还有一个失败:
C ++没有匿名结构。它确实有匿名联盟,出于上述目的,使其更接近预期用途(删除// This is wrong on so many different levels.
union {
float arr[4];
struct {
float x,y,z,w;
};
};
“前缀”),因为你可以肯定地看到它如何有利于工会背后的一般理念。
不要这样做。请。
答案 1 :(得分:4)
严格来说,behaviour of your code is undefined。与我之前所说的相反,代码的行为并未定义(我认为它是实现定义的) 。有关说明,请参阅https://stackoverflow.com/a/1812932/367273。
&e.c[0]
的类型为char*
,因此将其打印为C字符串,而不是指针。该字符串为空白或由不可打印的字符组成,因此您看不到输出。类似的事情发生在e.c[1]
上,除了它是单个char
而不是字符串。
当我按如下方式初始化e
时:
e.i = 0x00424344;
最后两行分别打印DBC
和B
(这利用了我的机器上int
32位宽并且是小端的事实。
答案 2 :(得分:2)
未定义的行为访问union
的字段,其类型不是最后一个,至少在C ++中。
虽然在理论上取一个地址是合法的,但这不是工会的用途。