数据类型概述
在计算机的内存中存放了很多的二进制数据,数据类型就是管理这些数据的含义。比如下面的内存数据,尽管内存中的数据是完全一样的,但数据类型不同,实际代表的数值含义也不同。

sizeof关键字
sizeof可以获取数据所占的空间的字节数,返回值为长整型long,单位为字节。
使用方法:
- sizeof(变量、数据类型关键字)
- sizeof 变量

各种数据类型(类型修饰符)
1、整型
整型数据有五种:char、short、int、long、long long 以及这五种对应的unsigned形式。
下面是整型的总汇表:
| 数据类型 | 名称 | 所占空间 (字节) | 占位符 | 取值范围 |
| char(signed char) | 有符号字符 | 1 | %d | -2^7-1~2^7 |
| unsigned char | 无符号字符 | 1 | %c | 0~2^8-1 |
| short(signed short) | 有符号短整型 | 2 | %d | -2^15-1~2^15 |
| unsigned short | 无符号短整型 | 2 | %u | 0~2^16-1 |
| int(signed int) | 有符号整型 | 4 | %d | -2^31-1~2^31 |
| unsigned int | 无符号整型 | 4 | %u | 0~2^32-1 |
| long(signed long) | 有符号长整型 | 4、8 | %ld | ... |
| unsigned long | 无符号长整型 | 4、8 | %lu | ... |
| long long(signed long long) | 有符号长长整型 | 8 | %ld | -2^63-1~2^63 |
| unsigned long long | 无符号长长整型 | 8 | %lu | 0~2^64-1 |
long型的大小与计算机系统的位数有关。32位系统对应的是4字节、64位系统对应的是8字节
2、字符型
字符型数据也是一种整型,它对应的类型为unsigned char。0~255中的每一个数字对应一个字符,这个字符的规定可以在ASCII码表中查询得到。
在Linux中,可以使用" man ASCII "来查询ASCII码表:

ASCII码表的字符分类:
- 通讯、控制类字符:00H~1FH,共32个,有些无法显示在屏幕上
- 符号、数字、字母:20H~7FH,共96个,全部都可以显示在屏幕上
- IBM制定的扩充字符:80H~FFH,共128个,这些是非标准的ASCII码
定义相关特点:
字符型实质是整型,对应的是ASCII码。因此下面的定义效果实际上是一样的:
char val = 'A'; //以字符赋值变量
char val = 65; //以字符对应的ASCII码赋值变量
常用字符的ASCII码:
- a~z:97~122
- A~Z:65~90
- 0~9:48~57
- 空格:32
- 换行符\n:10
常用字符转换方法:
- 大写字母与小写字母转换:±32(97-65 = 32)
- 整型数字与字符数字转换:±48(0的ASCII码为48)
3、浮点型(实型)
浮点型数据有三种:float、double、long double
下面是浮点型的总汇表:
| 数据类型 | 名称 | 所占空间 (字节) | 占位符 | 精度(小数点后几位) |
| float | 单精度 | 4 | %f | 6 |
| double | 双精度 | 8 | %lf | 15~16 |
| long double | 长精度 | >=8与编译器有关 | %Lf | >=15~16 |
3.1 浮点型的表示
浮点型可以用小数来表示,也可以使用科学计数法表示,具体定义如下:
float val = 0.08;//正常的小数表示
float val = 8e-2;//科学计数法 8*10^-2
3.2 浮点型的内存形式
浮点型的内存存放形式遵循IEEE 754标准,具体标准如下:

对于E,这个称为偏移的阶码、移码。float的偏移量为127,double的偏移量为1023
下面以9.625为例,手算float类型的内存形式,具体计算过程如下:

下面是辅助计算的网址及操作方法:
Base Convert: IEEE 754 Floating Point

3.3 浮点型的比较判断注意点
如果浮点型的小数部分的处理是循环的(就是第一步进制转换时,小数部分*2取整步骤一直乘不尽),那么float与double类型的值是不一致的,这个原因是因为在内存中的M部分保存的数值不一样,精度缺失也不一样。下面以2.2和2.25为例:


在代码中的体现如下:
- float与double变量比较

- float与常数比较(常数默认为double型,加上 f 变为float型)

因此浮点数的比较不能直接使用 == 进行比较,而是使用求差的方式进行比较,具体代码如下:

4、布尔型
布尔变量是一个逻辑值,下面是布尔变量的表:
| 数据类型 | 名称 | 所占空间(字节) | 占位符 | 取值范围 |
| _Bool | 布尔 | 1 | %d | 0、1 |
- 布尔类型只有0、1,非0数赋值给布尔变量会变为1,0赋值给布尔变量还是0。
- 所占空间为1字节,用第0位来表示0、1,其余7位不使用。
在 " stdbool.h "中,定义了布尔类型的一些宏,具体如下:
#define bool _Bool /* _Bool是关键字,bool只是别名不是关键字 */
#define true 1
#define false
下面用一个例子来验证布尔的运算,代码和运行结果如下:

