前言
最近在学习数据结构的时候发现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
;