对于字符型,整型,浮点型,如果只用int double等关键字就能表示其数据类型,那么它们被统称为基本数据类型。
目录
基础数据类型
基本数据类型 | ||
整数类数据类型 | 枚举类型 | enum… 型 |
字符型 | char 型 signed char 型 unsigned char 型 | |
整型 | signed short int 型 unsigned short int 型 signed int 型 unsigned int 型 signed long int 型 unsigned long int 型 | |
浮点型 | float 型 double 型 long double型 |
整型
整型
包括有符号整型(signed int)和无符号整型(unsigned int),若在声明变量时若不加上类型说明符,即单输入int ,则默认为有符号整型,如:
int a;
signed int b;
unsigned int c;
//前两者表示的类型均为有符号类型,而c表示为无符号整型
int 类型与 unsigned类型虽然大小均为4,但实际上unsigned int属于较大的数据类型 (int类型最高位为符号位,而unsigned int类型没有符号位,unsigned int比int多一个bit位(1/8个字节),因此unsigned int 能储存的信息更多)。
int类型又分为short int、int、long int(在C99标准还定义了更大的long long类型),顾名思义,这些符号代表的含义分别为短整型,整型,长整型。当然这些数据类型都有对应的signed版和unsigned版。
与int类型不同的是,char类型同时存在有既不带signed又不带unsigned的单独一个"char"型。
除了signed可以省略外,如果单独的一个short或者long,其后默认跟的是int类型,如:
那么我们知道了这么多的类型,它们的范围又是多少呢?——我们可以在中找到它们的定义。
以下是在C++官网上查到的数据:C++官网中文版
而如果在编译器上打印这些常量就可以得到范围:
#include <stdio.h> #include <limits.h>//为了打印这些宏常量需要引入的头文件 #include <stdint.h>
#include <stdio.h>
#include <limits.h>//为了打印这些宏常量需要引入的头文件
#include <stdint.h>
int main(void)
{
printf("CHAR_MIN = %+d\n", CHAR_MIN);
printf("CHAR_MAX = %+d\n", CHAR_MAX);
printf("SCHAR_MIN = %+d\n", SCHAR_MIN);
printf("SCHAR_MAX = %+d\n", SCHAR_MAX);
printf("UCHAR_MAX = %u\n\n", UCHAR_MAX);
printf("SHRT_MIN = %+d\n", SHRT_MIN);
printf("SHRT_MAX = %+d\n", SHRT_MAX);
printf("USHRT_MAX = %u\n\n", USHRT_MAX);
printf("INT_MIN = %+d\n", INT_MIN);
printf("INT_MAX = %+d\n", INT_MAX);
printf("UINT_MAX = %u\n\n", UINT_MAX);
printf("LONG_MIN = %+ld\n", LONG_MIN);
printf("LONG_MAX = %+ld\n", LONG_MAX);
printf("ULONG_MAX = %lu\n\n", ULONG_MAX);
printf("LLONG_MIN = %+lld\n", LLONG_MIN);
printf("LLONG_MAX = %+lld\n", LLONG_MAX);
printf("ULLONG_MAX = %llu\n\n", ULLONG_MAX);
return 0;
}
上图可以看到我们每一个数据类型的具体范围
注:无符号整型的最小值是0,没有定义宏
整型常量
一般情况下,如果定义的常量不加以说明,都会被认为是十进制常量,如果要定义为八进制常量则要在开头+上一个0以表示八进制常量,十六进制则需要在开头添加0x或0X,例如:
#include <stdio.h>
#define A 10
#define B 010
#define C 0x10
int main()
{
printf("A=%d\n", A);//10
printf("B=%d\n", B);//8
printf("C=%d\n", C);//16
return 0;
}
在定义常量时,整型常量的后面附带有U或者L,被称为整型后缀。u和U用来表示该常量为无符号类型,l和L用来表示该常量为long类型。例如: 1234U为unsigned型,234567876543L为long型
整型常量的数据类型由三个因素决定:
- 该整型常量的值
- 该整型常量的后缀
- 所用编译器的各数据类型所表示的范围
举例:(例子均为十进制)
10 可以用int表示,数据类型为int型
2147483648 不可以用int表示,可以用long表示,则数据类型为long型
2147483648U 无符号整型需要用unsigned表示,因此为unsigned long型
如果在某些编译器下2147483648可以用int表示,则2147483648在那些编译器下为int型
注意:即使两个int型变量均在有效范围内,这两个变量相加后如果超出了int型的最大范围,就造成了数据溢出,程序也会抛异常。某些编译器可能会直接终端程序的进行。
unsigned型的运算不会造成数据溢出,当运算结果超出最大值时,结果为“数学运算结果%(unsigned能表示的最大值+1)”.
字符型
char型是用来保存“字符”的数据类型,如果char型没有声明是signed型还是unsigned型,那么其默认类型由编译器决定。如上图所示,在VS2019环境下 CHAR_MIN == -128,也就意味着其默认为有符号类型。因此在VS2019环境下 signed char型等同于char型。
当然,对于字符常量,其存储的是ASCII码值的大小,其本质仍为整数,因此字符常量为int型,而非char型。(转义字符也视为一个字符常量)
一个char型占用内存为8个bit位(1个字节)因此不管编译器定义的char型默认为有符号还是无符号,其范围都包含了0~127(即包含了所有ASCII码值),因此无论怎样定义char都能放得下。
在一般情况下,char 与unsigned char并无区别,当然如果把unsigned char 强行转换成char类型,也可能造成一些意外。对于unsigned char类型有时需留意一下。
浮点型
浮点型用来表示带有小数部分的实数。共有三种浮点型:
float---------------------------精度最低 |
double------------------------精度较高 |
long double------------------精度最高 |
尾数越多(即小数点后位数越多)精度要求越高。
三种浮点型所能表示的数值范围大小关系为:
float
浮点型常量类似于整型常量,后缀为f或F表示float型,后缀为l或L表示long double型,若不加后缀默认为double型。
利用浮点数和指数还可以表示为科学计数法,例如:
- 4.56 × 10^3 就可以表示为:4.56e3
- 12.34 × 10^(-4)就可以表示为:12.34e-4
如果小数点前的数字或后的数字为0则可以省略,但不能同时省略,例如:
.5 ------------0.5、12. -----------12.0
注意:浮点型的精度是存在误差的,因为计算机将浮点数转换为二进制时浮点数的某一位可能丢失。
sizeof操作符
在了解了这些基本数据类型后,如果要获取它的长度,可以使用sizeof操作符
C语言定义char类型的长度为1,对于不同的数据类型,都可以用sizeof获取其长度。对于各种数据类型,均有有符号版和无符号版,长度也相同。另外,short int long的长度符合以下关系:
sizeof(short) <= sizeof(int) <= sizeof(long)
int main()
{
printf("short = %zu\n", sizeof(short));
printf("int = %zu\n", sizeof(int));
printf("long = %zu\n", sizeof(long));
//对于sizeof返回值的打印格式,应为%zu。
return 0;
}
根据编译器的不同,其结果也不尽相同,VS2019环境下,长度分别为2,2,4
而在其他编译器下,也可能三者的长度均相同
sizeof的返回值为中定义的size_t类型,如果在该头文件中查找会发现这么一句代码:
typedef unsigned int size_t;
也就是说size_t其实就是unsigned int 类型,只不过起了个别名而已。
sizeof除了用来计算不同数据类型的长度,还可以用来计算数组的元素个数;
数组元素个数 = sizeof(数组名) / sizeof(数组中某个元素)
运算
同数据类型
对于同数据类型之间的运算,要考虑运算符的优先级与结核性。
具体可以查看运算符表:
优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
1 | [] | 数组下标 | 数组名[常量表达式] | 左到右 | |
() | 圆括号 | (表达式)/函数名(形参表) | |||
. | 成员选择(对象) | 对象.成员名 | |||
-> | 成员选择(指针) | 对象指针->成员名 | |||
2 | - | 负号运算符 | -常量 | 右到左 | 单目运算符 |
(type) | 强制类型转换 | (数据类型)表达式 | |||
++ | 自增运算符 | ++变量名 | 单目运算符 | ||
-- | 自减运算符 | --变量名 | 单目运算符 | ||
* | 取值运算符 | *指针变量 | 单目运算符 | ||
& | 取地址运算符 | &变量名 | 单目运算符 | ||
! | 逻辑非运算符 | !表达式 | 单目运算符 | ||
~ | 按位取反运算符 | ~表达式 | 单目运算符 | ||
sizeof | 长度运算符 | sizeof(表达式) | |||
3 | / | 除 | 表达式/表达式 | 左到右 | 双目运算符 |
* | 乘 | 表达式*表达式 | 双目运算符 | ||
% | 余数(取模) | 整型表达式%整型表达式 | 双目运算符 | ||
4 | + | 加 | 表达式+表达式 | 左到右 | 双目运算符 |
- | 减 | 表达式-表达式 | 双目运算符 | ||
5 | 左移 | 变量 | 左到右 | 双目运算符 | |
>> | 右移 | 变量>>表达式 | 双目运算符 | ||
6 | > | 大于 | 表达式>表达式 | 左到右 | 双目运算符 |
>= | 大于等于 | 表达式>=表达式 | 双目运算符 | ||
小于 | 表达式 | 双目运算符 | |||
小于等于 | 表达式 | 双目运算符 | |||
7 | == | 等于 | 表达式==表达式 | 左到右 | 双目运算符 |
!= | 不等于 | 表达式!= 表达式 | 双目运算符 | ||
8 | & | 按位与 | 表达式&表达式 | 左到右 | 双目运算符 |
9 | ^ | 按位异或 | 表达式^表达式 | 左到右 | 双目运算符 |
10 | | | 按位或 | 表达式|表达式 | 左到右 | 双目运算符 |
11 | && | 逻辑与 | 表达式&&表达式 | 左到右 | 双目运算符 |
12 | || | 逻辑或 | 表达式||表达式 | 左到右 | 双目运算符 |
13 | ?: | 条件运算符 | 表达式1? 表达式2: 表达式3 | 右到左 | 三目运算符 |
14 | = | 赋值运算符 | 变量=表达式 | 右到左 | |
/= | 除后赋值 | 变量/=表达式 | |||
*= | 乘后赋值 | 变量*=表达式 | |||
%= | 取模后赋值 | 变量%=表达式 | |||
+= | 加后赋值 | 变量+=表达式 | |||
-= | 减后赋值 | 变量-=表达式 | |||
左移后赋值 | 变量 | ||||
>>= | 右移后赋值 | 变量>>=表达式 | |||
&= | 按位与后赋值 | 变量&=表达式 | |||
^= | 按位异或后赋值 | 变量^=表达式 | |||
|= | 按位或后赋值 | 变量|=表达式 | |||
15 | , | 逗号运算符 | 表达式,表达式,… | 左到右 | 从左向右顺序运算 |
说明:
同一优先级的运算符,运算次序由结合方向所决定。
简单记就是:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符
——来源:360百科
值得注意的是:赋值运算符与复合赋值运算符的优先级非常低,优先级仅高于逗号表达式
不同数据类型
对于不同数据类型的运算,首先需要进行类型转换。
类型转换的第一条规则是整型提升:对于所有的整型和字符型,(不包括long型)如果用int型就可以表示出所有的原始数据类型的所有数值,就将值转换成为int型,否则会被转换成unsigned int型。
整型提升只用来优化计算,不会改变符号和数值。
除了整型提升以外,普通算术类型转换也需要掌握。简单来说就是两个不同数据类型的运算,小的数据类型会转换成大的再进行运算,当然,这只是一般情况,对于某些特殊情况如:
long型与unsigned型的运算:如果long型可以表示unsigned型所有的值,那么转换成long型运算,否则两个操作数均转为unsigned long型。