c++ - 在 union 中使用字符数组

标签 c++

我可以打印整数的地址和值,但不能打印 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

}

O/P:0x7fff5451ab68 0x7fff5451ab68

最佳答案

免责声明:OP 的标签非常含糊,所以这个答案使用代码作为引用框架,即 C++(使用 iostream,拉入 std 命名空间,cout)。

您正在使用 union以不适当的方式。但我们稍后再谈。

e.i = 20;

您的代码首先使用 union 作为 i ,一个整数。哪个没问题。但是你后来的做法真的不是什么好主意。首先你做了两件可以接受的事情:
cout << &e.j;
cout << &e.i;

你查询了两个int的地址s 在 union 中,这稍微好一点,因为它们都共享存储,因此第一个字节的地址是共享的。
cout << &e.c[0]; //Why can't I print this address
cout << e.c[1]; // Why can't I print this value

现在,这就是你越界的地方。您现在正在执行隐式指针算术并在索引到 char[] 方面取消引用。数组,即使您试图获取第一个元素的地址,也可能会评估一个元素,该元素不是 union 中的最后一个集合。所以,这是一个很大的禁忌。

此外,&e.c[0]基本上是char*它将被 cout“拦截”并被视为 C 风格的字符串。它不会把它当作一个简单的地址。
cout << e.c[1]; // Why can't I print this value

未定义的行为。 “可是,可是!” ,我能听到你们中的一些人说。是的,它是 C++ 中的 UB。在 C99 (6.5/7) 中有效,只需通过脚注和一些胶带即可。这是一个简单的问题,LightnessRacesInSpace 和 Mysticial 在这个答案和其他人的评论中已经解释过。

是的,您可以将您拥有的任何类型化变量转换为 char 数组,并出于您的任何目的使用它。但是通过 union 进行类型转换在 C++ 中是非法的,没有任何借口和借口。是的,它可能会起作用。是的,如果你不介意的话,你可以继续使用它。但是按照 C++ 标准,这显然是非法的。

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

C++ 中的 union 有一个目的,如下所述。它们还可以具有成员函数和访问说明符。它们不能有虚函数或静态成员。它们也不能用作基类或从某些东西继承。它们不能用于类型双关。在 C++ 中这是非法的。

进一步阅读!

了解 union

union 是:
  • 一种允许内存重用的方法。
  • 就是这样。

  • union 不是:
  • 一种在 union 元素之间进行牛仔类型转换的方法
  • 一种欺骗严格别名的方法。

  • 甚至 MSDN 也做对了:

    A union is a user-defined data or class type that, at any given time, contains only one object from its list of members (although that object can be an array or a class type).



    这是什么意思?这意味着您可以按照以下方式定义一些内容:
    union stuff {
    
        int i;
        double d;
        float f;    
    
    } m;
    

    这个想法是他们都坐在内存中的同一个空间。 union 的存储是从给定实现中最大的数据类型推断出来的。平台在这里有很多自由。规范无法涵盖的自由。不是 C。不是 C++。

    绝不能int 的身份写信给 union 然后将其读作 float (或其他任何东西)作为某种奇怪的牛仔 reinterpret_cast 的一种方式。

    std::cout的使用是为了示例目的和简单性。

    这是非法的:
    m.i = 5;
    std::cout << m.f; // NO. NO. NO. Please, no.
    

    这是合法的:
    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;
    

    请注意没有“交叉火力”。这是预期用途。在三个不同时间使用的三个变量的超薄内存存储,没有冲突。

    误解是如何产生的?有一天,一些非常坏的人醒来,我敢打赌其中一个是 3D 程序员,并考虑过这样做:
    // This is wrong on so many different levels.
    union {
    
        float arr[4];
        struct {
            float x,y,z,w;
        };
    
    };
    

    毫无疑问,他有一个“高尚的想法”,以浮点数组和单独的 xyzw 成员的形式访问 4 元组。现在,您知道为什么在 union 方面这是错误的,但这里还有一个失败:

    C++ 没有匿名结构 .它确实有匿名 union ,用于上述目的,使其更接近预期用途(删除 m.“前缀”),因为您肯定可以看到这如何有益于 union 背后的一般思想。

    不要这样做。请。

    关于c++ - 在 union 中使用字符数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15952204/

    相关文章:

    c++ - .obj 中已定义的函数

    c++ - 如何读取视频的亮度和对比度?

    c++ - 来自主线程的信号未到达第二线程 Qt 5 中的插槽

    c++ - 使用 ld 创建依赖于另一个共享库的共享库

    c++ - 查看字符串变量时,调试器显示 npos=4294967295

    c++ - 如何将 `__ramfunc` 固有函数应用于构造函数?

    c++ - “无匹配函数”-尝试插入集合时出错 (c++)

    c++ - 为什么我们应该为 initializer_list 的情况重载转发构造函数?

    c++ - 在 C++ 中转换类型时出错

    C++ is_member_pointer 实现