在union中使用char数组

tez*_*tez 6 c++

我能够打印出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

小智 19

免责声明:OP的标签非常模糊,因此这个答案使用代码作为参考框架,即C++(使用iostream,拉入std命名空间cout).

union以不恰当的方式使用.但我们稍后会再回过头来看看.

e.i = 20;
Run Code Online (Sandbox Code Playgroud)

您的代码首先使用union作为i整数.哪个没关系.但是你之后所做的并不是一个好主意.首先,你做了两件有些可接受的事:

cout << &e.j;
cout << &e.i;
Run Code Online (Sandbox Code Playgroud)

您查询了int联合中两个s 的地址,这是很小的,因为它们都共享存储,因此共享第一个字节的地址.

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)

现在,这是你越过界限的地方.您现在正在执行隐式指针算法和解引用方面的索引到char[]数组中,即使您尝试获取第一个元素的地址,也可能会评估一个元素,该元素不是联合中设置的最后一个元素.所以,这是一个很大的禁忌.

此外,&e.c[0]基本上char*它将被"拦截"并被cout视为C风格的字符串.它不会将其视为简单的地址.

cout << e.c[1]; // Why can't I print this value
Run Code Online (Sandbox Code Playgroud)

未定义的行为."但是,但是!" ,我听到你们有些人说.是的,它是C++中的UB.在C99(6.5/7)中有效,并且几乎没有通过脚注和一些胶带.这是一个简单的问题,LightnessRacesInSpace和Mysticial已经在这个答案和其他人的评论中解释过了.

是的,您可以将任何类型的变量转换为char数组并将其弄乱,无论出于何种目的.但是在C++中通过工会打字是非法的,没有任何问题和借口.是的,它可能会奏效.是的,如果你不为它烦恼,你可以继续使用它.但根据C++标准,这显然是非法的.

除非该成员是您为其分配值的联合的最后一个成员,否则您不应检索其值.就这么简单.

C++中的联合有一个目的,如下所述.它们还可以具有成员函数和访问说明符.他们不能拥有虚拟功能或静态成员.它们既不能用作基类,也不能从某些东西继承.而且它们不能用于打字.这在C++中是非法的.

进一步阅读!

了解工会

工会是:

  • 一种允许内存重用的方法.
  • 而已.

工会不是:

  • 在联盟的元素之间铸造牛仔的方法
  • 一种欺骗严格别名的方法.

即使是MSDN也是如此:

union是用户定义的数据或类类型,在任何给定时间,它只包含其成员列表中的一个对象(尽管该对象可以是数组或类类型).

这是什么意思?这意味着您可以按照以下方式定义某些内容:

union stuff {

    int i;
    double d;
    float f;    

} m;
Run Code Online (Sandbox Code Playgroud)

这个想法是他们所有人都坐在记忆中的同一个空间.从给定实现中的最大数据类型推断出联合的存储.平台在这里有很多自由.自由规格无法涵盖.不是C.不是C++.

不能写作联盟作为一个int,然后把它作为一个float(或其他任何东西)读作一种奇怪的牛仔reinterpret_cast的方式.

使用std::cout是出于示例目的和简单性.

这是非法的:

m.i = 5;
std::cout << m.f; // NO. NO. NO. Please, no.
Run Code Online (Sandbox Code Playgroud)

这是合法的:

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;
Run Code Online (Sandbox Code Playgroud)

注意没有"交火".这是预期用途.超薄内存存储三个不同时间使用的三个变量,没有战斗.

错误概念是如何上升的?有些非常糟糕的人有一天醒来,我打赌他们中的一个是3D程序员,并考虑过这样做:

// This is wrong on so many different levels.
union {

    float arr[4];
    struct {
        float x,y,z,w;
    };

};
Run Code Online (Sandbox Code Playgroud)

毫无疑问,他有一个"高贵的想法",可以作为浮动阵列和单独的xyzw成员获得4元组.现在,你知道为什么这在工会方面是错误的,但这里还有一个失败:

C++没有匿名结构.它确实有匿名联盟,出于上述目的,使其更接近预期用途(删除m."前缀"),因为你可以肯定地看到它如何有利于工会背后的一般理念.

不要这样做.请.

  • 只是为了澄清.[自C99 TR2以来,联盟类型惩罚已合法(但实施定义的行为).](http://stackoverflow.com/questions/11639947/is-type-punning-through-a-union-unspecified-in-c99 -and-it-it-specified)但是从C++ 11开始,它仍然是C++中的UB. (5认同)
  • 虽然在C89和C++中通过联合进行的类型惩罚在技术上是UB,但它是一种非常常见的习惯用法,并得到所有主要编译器的良好支持. (4认同)
  • 这是我现在的事情:https://dl.dropboxusercontent.com/u/17632594/cowboy_cast.png (4认同)
  • @jthill:如果你施放指针,这只是合法的.据我所知,通过工会进入没有这种豁免. (2认同)