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

熊孩纸 阅读:156 2022-06-06 14:14:13 评论:0

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; 2147483489 7fffff61 61 ff ff 7f
u1.s = 32609; 32609 7f61 61 7f ff 7f
u1.c = ‘A’; 65 41 41 7f ff 7f
  • u2的内存中的数据变化如下:
赋值操作 十进制 十六进制 内存第1字节 内存第2字节 内存第3字节 内存第4字节
u2.c = ‘A’; 65 41 41 00 00 00
u2.s = 32609; 32609 7f61 61 7f 00 00
u2.i = 2147483489; 2147483489 7fffff61 61 ff ff 7f
  • 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; 
} 

标签:C++
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

搜索
排行榜
关注我们

一个IT知识分享的公众号