#1. 引言
数据类型是程序的基础,决定了编译器以及运行时的各项操作解释。在编译时不同的变量应该分配多大的空间?在运行时对指针进行+1操作,指针应该跳几个字节?在类型明确的情况下,上述操作都有据可循,不会有歧义。C / C++ / JAVA / C#等语言都明确定义了数据类型。我个人也是比较偏向于喜欢使用该类编程语言。
本章主要讲述C++的自定义的内置类型,同时对类型描述const用法做一些总结。
#2. 原始内置类型 原始内置类型,书中原文为Primitive Built-in Types,具体又分为两种 arithmetic types & void arithmetic types : 算术类型 bool, char,int, short, long,float,double等类型 void: 理解为空类型,对于没有返回的函数,常常将返回值写为void.
对于char, short, int,long,long long类型,都是带符号的整数类型数据,只是可以表示的数据范围不同,对应的数据长度也不同。下面是我在64为服务器上运行的测试程序,可以看到各类型数据的长度以及可以表示的数据范围也不一样。我编写了一个很简单的测试程序,测试不同数据类型的数据长度以及对于整数,可以表示的数据范围。
void typeSizeTest()
{
std::cout << "char size is: " << sizeof(char) << std::endl;
std::cout << "int size is: " << sizeof(int) << std::endl;
std::cout << "short size is: " << sizeof(short) << std::endl;
std::cout << "long int size is: " << sizeof(long int) << std::endl;
std::cout << "long size is: " << sizeof(long) << std::endl;
std::cout << "long long size is: " << sizeof(long long) << std::endl;
std::cout << "float size is: " << sizeof(float) << std::endl;
std::cout << "double size is: " << sizeof(double) << std::endl;
char maxChar = 0x7F, minChar = 0x80;
printf("max char: %d, min Char: %d\n", maxChar, minChar);
short maxShort = 0x7FFF, minShort = 0x8000;
std::cout << "max Short: " << maxShort << " min Short: " << minShort << std::endl;
int maxInt = 0x7FFFFFFF, minInt = 0x80000000;
std::cout << "max Int: " << maxInt << " min Int: " << minInt << std::endl;
long long maxLongLong = 0x7FFFFFFFFFFFFFFF, minLongLong = 0x8000000000000000;
std::cout << "max LongLong: " << maxLongLong << " min LongLong: " << minLongLong << std::endl;
}
char size is: 1
int size is: 4
short size is: 2
long int size is: 4
long size is: 4
long long size is: 8
float size is: 4
double size is: 8
max char: 127, min Char: -128
max Short: 32767 min Short: -32768
max Int: 2147483647 min Int: -2147483648
max LongLong: 9223372036854775807 min LongLong: -9223372036854775808
最大值符号位为0,其他全1
最小值符号位为1,其他全0
对于有符号的整数类型表达的范围,此处csapp书中给出了非常好的解释。 在40页中有一个公式描述了补码下的数据表示公式。
可以看出,如果是正数,那么w-1位为0,其他位全1即可。 如果是负数想要最小,w-1位为1, 其他位为0,就可以对于的数值最小。 通过这个公式看简介明了。
float和double类型对应的数据位数以及精度不一样。在我的测试机器中,float占据4字节,double占据8字节。阮一峰在他的博客中对浮点数在计算机中的存储方式做了一个非常详细的说明,简单易懂,链接如下。 http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html
在C++中需要注意类型转换的问题,其中可能会导致一些数据精度的损失以及可表示范围的变化。根据业务判断相关变量的可能范围,选择合适的类型。
3.const的使用
const是一个限定符,经常用来描述不希望被改变的变量。主要有如下几种用法:
3.1 用来修饰一个普通对象
这里用来修饰一个普通变量,比如数组长度,任务数量等等,该变量一旦初始化后就不能改变。如下代码就是一个示例。
const int bufSize = 256;
int buf[bufSize];
for (int k = 0; k < bufSize; k++)
{
buf[k] = k;
}
默认情况下,const对象的作用域在文件内,如果需要跨文件使用,通过 extern关键字进行描述,达到跨文件使用的效果。
3.2 用来修饰一个引用
3.2.1 定义一个常量引用去引用一个常量
这种用法比较符合常规思维,引用的类型和要引用的对象类型要匹配
const int a = 5;
const int &b = a;
3.2.2 定义一个常量引用去引用一个普通变量
int c = 5;
const int &d = c;
用一个常量引用去引用一个普通变量是可以的,但是我们无法通过这个引用去修改这个变量。
3.3 用来修饰指针
3.3.1 指针地址不可改变
double a = 3.14;
double * const b = &a;
*b = 5;
该代码说明指针b一直指向a,不可以重新指向其他对象。但可以通过指针修改a的内容
3.3.2 指针指向的内容不可改变
const double a = 3.14;
const double *b = &a;
const double c = 5;
b = &c;
这里指针指向谁可以修改,但是你不可以修改指针指向对象的内容。
3.3.3 指针地址不可改,指向的对象不可改
const double a = 3.14;
const double* const b = &a;
这种情况下既不可以让b指向其他对象,也不可以通过b修改a的内容。双重约束。
附录: 错误分析
const int c = 5;
int& d = c * 2;
错误信息:
initial value of reference to none const must be an lvalue
这里涉及到lvalue(左值), rvalue(右值)的问题,先搞明白这个概念再分析上面这个错误。每一个C++表达式,要么是左值,要么是右值,左值可以理解为有名字的对象,比如说我们自己定义的变量;右值可以理解为是临时对象; 那么上面例子中c*2其实结果只是一个临时变量,是右值。但是编译器认为非常量引用要对应一个左值。匹配不上,所以报错。
可以进一步解释,上述代码可以这么写:
const int c = 5;
int temp = c * 2;
int& d = temp;
```
temp只是一个临时变量,如果d不是常量引用,说明我们可以通过d去修改临时变量?
这种行为在C++中是非法的。
```
// 下面是MSDN关于左值右值的示例。
int main()
{
int i, j, *p; //属于定义好的变量,有名称,为左值;
// 正确, i 为左值.
i = 7;
//错误: The left operand must be an lvalue (C2106).
7 = i; // C2106
j * 4 = 7; // C2106
// 正确: the dereferenced pointer is an lvalue.
*p = i;
const int ci = 7; //正确
// 错误: the variable is a non-modifiable lvalue (C3892).
ci = 9; // C3892
// 正确: the conditional operator returns an lvalue.
((i < 3) ? i : j) = 7;
}
```
[MSDN lvalue, rvalue解释](https://msdn.microsoft.com/en-us/library/f90831hc.aspx)