在union中使用char数组

时间:2013-04-11 15:01:00

标签: c++

我能够打印整数的地址和值,但不能打印联盟的字符。为什么会这样呢

#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

3 个答案:

答案 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;

最后两行分别打印DBCB(这利用了我的机器上int 32位宽并且是小端的事实。

答案 2 :(得分:2)

未定义的行为访问union的字段,其类型不是最后一个,至少在C ++中。

虽然在理论上取一个地址是合法的,但这不是工会的用途。