C++11中枚举enum和union,顺带说一下内存对齐和大小端问题

这两个不太常用的小知识点,今天碰到了,所以特来总结一下。

union类

源于下面这道题

union X
{
    int a;
    struct
    {
        short b;
        short c;
    };
};
int main(){
    X x;
    x.a = 0x20150810;
    printf("%x,%x\n", x.b, x.c);
    return 0;
}

C++11之前的union

union就是里面的成员共用同一个存储空间,这个存储空间的大小与union中占用空间最大的一个成员相同

大小端问题

问输出结果的可能的值
如果按照大端模式存储:从低地址到高地址:20 15 08 10
输出从低地址到高地址:20 15 08 10
如果按照小端模式存储:从低地址到高地址:10 08 15 20
输出从高地址到低地址:08 10 20 15
一般intel平台就是小段机,所以我的电脑上就显示810,2015
大段就是一个数据,里面字节(即8位)储存的顺序跟内存一样是从低到高的
而小端就是刚好反过来。记着输出的时候还要再反义词别忘了,就跟上面说的那样。
再来一个union的题巩固一下

union S
{
    std::int32_t n;     // occupies 4 bytes
    std::uint16_t s[2]; // occupies 4 bytes
    std::uint8_t c;     // occupies 1 byte
};                      // the whole union occupies 4 bytes

int main()
{
    S s = {0x12345678}; // initializes the first member, s.n is now the active member
    // at this point, reading from s.s or s.c is undefined behavior
    std::cout << std::hex << "s.n = " << s.n << '\n';
    s.s[0] = 0x0011; // s.s is now the active member
    // at this point, reading from n or c is UB but most compilers define it
    std::cout << "s.c is now " << +s.c << '\n' // 11 or 00, depending on platform
              << "s.n is now " << s.n << '\n'; // 12340011 or 00115678
}

内存对齐问题

内存对齐用#pragma pack(n) 设置,n代表设置的内存对其长度,一个细节是n只能设置为1,2,4,8,16这些数,设置别的数是无效的,等于没有设置,还是编译器自己默认的大小
对其规则:

1、第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、在数据成员完成各自对齐之后,类(结构或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

我说下我的个人理解,就是先看这个数据成员中最大的那个有多大,选择其和#pragma pack中较小的那个,就是这个数据结构中需要对齐的量。然后放进去的每一个数也都要对齐,其起始位置要是自身大小/或者设置的对其大小较小值的倍数关系。

struct A{
    char a[5];      //占了1~5个位置
    short tt;       //因为short占两个位置,所以其选择从7号位开始,
    char b[5];
};
cout<//输出14
//其中最大的对齐量为short,所以A结构内存占据如下:
|a[0]|a[1]|
|a[2]|a[3]|
|a[4]| 空 |
| tt | tt |
|b[0]|b[1]|
|b[2]|b[3]|
|b[4]| 空 |

struct B{
    short tt;         //占据了1~2字节
    char a[5];          //跟在tt后面占据了3~7字节
    char b[5];          //跟在a后面,占据了8~12字节
};
cout<//输出12
所以B结构内存占据如下:
| tt | tt |
|a[0]|a[1]|
|a[2]|a[3]|
|a[4]|b[0]|
|b[1]|b[2]|
|b[3]|b[4]|

C++11之后的union

C++11之后union具有的特性

  1. 能够有成员函数,包括构造函数和析构函数(跟类非常像吧)
  2. 不能继承,也不能被继承,也不能有虚函数
  3. 不能有引用成员,不管是左值引用还是右值引用,不能有静态成员变量,但是可以有静态成员函数

枚举类型

C++11之前就有枚举类型(Uncoped enumerations),后来因为其类型不安全,所以C++又加入了枚举类(Scoped enumerations),详见cppreference里的enumeration declaration

无作用域的枚举类型(Uncoped enumerations)

定义可以用如下方法

enum name { enumerator = constexpr , enumerator = constexpr , ... }(1)  
enum name : type { enumerator = constexpr , enumerator = constexpr , ... }  (2) (since C++11)
enum name : type ;  (3) (since C++11)

具体例子

enum Color { red, green, blue };      //枚举类型默认的是int类型
Color r = red;        //red默认从0开始,green为1,blue为2
int a = red;     //可以直接拿来用red
int b = r;       //枚举类型也可以隐式的转换成为int的
Color rw = static_cast(10);  //而整型并不能隐式的转换为枚举类型,所以需要显示的类型转换

因enum的元素可以直接拿在外部用,所以不同的枚举类型不能定义相同名字的元素

枚举类型默认的赋值规则是,如果没有实现定义值,则从0开始,后面的每一个元素比前面的大1,如果定义的话,就是定义的值,后面的元素也依然比前面大1

enum Foo { a, b, c = 10, d, e = 1, f, g = f + c };
//a = 0, b = 1, c = 10, d = 11, e = 1, f = 2, g = 12

有作用域的枚举类型

这是C++11为了填坑来弄的(感觉是不是需要专门总结一下C++11新加入的东西哪些是新的,哪些是为了填坑才弄的)
定义如下:

enum struct|class name { enumerator = constexpr , enumerator = constexpr , ... }    (1) 
enum struct|class name : type { enumerator = constexpr , enumerator = constexpr , ... } (2) 
enum struct|class name ;    (3) 
enum struct|class name : type ; (4) 

就是在原有方法的基础上中间加入enum。
这是枚举类中的元素就不能全局使用了,要使用必须加上作用域,而且也不能进行隐式的类型转换了。

enum class Color { red, green = 20, blue };
Color r = Color::blue;
int n = static_cast<int>(r); // OK, n = 21

另外还有个小知识就是C++17标准中,可以用初始化列表去初始化enum类型了

enum byte : unsigned char {}; // byte is a new integer type
byte b { 42 }; // OK as of C++17 (direct-list-initialization)
byte c = { 42 }; // error
byte d = byte{ 42 }; // OK as of C++17; same value as b
byte e { -1 }; // error

上面是那个cppreference给的例子,只不过我手边所有的编译器都还不支持C++17,所以也没法验证。

初始化规则

enum的初始化规则和其他类型一样,局部变量就随机初始化,全局或者静态变量就初始化为0

enum Color { red = 10, green = 20, blue } a;
cout<//输出0,因为a是全局变量,所以自动初始化为0

你可能感兴趣的:(语言语法)