C++ union共用体联合体用法大全_看这篇就够了

1. 背景

关于union的知识点,教材和网上的资料都比较少,经过不断的查阅各种资料和测试,对union有了一些基本了解,这里进行整理,方便以后需要时查看

2. union存在的必要性

我们先来看下面的两段对比的代码,体验以下union的用处

#include <iostream>
using namespace std;

int main() {
    string name;

    int score;
    char degree;
    bool is_pass;

    name = "Li Ming";

    score = 90;
    cout << name << "---math: " << score << endl;       // Li Ming---math: 90

    degree = 'B';
    cout << name << "---english: " << degree << endl;   // Li Ming---english: B

    is_pass=true;
    cout << name << "---politics: " << is_pass << endl; // Li Ming---politics: 1

    return 0;
}
#include <iostream>
using namespace std;

union {
    int score;
    char degree;
    bool is_pass;
} u;

int main() {
    string name;

    name = "Li Ming";

    u.score = 90;
    cout << name << "---math: " << u.score << endl;       // Li Ming---math: 90

    u.degree = 'B';
    cout << name << "---english: " << u.degree << endl;   // Li Ming---english: B

    u.is_pass=true;
    cout << name << "---politics: " << u.is_pass << endl; // Li Ming---politics: 1

    return 0;
}

说明:

  • 如上面所示,科目得分这一数据字段,有几种不同的数据形式;在代码片段1中,我们定义了3个变量来表示3种不同的科目得分,所有需要在内存中分配3块内存,有没有办法只分配1块内存,就能完成这个任务呢,这个办法就是union
  • 在代码片段2中,我们定义了一个union类型的数据,union中的3个数据成员共享1块内存,且3个数据成员的内存起始地址一样,这在内存紧张的情况下非常有用,该内存的长度计算方法如下:
    • 此长度能容纳最大长度的数据成员
    • 此长度是构成所有数据成员的基本数据类型长度的整数倍,如char a[5]的基本数据类型长度为1
  • 下面我们通过一段代码来验证一下:
#include <iostream>
using namespace std;

union {
    int i;
    double d;
    char c[10];
} u;


int main() {
    cout << &u << endl;     // 0x407040
    cout << &u.i << endl;   // 0x407040
    cout << &u.d << endl;   // 0x407040
    cout << &u.c << endl;   // 0x407040

    cout << sizeof(u) << endl;      // 16

    return 0;
}

因为int的字节数为4,double的字节数为8,char的字节数为1,所有数据成员的最长长度为10,但10不是4和8的倍数,所有该union的长度为16

3. union的几种定义方式

3. 1 只定义类型

#include <iostream>
using namespace std;

union u_type1 {
    int i;
    double d;
    char c[10];
};

int main() {
    
    u_type1 u1;
    u1.i = 1;
    cout << u1.i << endl;       // 1
    
    return 0;
}

3.2 定义类型的同时定义变量**

#include <iostream>
using namespace std;

union u_type2 {
    int i;
    double d;
    char c[10];
} u2;

int main() {

    u2.i = 2;
    cout << u2.i << endl;       // 2

    return 0;
}

3.3 定义无类型名的union变量

#include <iostream>
using namespace std;

union {
    int i;
    double d;
    char c[10];
} u3;

int main() {

    u3.i = 3;
    cout << u3.i << endl;       // 3

    return 0;
}

3.4 定义无类型名的union
此方式定义的union和union中数据成员的使用需要在同一作用域中

#include <iostream>
using namespace std;

int main() {
   
    union {
        int i4;
        double d4;
        char c4[10];
    };

    i4 = 4;
    cout << i4 << endl;         // 4
    
    return 0;
}

4. 数据成员内存覆盖的问题

4.1 十六进制的一个字节几位

我们都知道数据在内存中是以二进制的方式存在的,一个字节有8位

但我们debug的时候都喜欢用十六进制,那是因为十六进制的更加便于我们理解数据,那十六进制的一个字节有几位?

这里我们只讨论正数,方便理解,一个二进制字节能表示的数据范围为00000000-11111111,即0-255,对应的十六进制为00-ff,也是2位十六进制能表示的数据范围,所以十六进制一个字节有2位

4.2 多字节的高位与低位

#include <iostream>
using namespace std;

int main() {

    int a = 5456511841;
    cout << hex << a << endl;       // 453bc361

    short *p;
    p = (short *)&a;
    cout << hex << *p << endl;             // c361
    
    return 0;
}

