一、前言
C++中类型主要分为两种:基本类型、符合类型。本文要介绍的是基本类型,也就是整数和小数。
无论学习什么编程语言,数据类型都是逃不开的,但你想过为什么会有它吗?
即使是python也不例外,只不过它数据类型比较隐蔽、且处于动态变化当中。
二、数据类型到底是什么?
从本质上来说,我们在编程语言中所定义的一切变量,都是在操作内存,比如下面这句代码:
int a=10;
上面这句代码的含义就是:分配一块int
类型大小的内容,给它取了个名字叫做a
,同时让这块内存存放数字10
。
至于什么是变量,前面已经提到过了,这里也就不再赘述了,想要更深层次的理解,可以查看另一篇文章:常量与变量。
那么到底什么是内存?
这可不是你的C盘、D盘那个硬盘存储,它是一个你平时可能根本看不到的东西,但可以通过windows电脑自带的任务管理器查看:
硬盘决定了你这台电脑可以下载、保存多少资料、程序。
而内存则决定了你电脑运行起来卡不卡,内存越大,能同时装进来运行的软件就可以更多,就越不容易卡顿。
我们在编程中所使用的数据类型所分配的内存,都是分配的这个内存。
C/C++中的基本数据类型总的来说只有两种:整数、小数。
但为了更加合理的分配内存,就出现了各种占用大小不同的类型,比如整数就有:short
,int
,long long
,小数有:float
,double
。
从本质上来说,无论是整数、还是小数,它们在内存中并没有太大的区别,都是一块指定大小的内存。
比如char
类型占一个字节,long long
占8个字节,double
占8字节,字节相关内容可以查看:数据大小。
之所以不同,取决于我们人为给它赋予的意义。
即:我们规定使用int
的时候,这块内存就存放数字,使用char
的时候,这块内存就存放字符。
但无论是整数、小数、还是字符,编译完成后都是数字0和1,且都放在了内存里。
其它语言不一定可以,但在C/C++语言中,你就可以利用这个特性任意强制转换解释方式:
char c='A';
print("%d",c);
上面的代码我使用%d
输出数字的方式来打印一个字符类型,会报错吗?并不会。
因为char
本质上来说,仅仅是占据一个字节的内存块而已,内存里面只能存0和1的数字,它存储的本身就是数字,所以你会看到打印出来的是65
这个数字。
那为什么使用%c
就能打印出字符呢?
这使用了一个叫做ASCII码表的东西,具体来说,就是当你想要打印字符,它就会查这个表,找到这个数字对应的字符,然后让其显示到屏幕上。
字符又是如何显示出来的?
这是提前保存到你电脑上的像素点,打印字符本质上就是将提前保存的对应字符显示到屏幕上而已。你所看到的所有汉字,之所以能够看到,就是因为你电脑上有对应的编码转换表,如果没有,那你就看不到这些汉字了。
当你理解了这个,甚至你还能更进一步理解指针了,比如我想要分配一块1024
字节的缓存区,最常见的方式就是使用char
,因为它占据一个字节,更好计算:
char buf[1024];
但如果你非要用int
行不行?当然也行!
int buf[256];
char cbuf=(char*)buf
这两者其实就是一个意思,都是1024
个字节大小的内存,只不过我们人为解释的方式不同而已。
而强制转换就是这样一种更换解释方式的语法。
三、注意点
从上面的介绍中相信大家也已经能够感受到一些奇妙之处了。
C/C++之所以有如此多的数据类型名仅仅只是一些历史遗留问题而已,因为以前的内存很珍贵,寸土寸金,而现在内存却越发的便宜了。
非要说的话,从如今的现代编程角度来看,这些名字都是没有必要的。
因为我们总的来说,只需要四种基本数据类型其实就足够了:整数、小数、字符、布尔。
比如C/C++中,char
、short
、int
、long long
都是内存大小不同的数字。
至于float
、double
也只是内存大小不同的小数。
字符是个有趣且繁琐的东西,因为现代编程语言中基本都采用的是utf-8
编码,每个字符可就不是占用一个字节了,而是可变的。
比如英文占用1字节,中文就占用3个字节,所以这里将其归为新的类型。至于字符串则是字符的集合。
而布尔,在C语言中它其实和char
是一样的,占一个字节,只不过人为规定其不为0则为真,否则为假。
目前很火的现代语言rust
中,我们就可以看到这种显著的变化:
直接指定存储数字需要分配的大小:i8,i16,i32,i64 //后面的数字为位数,要除以8才是所占字节
如果是存放正数就是:u8,u16,u32,u64
如果为小数就是:f32,f64
如果为字符就是:char //char占四个字节,保证能表达所有类型的字符。
字符串就是:string //为utf-8编码,每个字符所占空间都不一样,比如本站点:"kucoding.com",全英文12个字节,而如果是"酷编程网",汉字三字节,四个汉字也占12字节。
布尔类型就是:bool //它只有两种取值,true与false
因此注意点就来了,一般来说,完全不同的类型之间,就最好不要进行强制转换,否则很容易出现错误。
比如小数与整数,虽然都占相应的空间,但其底层存储、解释方式是不同的,一般不建议强转。
占位小的类型尽量不要转到占位大的类型。
比如char
在C/C++中只占一个字节,你却要强制转成4字节的int
来用,那么多出来的3个字节上面的数据对于我们来说就是未知的,后面使用它很容易出现问题。
而大转小就不存在这种问题了,但也要小心数据截断,比如int
占4
字节,存255
之内的数字强制转换为char
自然没事,因为这些数字本身就只需要一个字节就能存。
可如果超过了255
,那么就会造成超出255
的数字被遗留在了高字节上,你拿到的数字只是最后低位上的一个字节大小的余数。
四、常使用点
理解数据类型本质上只是分配大小不同的内存了之后,你可能写起代码来就会更加随心所欲了。
比如一个函数需要一个数字,但这个数字肯定在255
之内,那你就用一个char
类型,而不是int
类型。
判断一条语句是否为真的时候,你也没必要非要用bool
类型,因为在C/C++中判断一个语句是否为真的条件是,它是不是不等于0
。
因此以下写法是完全等价的:
int a=10;
if(a!=0){}
if(a){}
还有很多时候,有些回调函数会让你传入指针,方便函数内部使用。
比如创建线程时,需要一个回调函数,同时你可以在创建线程的时候传递一个函数指针交给回调函数使用。
但有时候我就只想传入一个数字而已,用指针也太麻烦了吧?
但指针本质上也就是一个存储内存地址的内存而已,套了个娃。
所以你依旧可以强制转换:
void fun(void* p){
int n=(int)p;
}
fun((void*)100)
先将数字100强制转换为指针,然后进入函数后,再强制转换回来即可。
但需要注意的是,指针所占用的内存大小时固定的,并且取决于当前你项目是32位还是64位的程序,指针占用内存大小与你项目位数是相同的。
但这基本仅限于C/C++语言,因为这种写法虽然写起来很爽,但如果看起来会很迷糊。
所以在现代编程语言中,都会尽量制止这种随意强制装换的写法。
五、无符号类型
C/C++编程中我们可能常常能看到unsigned
关键字,它一般是加在其它类型前面的,比如:
unsigned int a;
unsigned char c;
它代表的就是无符号,说白了就是只能存放整数。
比如char
占8个字节,按理来说可以存放0-255
中任意的数字,但由于char
本身为有符号的类型,最高位用作了判断这个数为正数还是负数,所以实际上只有7位用于存数,即只能表示0-127
这128
个数字。
但由于最高位有了正负号,所以其实质上可以表示的是-128到127
这个范围的数字。
其它类型也都类似,其实也仅仅只是更换了一个解释方法而已,内存还是那块内存。
不过用无符号类型的时候就需要注意,由于它不能表示负数,所以一旦使用减法,试图让它小于0时,它就会溢出变成一个很大的数字:
unsigned int a=0;
a-=1; //此时a不是-1,会变成一个很大的数字
六、sizeof
前面一直谈论数据类型的大小,C/C++中其实有一个运算符可以获取到这些类型的大小:sizeof
。
其用法非常简单:
#include<iostream>
using namespace std;
int main() {
cout << sizeof(int) << endl;
char c;
cout << sizeof(c);
}
它既可以直接获取类型的大小,也可以获取变量的大小,单位是字节:
但要注意,它是运算符,不是函数,它是在编译期间就完成了获取大小的工作,如果是无法获取动态分配内存大小的。