计算机处理的对象是数据,而数据是以某种特定的形式存在的。其实我们也可以从人的角度来思考。计算机终归要为人服务,用于处理生活中的各种信息,而人所要处理的这些信息的表现形式就是数据对象。所以由此可见数据类型的总要性,也是各大公司笔试面试的常考内容之一。
C++语言中的数据类型大致可分为基本数据类型、复合类型以及一个特殊的类型—指针类型。指针将专门写一篇总结。
一、基本数据类型
基本数据类型,可分为整形、字符型、浮点型以及布尔型几大类,其中整形和浮点型可根据有无符号或精度问题可再细分。
表1 基本数据类型及其相关信息
类型 | 标识 | 字节 | 范围 | |
整形 | 短整形 | short | 2 | -32768~32767 |
整形 | Int | 2 | -2147483648~2147483647 | |
长整形 | long | 4 | -2147483648~2147483647 | |
无符号短整形 | unsigned short | 4 | 0~65535 | |
无符号长整形 | unsigned long | 4 | 0~4294967295 | |
无符号整形 | unsigned int | 4 | 0~4294967295 | |
字符 | 字符型 | char | 1 | -128~127 |
无符号字符型 | unsigned char | 1 | 0~255 | |
浮点型 | 单精度型 | float | 4 | 3.4*10(-38)~3.4*10(38) |
双精度型 | double | 8 | 1.7*10(-308)~1.7*10(308) | |
长双精度型 | long double | 8 | 1.7*10(-308)~1.7*10(308) |
上表是关于基本数据类型的标识符以及其在存储时所占的空间(注意:对于不同的编译器分配的字节数不同,这里指针是常规下的)以及其范围。
二、复合类型
复合类型,主要包括枚举类型、数组类型、结构体、共用体以及类。下面简单的介绍下概念,主要内容会再第三部分的笔试面试里通过例子描述。
1、枚举类型
枚举类型是c++提供的另一种创建符号常量的方式,这种方式可以替代const。需要注意的是,在默认情况下,将整数赋给枚举量,第一个枚举量默认值为0。
枚举类型有种符号化的定义方式,即每个变量的值按照2的幂递增,使其能够进行运算。具体这里不详述。
2、数组
数组也是笔试面试中会常考的一个内容。需要特别注意的一个问题就是数组名就是整个数组的首地址。还有就是字符数据,它是以‘/0’结束。
3、结构体和类
两者大致功能相同,区别在于结构体成员默认作用域是public,而类是private。还有一个常考内容就是内存对齐问题。
4、共用体Union
共用体内的成员共用同一块内存,只能同时存储其中的一种类型。
三、笔试面试常考问题
以下为各大公司在招聘时,针对数据类型这一块常考的内容。
1、数据类型转换
这是针对基本数据类型考的比较多的一部分。数据类型的转换主要有以下几种情况:
(1)在混合类型的算数表达式中
在这种情况下,c++将对值进行转换,主要包含两种情况:
l 自动转换:在计算表达式时,C++将bool、char、unsigned char和short等转换为int。这种转换为整形提升。
如:short a = 3;
short b = 5;
short c = a + b; //此时计算时,是将a和b同时提升为int类型,再将结
//果转换为short。
l 有些类型在与其他类型同时出现在表达式中时,较小的类型将被转换成较大的类型。如int转成double。
注:传统C语言总是将float提升为double,即使两个数都是float类型。还有一个规则就是只要某个操作数是无符号类型,则将另一个操作数也转为无符号类型。
如:int ival = 3;
double dval = 3.14159;
cout<<(ival + dval)<<endl; //输出为6.14159
unsigned int a = 6;
int b = -20;
char c;
cout<<(a + b > 6)?:(c = ‘1’):(c = ‘0’)<<endl; //输出为1
第一个是因为上面的ival在计算时提升为double类型,而第二个是因为b转换为了unsigned int类型。此时引出另一个问题,对于数据类型,当超过范围后,是从另一个范围边取相应超出的值。所以-20是4294967382。
特例:long类型和unsigned int类型,只有当机器上的long类型足以存放unsigned int类型的所有值时,unsigned int才会转换成long类型,否则都将转换成unsigned long类型。
(2)用一种类型的表达式赋值给另一种类型的表达式
在这种情况下目标转换类型是被赋值对象的类型。
如:double dval = 3.14159;
int ival = dval;
cout<<ival<<endl; //输出值为3,dval被截断
(3)把一个表达式传递给一个函数,调用表达式的类型与形参类型不同
在这种情况下,实参将被强制转换成形参类型。
(4)从一个函数返回一个表达式的类型与返回类型不同
在这种情况下,返回表达式类型自动转换成函数类型。
因此,在实际的情况下,我们需要注意算术转换问题,因为这有可能会导致精度丢失或者得到的是意向不到的结果。
2、sizeof运算
sizeof运算取得时对象在内存中(栈)所占的存储位数。对于指针,其大小为4,对于空类大小为1,含有虚函数或虚继承则为4,因为虚函数等相当于指针。静态成员存放全局数据区,sizeof不计算该部分内存。而考的最多的则是结构体和类的内存对齐问题。结构体的长度是最长数据原始的整数倍。
注:依据编译器不同,内存中的数据一般都会支持数据对齐,DWORD数据的内存一般为被4除尽,而WORD则为被2除尽。
例:struct{
short a1;
short a2;
short a3;
}A;
struct{
long a1;
short a2;
}B;
cout<<sizeof(A)<<endl; //输出为6
cout<<sizeof(B)<<endl; //输出为8
注:如果结构体种存在union,则union中取类型所占字节最长的那个用来sizeof运算。
3、sizeof与strlen的区别
两者的区别主要有一下几点:
(1)sizeof操作符的结果类型是size_t,它再头文件中的typedef为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小。但静态对象不在计算之内。
(2)sizeof是算符,strlen是函数。
(3)sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以’/0’结尾。sizeof还可以用函数做参数。
(4)数组做sizeof的参数不退化,传递给strlen就退化为指针。
(5)大部分编译程序在编译的时候就把sizeof计算过了,是类型或是变量的长度。这就是sizeof(x)可以用来定义数组维数的原因。这一点同时也决定着sizeof不能返回被动态分配的数组或外部的数组的尺寸。数组做函数参数时,传递的是数组首地址,所以sizeof计算为4.
(6)strlen的结果要再运行时才能计算出来,用来计算字符串的长度,而不是类型占内存的大小。还有就是strlen计算的字符长度并不包含’/0’,而sizeof则会把该符号一起计算在内。
[参考资料]
[1] C++ Primer Plus第五版
[2] 程序员面试宝典