1-05 C++起步: 结构与联合 —— C++对联合的扩充

结构的数据成员各自占用一块内存空间, 它们形成一个整体, 所占空间是全部成员所占空间的总和。联合与此不同, 它的数据成员重叠挤占同一段内存空间, 有点像"一套班子, 多块招牌", 它们形成的整体, 所占空间取成员中占用空间最大者。下图表示了结构和联合与区别:

struct A {
    int x;
    float y;
};

 
xy
    
    
 

结构A拥有2个成员:
    x: 4字节, 整数
    y: 4字节, 单精度实数
两个成员各自分配内存空间
整个结构占空间8字节

union B {
    int x;
    float y;
};

 
x
    
y
 

联合B拥有2个成员:
    x: 4字节, 整数
    y: 4字节, 单精度实数
两个成员挤占同一段内存空间
整个联合占空间4字节

在程序设计中, 联合一般用于把同一段内存空间, 根据不同的情况存储不同类型的数据。下面以几何图形为例说明结构和联合的用法。

对于点, 需要保存该点的横坐标x和纵坐标y; 对于直线, 需要保存直线方程ax + by + c = 0的三个系数; 对于矩形, 需要保存两个点 —— 左上角p1和右下角p2的坐标; 对于圆, 需要保存圆心点center的坐标和半径radius等, 下面是点、线、矩形和圆的结构定义:

POINT结构表示点, 它有两个成员, 分别是点的横坐标x(实数)和纵坐标y(实数):

struct POINT { // 点(16字节)
    double x, y; // 坐标: (x, y)
};

xy
        
        

LINE结构表示直线, 它有三个成员, 分别是直线方程的三个系数(实数):

struct LINE { // 线(24字节)
    double a, b, c; // 直线方程: ax + by + c = 0
};

abc
        
        
        

RECT结构表示矩形, 它有两个成员, 分别是左上角和右下角的点(POINT结构):

struct RECT { // 矩形(32字节)
    POINT p1, p2; // 两点: 左上角和右下角
};

p1p2
xy
        
        
xy
        
        

CIRCLE结构表示圆, 它有两个成员, 分别表示圆心点(POINT结构)和半径(实数):

struct CIRCLE { // 圆(24字节)
    POINT center; // 圆心
    double radius; // 半径
};

centerradius
xy
        
        
 
        

对于一个几何图形, 它要么是点, 要么是直线, 要么是矩形, 要么是圆, ...。它不可能既是点, 又是线, 所以只需要用一套内存空间来表示即可。这里可以用联合来表示它:

union SHAPE { // 几何图形(32字节)
    POINT p; // 点(16字节)
    LINE l; // 直线(24字节)
    RECT r; // 矩形(32字节)
    CIRCLE c; // 圆(24字节)
};

p(点) 
l(直线) 
r(矩形)
c(圆) 
        
        
        
        

虽然从理论上说可以这么做, 但实际使用时却不方便, 因为我们并不清楚这个联合现在到底表示的是什么! 下面是改进的方案, 增设一个标记成员来识辨现在的图形到底是什么! 我们当然可以用编号来表示, 比如: 0 - 表示点, 1 - 表示直线, 2 - 表示矩形, 3 - 表示圆, ...。为了方便, 使用枚举来代替编码, 更直观:

enum SHAPE_TAG {point, line, rect, circle}; // 图形标识类(枚举)

SHAPE_TAG是枚举类型名, 该类型可取的值有: point, line, rect, circle, 这四个单词分别表示整数0, 1, 2, 3, 用来表示图形类别编号。下面是几何图形的结构定义, 在结构中嵌套了一个无名的联合:

struct SHAPE { // 几何图形结构(36字节)
    SHAPE_TAG tag; // 图形标记(4字节);
    union { // 图形联合体(32字节)
        POINT p; // 点
        LINE l; // 直线
        RECT r; // 矩形
        CIRCLE c; // 圆
    };
};

tagp, l, r 或 c
    
        
        
        
        

在几何图形结构里, 有两个成员, 一个是图形标记(枚举, 4字节), 另一个是一个图形联合体(32字节), 它要么是点p(16字节), 要么是直线l(24字节), 要么是矩形r(32字节), 要么是圆c(24字节)。


C++除对结构(struct)进行了扩充以外, 还对联合(union)进行了扩充。在联合中, 不仅可以包含数据成员, 还可以包含成员函数, 和结构中的成员函数相同。

下面来研究一下CPU内部实数的表示方法。整数的表示方法大家都比较熟悉: 有符号的整数在大多数机器里, 都采用补码来表示。而实数的表示方法却比较复杂, 从理论上说, 实数用科学计数法表示。例如:

5.125 = (101.001)2 = (1.01001 × 1010)2

单精度实数(32位)的存储格式为:

符号1位0 - 表示正数, 1 - 表示负数
阶码8位用移码表示的指数, 即: 指数 + 增量(127), 10 + 1111111 = 10000001
尾数23位小数部分(不含小数点前的1): 01001000000000000000000

一般移码是将原码最高位求反, 即00000010的移码为10000010, 相当于增加128; 而Intel的增量为127。小数点前的数字1是有效数字, 应该保存它, 但由于非零二进制实数小数点前的数字只可能是1, Intel的为了节省存储空间, 没有保留这个1, 做计算时将它还原出来, 计算完毕又去掉它。对于特殊的实数0, 则以32位全0数码来表示。

符号阶码尾数
0
10000001
01001000000000000000000

即: 5.125在机内的32位编码是0100 0000 1010 0100 0000 0000 0000 0000, 用十六进制数书写则是: 40a40000。

下面是一个小程序, 从键盘输入实数, 然后在屏幕上输出该实数的十六进制编码; 或者从键盘输入编码, 然后在屏幕上显示对应的该实数。要实现这个功能, 就必须把4个字节的内存空间, 时而看成是单精度实数, 时而看成是无符号整数, 这正是联合的含义。程序如下:

FLOAT.H

#ifndef _FLOAT_H_
#define _FLOAT_H_

union FLOAT {
    float value; // 实数值
    unsigned code; // 编码(无符号整数)
    void InputValue(); // 输入实数值
    void OutputValue() const; // 输出实数值
    void InputCode(); // 输入编码
    void OutputCode() const; // 输出编码
};

#endif

FLOAT.CPP

#include <iostream>
using namespace std;

#include "float.h"

void FLOAT::InputValue() {
    cout << "请输入实数值: "; cin >> value;
}

void FLOAT::OutputValue() const {
    cout << "实数值: " << value << endl;
}

void FLOAT::InputCode() {
    long flag = cin.setf(ios::hex, ios::basefield); // 保存原格式, 变为十六进制输入格式
    cout << "请输入实数编码(十六进制): "; cin >> code;
    cin.setf(flag); // 恢复原格式
}

void FLOAT::OutputCode() const {
    long flag = cout.setf(ios::hex, ios::basefield); // 保存原格式, 变为十六进制输出格式
    cout.width(8); // 设置显示宽度
    cout << "实数编码(十六进制): " << code << endl;
    cout.setf(flag); // 恢复原格式
}

MAIN.CPP

#include <iostream>
using namespace std;

#include "float.h"

int main() {
    FLOAT a;
    a.InputValue(); // 输入实数值
    a.OutputCode(); // 输出十六进制编码
    a.InputCode(); // 输入十六进制编码
    a.OutputValue(); // 输出实数值
    return 0;
}

程序运行的结果如下:

请输入实数值: 5.125
实数编码(十六进制): 40a40000
请输入实数编码(十六进制): 66ff157f
实数值: 6.023e+023

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值