前言
最近在学习数据结构的时候发现C++里面的很多数据类型、运算符的常识在特殊情况下能起到奇效,所以我想对这些基础做一个小小的总结,以方便后续使用。
1.基础数据类型
对于数据类型,我们需要掌握的是有哪些数据类型,每种数据类型在32位/64电脑上的内存占用如何,其数值范围是多少,而这些都是一一相关的,数值范围自然只跟数据所占字节位数以及unsigned和signed有关:
| 数据类型 | 说明 | 32位字节数 | 64位字节数 | 取值范围 | 16进制 | 量级 |
|---|---|---|---|---|---|---|
| bool | 布尔型 | 1 | 1 | true,false | - | - |
| char | 字符型 | 1 | 1 | -128~127 | 0x7F | 百 |
| unsigned char | 无符号字符型 | 1 | 1 | 0~255 | 0xFF | 百 |
| short | 短整型 | 2 | 2 | -32768~32767 | 0x7FFF | 3万 |
| unsigned short | 无符号短整型 | 2 | 2 | 0~65535 | 0xFFFF | 6万 |
| int | 整型 | 4 | 4 | -2147483648~2147483647 | 0x7FFFFFFF | 21亿 |
| unsigned int | 无符号整型 | 4 | 4 | 0~4294967295 | 0xFFFFFFFF | 42亿 |
| long | 长整型 | 4 | 8 | - | - | - |
| unsigned long | 无符号长整型 | 4 | 8 | - | - | - |
| long long | 长整型 | 8 | 8 | - | - | - |
| float | 单精度浮点数 | 4 | 4 | - | - | - |
| double | 双精度浮点数 | 8 | 8 | - | - | - |
| * | 指针 | 4 | 8 | - | - | - |
| ssize_t | 计数类型 | 4 | 8 | - | - | - |
| size_t | 无符号计数类型 | 4 | 8 | - | - | - |
注:1字节byte=8位bit,1KB=1024B=2^13bit,1M=1024KB==2^23bit,1G=1024M=2^33bit
其中要特殊注意的地方有:
受操作系统位数影响的数据类型只有
long、指针、size_t等基础类型,都是从4字节变为8字节;size_t和ssize_t等同于long和unsigned long;
对于大数据处理,利用int的数值范围作为bit数组的索引,其中
unsigned int的取值范围为0~2^32-1,所以存储一个42亿大小的bit数组需要用1/2G=512M内存;char字符串数组的定义形式有:char a[] = {‘a’, ‘b’, ‘\0’},或者char a[] = “ab”;
由于操作系统中编译器会进行内存对齐,其中32位/x86系统对齐内存为4字节,x64系统对齐内存为8字节,即内存地址是4的倍数,这就意味着,对于32位系统中的例子如下:
1
2
3
4
5
6
7struct A{
A(){}
~A(){}
int m1;
char m2;
static char m3;
}这个结构体占8字节内存,这是因为类的大小只与成员变量(非static成员)的虚函数指针有关,因此只需要考虑
m1和m2的内存占用,即4+1=5,但是由于内存对齐的作用,其会占用8字节。指针的内存占用与指向的内容无关,即double *并不是说指针类型为double;
float和double的取值范围计算方式很独特,比如float的4字节=32位,包括1符号位+8指数位+23尾数位,所以其取值范围为:
$$ value = significand \times {2^{exponent}} $$
由于指数位为8,所以指数范围为-127~128,那么其取值范围为-2^128~2^128,而尾数位23决定了其精度,即2^23 = 8388608,共7位,所以只能有7位有效数字,其中只能确保6位。同理double有1符号位+12指数位+53尾数位,保证15~16个有效数字。
2.Ascii码
ASCII 码使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符,一般常用的字符只有128种。其中我们要特殊注意的有:
- ascii码中的48~57对应十进制中的0~9;
- ascii码中的65~90对应大写字母中的A~Z;
- ascii码中的97~122对应小写字母中的a~z;
3.运算符
3.1算术运算符

其中要特殊注意的是有:
+和*运算符可能造成数据内存溢出;/默认返回int类型的结果;++和--需要注意的是:1
2i++ => y = i;i = i + 1;return y;
++i => i = i + 1; return i;相对来说,
i++多了一个临时变量,另外它的过程是先引用在自增,一定要注意,一般来说自增操作会在最后执行,具体看后续的运算符优先级。
3.2关系运算符

3.3逻辑运算符

这里要注意的是在使用逻辑运算符时,一定要判断何时为true,何时为false,在c++中,以下情况会被认为false:0、false、NULL、\0、nullptr。
3.4位运算符

位运算符主要包括与(&),或(|),非(~)和异或(^),之所以叫位运算,是因为其是针对2进制数进行操作的,我们利用位运算可以做很多便捷的事情:
- 对于
&,利用这个我们可以用来逐渐将一个数变为0,通过n&(n-1),每次执行都会将一个1的位置零,因此其有效执行次数就是这个数的2进制形式中的1的个数; - 奇偶性:
a&1为0时候表示其为偶数,否则为奇数; - 取相反数:
~a+1这个过程就是负数的2进制转换操作,先取反,然后+1; - 取第k位数:a>>k&1,其中k是从0开始计算的;
- 每8位取出数,可用于图像:a>>24&0xFF,a>>16&0xFF,a>>8&0xFF,a&0xFF;
- 判断正负:
a>>31为0则正,否则为负。
3.5赋值运算符

3.6杂项运算符

这里面有几个要注意的点有:
sizeof返回的是数据的内存占用,单位为字节,而&返回的是数据的地址,*返回的是指针指向的数据内容;,运算符返回的是最后一条语句的值,eg:a=(1,2,3),返回3;.和->都可以被用来引用成员,但是->一般用于指针类型结构体/类;cast并不是真正的运算符,其包括static_cast、dynamic_cast、reinterpret_cast和const_cast等方式,具体不描述。
3.7运算符优先级

运算符的优先级很重要,比如下面几个例子:
1 << 3 + 2 & 7等价于(1 << (3 + 2))&7;a++*2等价于y=a*2; a=a+1; return y;++a*2等价于a=a+1; return a*2;