对于上面的a,有4个字节,那在内存中是以453bc361,还是以61c33b45的方式存在?
正确的答案是,不管是存放还是读取,都是数据和内存的高低位一一对应,即数据的低位对应内存的低位,数据的高位对应内存的高位
所有在内存中以61c33b45的方式存在;指针p读取前两个字节,即61c3,对应的数据就是c361

4.3 union数据成员内存覆盖

我们先看下面的例子:

#include <iostream>
using namespace std;

union {
    char c;
    short s;
    int i;
} u1, u2;

int main() {
    // =================u1====================
    u1.i = 2147483489;
    cout << hex << u1.c << endl;        // a
    cout << hex << u1.s << endl;        // ff61
    cout << hex << u1.i << endl;        // 7fffff61
    cout << endl;

    u1.s = 32609;
    cout << hex << u1.c << endl;        // a
    cout << hex << u1.s << endl;        // 7f61
    cout << hex << u1.i << endl;        // 7fff7f61
    cout << endl;

    u1.c = 'A';
    cout << hex << u1.c << endl;        // A
    cout << hex << u1.s << endl;        // 7f41
    cout << hex << u1.i << endl;        // 7fff7f41
    cout << endl;

    // =================u2====================
    u2.c = 'A';
    cout << hex << u2.c << endl;        // A
    cout << hex << u2.s << endl;        // 41
    cout << hex << u2.i << endl;        // 41
    cout << endl;

    u2.s = 32609;
    cout << hex << u2.c << endl;        // a
    cout << hex << u2.s << endl;        // 7f61
    cout << hex << u2.i << endl;        // 7f61
    cout << endl;

    u2.i = 2147483489;
    cout << hex << u2.c << endl;        // a
    cout << hex << u2.s << endl;        // ff61
    cout << hex << u2.i << endl;        // 7fffff61
    cout << endl;

    return 0;
}

说明:

  • char为1字节数,short为2字节数,int为4字节数,所以union的字节长度为4
  • u1的内存中的数据变化如下:
赋值操作十进制十六进制内存第1字节内存第2字节内存第3字节内存第4字节
u1.i = 2147483489;21474834897fffff6161ffff7f
u1.s = 32609;326097f61617fff7f
u1.c = ‘A’;6541417fff7f
  • u2的内存中的数据变化如下:
赋值操作十进制十六进制内存第1字节内存第2字节内存第3字节内存第4字节
u2.c = ‘A’;654141000000
u2.s = 32609;326097f61617f0000
u2.i = 2147483489;21474834897fffff6161ffff7f
  • union中的各个数据成员都可以取值和赋值,取值和赋值都只操作从起始地址开始的各自字节数,但union中最多只有一个数据成员是有意义的

5. union的函数成员、构造函数、析构函数

#include <iostream>
using namespace std;

union u_type {

    u_type() {
        cout << "constructor" << endl;
    }

    char c;
    short s;
    int i;

    void print() {
        cout << "hello world" << endl;
    }
    
    ~u_type() {
        cout << "destructor" << endl;
    }
    
} u;

int main() {
    
    u.print();
    
    return 0;
}

结果如下:

constructor
hello world
destructor

6. 控制访问权限

默认访问权限是public

#include <iostream>
using namespace std;

union {

public:
    char c;

protected:
    short s;

private:
    int i;

} u;

int main() {

    u.c = 'a';
    cout << u.c << endl;

    return 0;
}

7. union的局限性

  1. union中的对象成员,不能有构造函数、析构函数、重载的复制赋值运算符;对象成员的对象成员也不能有,依此类推
  2. union不能继承

8. union实战例子

使用union保存成绩信息,并输出

#include <iostream>
using namespace std;

class ExamInfo {
    
public:
    ExamInfo(string n, char g):name(n),mode(GRADE),grade(g){}
    ExamInfo(string n, bool p):name(n),mode(PASS),grade(p){}
    ExamInfo(string n, int p):name(n),mode(PERCENTAGE),grade(p){}

    void show();

private:
    string name;
    enum {GRADE, PASS, PERCENTAGE} mode;
    union {
        char grade;
        bool pass;
        int percent;
    };

};

void ExamInfo::show() {
    cout << name << ": ";

    switch(mode) {
        case GRADE: cout << grade;break;
        case PASS: cout << pass ? "PASS" : "FAIL";break;
        case PERCENTAGE: cout << percent;break;
    }

    cout << endl;
}

int main() {

    ExamInfo examInfo1("English", 'B');
    ExamInfo examInfo2("Calculus", true);
    ExamInfo examInfo3("C++ Programming", 85);

    examInfo1.show();       // English: B
    examInfo2.show();       // Calculus: 1
    examInfo3.show();       // C++ Programming: 85
    
    return 0;
}
  • 18
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值