这两个不太常用的小知识点,今天碰到了,所以特来总结一下。
源于下面这道题
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;
}
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之前就有枚举类型(Uncoped enumerations),后来因为其类型不安全,所以C++又加入了枚举类(Scoped enumerations),详见cppreference里的enumeration declaration
定义可以用如下方法
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