《Hands-On System Programming with C++》读书笔记之三
探索C/C++默认数据类型
字符类
- char:通常但不总是8bits, corresponding with the ASCII, 编译器区别对待它和一般整型。
#include <iostream>
int main(void)
{
int i = 0x42;
char c = 0x42;
const char *str = "Hello World\n";
std::cout << i << '\n'; // 66
std::cout << c << '\n'; // B
std::cout << str; // Hello World
}
可以用下面的代码查看系统的数据类型长度。
#include <iostream>
#include <limits>
int main(void)
{
using namespace std;
auto num_bytes_signed = sizeof(signed char);
auto min_singed = numeric_limits<signed char>().min();
auto max_signed = numeric_limits<signed char>().max();
auto num_bytes_unsigned = sizeof(unsigned char);
auto min_unsigned = numeric_limits<unsigned char>().min();
auto max_unsigned = numeric_limits<unsigned char>().max();
cout << "num bytes(signed):" << num_bytes_signed << endl; // 1
cout << "min value(signed):" << +min_singed << endl; // -128
cout << "max value(signed):" << +max_signed << endl << endl; // 127
cout << "num bytes(unsigned):" << num_bytes_unsigned << endl; // 1
cout << "min value(unsigned):" << +min_unsigned << endl; // 0
cout << "max value(unsigned):" << +max_unsigned << endl << endl; // 255
}
- wchar_t: Unicode characters. 使用前务必确定数据的格式,我的系统里是4bytes。
整数类
- short int: 我的系统中占用2 bytes。在大多数32或64位系统里,短于32位的操作数并不能提供效率,尤其是RISC指令集的CPU可能会需要额外很多指令来处理短操作数,因此需要小心使用。
- int:我的系统中占用4 bytes。在64位处理器中int仍更多保持32位是出于兼容性的考虑。
- long int:我的系统中占用8 bytes, (Windows中为4bytes,long long int才是8bytes)。
浮点数类
浮点数类型在系统调用中是较少使用的
- float:4bytes。
- double:8bytes。
- long double:16 bytes。
布尔类
此类仅在C++中定义。几乎没有系统只使用1个bit来表示bool类型,通常使用1个至8个bytes来表示。
标准整数类型
stdint.h或cstdint文件提供了标准化长度的整数数据类型,包括:
- int8_t, uint8_t
- int16_t, uint16_t
- int32_t, uint32_t
- int64_t, uint64_t
这些类型依操作系统、处理器、编译器而异调用其他的基本数据类型,可以用书中的例子来查看这些数据类型背后的底层数据类型。
#include <typeinfo>
#include <iostream>
#include <string>
#include <cxxabi.h>
using namespace std;
template <typename T>
string type_name()
{
int status;
string name = typeid(T).name();
auto demangled_name = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status);
if (status == 0)
{
name = demangled_name;
free(demangled_name);
}
return name;
}
template <typename T1, typename T2>
void are_equal()
{
#define red "\033[1;31m"
#define reset "\033[0m"
cout << type_name<T1>() << " vs " << type_name<T2>() << endl;
if (sizeof(T1) == sizeof(T2))
{
cout << " -size:both == " << sizeof(T1) << endl;
}
else
{
cout << red " -size:"
<< sizeof(T1) << " != " << sizeof(T2)
<< reset << endl;
}
if (type_name<T1>() == type_name<T2>())
{
cout << " -name:both == " << type_name<T1>() << endl;
}
else
{
cout << red " -name:"
<< type_name<T1>() << " != " << type_name<T2>()
<< reset << endl;
}
}
int main()
{
are_equal<uint8_t, int8_t>();
are_equal<uint8_t, uint32_t>();
are_equal<signed char, int8_t>();
are_equal<unsigned char, uint8_t>();
are_equal<signed short int, int16_t>();
are_equal<unsigned short int, uint16_t>();
are_equal<signed int, int32_t>();
are_equal<unsigned int, uint32_t>();
are_equal<signed long int, int64_t>();
are_equal<unsigned long int, uint64_t>();
are_equal<signed long long int, int64_t>();
are_equal<unsigned long long int, uint64_t>();
return 0;
}
当需要使用确定长度的整数时,应使用标准整数类型库,让工具帮你转换成对应的底层数据类型。
Structure packing
标准整数类型并不能保证数据占用的空间就是指定的字节数。编译器出于对齐数据或优化性能考虑,可能会用更长的数据类型替换较短的。
#include <iostream>
struct mystruct
{
uint64_t data1;
uint16_t data2;
};
int main()
{
std::cout << "size: " << sizeof(mystruct) << std::endl;
// size: 16
}
另一个例子:
#include <iostream>
struct mystruct
{
uint16_t data1:2, data2:14;
uint64_t data3;
};
int main()
{
std::cout << "size: " << sizeof(mystruct) << std::endl;
// size: 16
}
这个结果可能会使你用这个数据结构来访问你的硬件时产生错误,因为两组数据中间填充了6个bytes。解决的办法是
#include <iostream>
#pragma pack(push, 1)
struct mystruct
{
uint16_t data1:2, data2:14;
uint64_t data3;
};
#pragma pack(pop)
int main()
{
std::cout << "size: " << sizeof(mystruct) << std::endl;
// size: 10
}
上面的#pragma可以使有效数据在内存中保持连续。但是structure packing并不能确保在所有情况下都能实现。比如:
#include <iostream>
#pragma pack(push, 1)
struct alignas(16) mystruct
{
uint16_t data1;
uint64_t data2;
};
#pragma pack(pop)
int main()
{
mystruct s;
std::cout << "size: " << sizeof(mystruct) << std::endl;
// size: 16
std::cout << "address data1: " << &s.data1 << std::endl;
// 0x7fff09c5e0b0
std::cout << "address data2: " << &s.data2 << std::endl;
// 0x7fff09c5e0b2
return 0;
}
这里要求将结构体在内存中按16字节对齐摆放, 结果就会导致packing后原本占用10bytes的结构体又被扩展为16bytes。
指针类型的大小随CPU、操作系统和程序运行的模式不同而不同。