5、缺省型void
void是缺省型,代表没有类型。通常用法如下:
- 用于函数前,代表没有返回值
- 用于指针类型void*,代表通用指针类型,可以指向任何类型的数据
数据类型转换
数据类型的转换有自动类型转换和强制类型转换,自动类型转换还包括赋值操作时的转换、二元运算时的转换,数据类型的转换框图如下:

1、自动类型转换
1.1 赋值操作
在赋值操作时,等号右边的变量类型会自动转变成等号左边的变量类型,当右边类型精度比左边类型精度大时,会产生损失精度的问题。
/* 赋值时,数据类型转换 */
int a;
a = 3.14;//a为int,3.14为double,赋值时3.14会转成int变成3,产生了精度损失
1.2 二元运算
二元运算就是有两个参数的运算,如加、减、乘、除等。
二元运算时自动会向精度高的类型转换,转换路径如下:

简单的验证代码如下:
/* 二元运算时的数据类型转换 */
int a = 50;
double b = a*0.3;/* a为int,0.3为double. a会先转为double再与0.3相乘 */
2、强制类型转换
强制类型转换就是在变量前写上" (变量类型) ",即可将该变量的类型进行转换。
例如在除法中,需要保留精度,具体代码如下:
/* 未强制转换 */
int a = 100;
int b = 3;
printf("%.2lf",a/b);
//a,b都为int,运算之后为33.333舍弃小数部分,即33,精度损失
/* 进行强制转换 */
int a = 100;
int b = 3;
printf("%.2lf",(double)a/b);
//a被强转为double,运算时b会自动转为double,最终结果为double型33.33,精度不丢失
数据定义时的前缀
0、概述
数据定义时,有一些前缀,比如前面的int、char这种,也有一些像const、auto这种。这些前缀分为3类:类型修饰符、类型限定符、存储类型。
类型修饰符:
类型修饰符就是定义数据类型。
如:int、char、float等
类型限定符:
类型限定符用来进一步定义变量的使用方式或属性,但不影响变量的基本类型
如:const、volatile、restrict
存储类型:
存储类型定义了变量的生命周期和作用域。
如:auto、static、extern、register
相关名词概念:
- 变量的作用域:变量能够被使用的范围;针对的是程序编译链接阶段
- 生命周期:变量创建(分配存储空间)到变量销毁 (释放存储空间)之间的时间段(即变量的存在时间);针对的是程序的执行阶段;
- 初始值:不同的存储类型,默认的初始值不同
1、const
1.1 const修饰变量
const是一个类型限定符,可以让变量变为只读常量,这样通过一般的赋值方式就不可以改变变量的值。
定义可以为:" const int a; " 或者 " int const a; "

const修饰的变量修改值,可以通过指针的方式来修改。

1.2 const修饰指针
const修饰指针,可以规定*p、p是否进行修改。
具体内容在:博文8.C基础_指针基础-优快云博客中"其他指针-const修饰的指针"中
2、auto
auto是一个存储类型,它是一种默认情况。当定义变量时什么都不写时,其实就是auto类型。
比如定义" int a "这实际上就是" auto int a "
auto说明的变量只能在某个程序范围内使用,即:auto的作用对象是局部变量,因此auto不能对全局变量进行修饰。对于auto,无初始值的情况下,它的初始值是一个不确定的数。

3、register
register是一个存储类型,它可以让局部变量不在内存中开辟空间,而是存储在寄存器中。
一般是将高频使用的局部变量加上register来提高代码运行的效率,但实际上这些工作都是编译器自动进行。
register只是程序员的主观想法,当CPU的寄存器没有足够空间时,会自动变为auto修饰。同时,过多的register也可能导致程序运行速度降低,因为占用了过多的寄存器,使得真正的运算能够使用的寄存器变少,从而导致运行速度降低。
注意:因此register修饰的变量存储在寄存器中,C中的地址指的是内存中的地址,因此register修饰的变量不能够使用指针去指向它。

4、static
static是一个存储类型,它可以将变量变为静态存储变量,用于定义全局变量或局部变量。默认的初始值为0。
对于局部变量,被static修饰后,不会在栈中开辟空间,而是存放在静态存储区,地址是固定的,因此函数退出后释放栈空间,不会影响静态局部变量。
对于全局变量或函数,被static修饰后,代表只能当前文件下使用该全局变量或函数,其他文件通过extern并不能找到该全局变量或函数。

5、extern
extern是一个存储类型,被称为外部参照引用型。当希望在一个文件去使用另一个文件定义的全局变量或函数时,需要在变量或函数前加上extern去声明。(函数前可以不加)
extern只是声明,编译时并不会分配内存,而是在链接阶段将其与实际的定义进行关联。
注意:static修饰的全局变量或函数,extern声明后并不能找到该全局变量或函数。


被折叠的 条评论
为什么被折叠?



