写这篇,一是想巩固下基础知识;二是发觉网上很多写的也是错漏较多,模糊了去查了看需要很多时间去分辨对错;故自己总结下,自觉应该不会出错吧(错了欢迎指出,我再纠正。)- -|||
一、C/C++内置类型长度
我们知道C++内置类型有bool, char, short, int, long, long long, float, double, long double, wchar_t
这些。每种类型的长度都不一样,但是C/C++标准并没有规定每个类型必须要占多少字节,而是规定了长度的最小值等一些约束。所以实际上各个类型的变量长度是由编译器来决定的。
那么编译器是根据什么来决定类型的长度呢?操作系统位数orCPU位数?
我们知道这样一个关系:
- 32位编译器编译出来的是32位应用程序;64位编译器编译出来的是64位应用程序。
而这里编译器是32位or64位指的是利用该编译器生成的应用程序是32位or64位;与该编译器应用程序自身是32和64位的没有关系,也就是与操作系统是32位or64位没有绝对关系,同样与CPU位数也没有绝对关系。
比如在64位系统上的32位编译器,其应用本身可以是32位的也可以是64位的;同样的,理论上在32位系统上一样可以做出64位的编译器, 其应用本身是32位的,但是可以生成64位程序(只是32位的系统不能运行64位的应用程序)。
事实上,编译器依据一种数据模型来决定其类型的长度。这些数据模型有:ILP32, ILP32LL, LP64, LLP64, ILP64等。其中I,L,P分别指int,long, pointer (如ILP32LL与ILP32LL64等同,意为int,long,pointer为32位,longlong为64位)。其中各种模型中类型的长度区别为:
类型 | ILP32 | ILP32LL | LLP64 | LP64 | ILP64 |
---|---|---|---|---|---|
char | 8 | 8 | 8 | 8 | 8 |
short | 16 | 16 | 16 | 16 | 16 |
float | 32 | 32 | 32 | 32 | 32 |
int | 32 | 32 | 32 | 32 | 64 |
long | 32 | 32 | 32 | 64 | 64 |
pointer | 32 | 32 | 64 | 64 | 64 |
long long | … | 64 | 64 | 64 | 64 |
double | 64 | 64 | 64 | 64 | 64 |
现如今,32位的windows程序的数据模型为ILP32LL;64位的Windows程序数据模型是LLP64;绝大部分Unix,linux编译器和应用程序都是使用的LP64模型。
二、数值常量的类型
2.1 数值常量的表示方法
C/C++的数字常量有这样几种表示:18, -23, 3., -5.4, 2E-2, 07, -036, 0x3a, -0xFF
。
其中18, -23, 3., -5.4, 2.9E-2
为10进制表示, 其中2.9E-2为科学计数法(10进制指数表示);07, -036
为8进制表示;0x3a, -0xFF
为16进制表示。
其中18, -23, 07, -036, 0x3a, -0xFF
皆为整数型常量;3., -5.4, 2E-2
皆为浮点数型常量(小数型常量)。
从中我们可以总结出:
- 以0开头的为8进制常量,0后只能以数字0~7表示;
- 以0x开头的为16进制常量,其后只能以数字和字母0~F表示;
- 其他为10进制常量,整形不能以0和0x开头,只能以0~9和小数点表示;
- 8进制和16进制不能用来表示浮点数;
- 科学计数法表示的一定是浮点数
2.2 数值常量的类型和后缀
C/C++数值常量后可以跟随一个后缀来给编译器指示类型;如果不加后缀,编译器会根据数值的范围指定合适的类型。
编译器自动选择:
- 如果是浮点型常量,则指定double类型;
- 如果是整数型常量,如果在int表示的范围内,则指定为int;如果超出int,对于正数,按照
int->unsigned int->long->unsigned long->long long->unsigned long long
的方向指定合适的类型;对于负数,按照int->long->long long
的方向指定合适的类型。
指定后缀:
整数型后缀有U,L或其组合(UL, LL,ULL或LLU);其中U,L不区分大小写,分别表示unsigned 和long;如ull和llu都表示unsigned long long。
小数型后缀有F, L。分别表示float,long double。即单精度浮点数和长双精度浮点数。
整数后接f,或者小数后接U都会报错。
三、隐式转换
3.1 隐式转换场合
C/C++在以下四种情况下会进行隐式转换:
- 算术运算式中,低类型能够转换为高类型。
- 赋值表达式中,右边表达式的值自动隐式转换为左边变量的类型,并赋值给他。
- 函数调用中参数传递时,系统隐式地将实参转换为形参的类型后,赋给形参。
- 函数有返回值时,系统将隐式地将返回表达式类型转换为返回值类型,赋值给调用函数。
3.2 算数运算的隐式转换
算数运算中,首先有如下类型转换规则:
- char和short先转换为int。
- float型数据在运算时一律转换为双精度(double)型,以提高运算精度(同属于实型) 。
- 当不同类型的数据进行操作时,应当首先将其转换成相同的类型,然后进行操作,转换规则是由低级向高级转换。转换规则如下图所示:
- 算数表达式的值类型与参与运算的值类型相同。
四则运算中,特别要注意unsigned运算。实际编程中最好避免unsigned值参与运算。
举例:
1 | //signed与unsigned int运算时,先都转换为unsigned |
四、64位编程应注意事项
4.1 格式化字符串:long使用%ld,指针使用%p
32位下,打印地址可以用%x,但在64位下只能打印低4位;所以64位应用%p。
1 | char *ptr = &something; |
4.2 64位下的对齐
64位下,因为long和指针的长度可能发生了变化,所以32位下对齐良好的的结构可能变得不太好,导致32位程序在64位系统上运行性能下降。如:
1 | struct A { |
ILP32下,sizeof(A)=16; 但是在LP64下,sizeof(A)=32。