目录
- 1. 数据类型的介绍
- 2. 计算类型长度的操作符:sizeof
- 3. 数据类型长度
- 4. signed 和 unsigned
- 5. 数据类型的取值范围
- 6. 变量
- 7. 算术操作符:+、-、*、/、%
- 8. 赋值操作符:= 和复合赋值
- 9. 单目操作符:++、–、+、-
1. 数据类型的介绍
C 语言提供了丰富的数据类型来描述生活中的各种数据。使用整型类型来描述整数,使用字符类型来描述字符,使用浮点型类型来描述小数。正所谓"类型",就是相似的数据所拥有的共同特征,编译器只有知道了数据的类型,才知道怎么操作数据。下面开始介绍 C 语言提供的各种类型,本章节主要探讨内置数据类型。
上面这张思维导图展示了 C 语言的数据类型整体分类,分为内置类型和自定义类型两大分支。内置类型是语言自带的基础类型,包含字符型、整型、浮点型、布尔类型;自定义类型由程序员自己创建,包含数组、结构体 struct、枚举 enum、联合体 union。
- 整型
//整数 - integer//短整型short[int][signed]short[int]unsignedshort[int]//整型int[signed]intunsignedint//长整型long[int][signed]long[int]unsignedlong[int]//更长的整型//C99中引入longlong[int][signed]longlong[int]unsignedlonglong[int]- 字符型
char//character[signed]char//有符号的unsignedchar//无符号的- 浮点型
floatdoublelongdouble- 布尔型
C 语言原来并没有为布尔值单独设置一个类型,而是使用整数 0 表示假,非零值表示真。在 C99 中也引入了布尔类型,是专门表示真假的。
_Bool布尔类型的使用得包含头文件<stdbool.h>,布尔类型变量的取值是:true或者false。
#include<stdbool.h>intmain(){_Booln=true;//=后面只能是两个值true/falseif(n)printf("I like c\n");//true表示的是真可以打印出来return0;}每一种数据类型都有自己的长度,使用不同的数据类型,能够创建出长度不同的变量,变量长度的不同,存储的数据范围就有所差异。说到了计算长度,那么有没有计算各种类型长度的操作符呢?
2. 计算类型长度的操作符:sizeof
sizeof是一个关键字,也是操作符,专门是用来计算操作符数的类型长度的,单位是字节。sizeof操作符的操作数可以是类型,也可以是变量或者表达式。
sizeof(类型)sizeof表达式sizeof的操作数如果不是类型,是表达式的时候,可以省略掉后边的括号。sizeof后边的表达式是不真实参与运算的,类型根据表达式的类型来得出大小。sizeof的计算结果是size_t类型的。
3. 数据类型长度
//sizeof 计算的结果应该使用%zd来打印intmain(){printf("%zd\n",sizeof(char));//打印的结果是1字节printf("%zd\n",sizeof(int));//打印的结果是4字节printf("%zd\n",sizeof(short));//打印的结果是2字节printf("%zd\n",sizeof(long));//打印的结果是4字节printf("%zd\n",sizeof(longlong));//打印的结果是8字节printf("%zd\n",sizeof(float));//打印的结果是4字节printf("%zd\n",sizeof(double));//打印的结果是8字节printf("%zd\n",sizeof(longdouble));//打印的结果是8字节也有可能是16字节,sizeof(long double) >= sizeof(double)return0;}//单位是字节,c语言标准:sizeof(long)>= sizeof(int)注:sizeof在表达式中不计算,因为在代码进行编译的时候,就根据表达式的类型确定了,而表达式的执行却要在程序运行期间才能执行,在编译期间已经将sizeof处理掉了,所以在运行期间就不会执行表达式了。
4. signed 和 unsigned
C 语言使用signed和unsigned关键字修饰字符型和整型类型的。
signed关键字,表示一个类型带有正负号,包含负值;unsigned关键字,表示该类型不带有正负号,只能表示零和正整数。对于int类型,默认是带有正负号的,也就是说int等同于signed int。由于这是默认情况,关键字signed一般都省略不写,但是写了也不算错。
signedinta;// 等同于int a;int 类型也可以不带正负号,只表示非负整数。这时就必须使用关键字unsigned声明变量。
unsignedinta;整数变量声明为unsigned的好处是,同样长度的内存能够表示的最大整数值,增大了一倍。比如,16 位的signed short int的取值范围是:-32768 ~ 32767,最大是 32767;而unsigned short int的取值范围是:0 ~ 65535,最大值增大到了 65,535。32 位的signed int的取值范围可以参看limits.h中给出的定义。
unsigned int里面的int可以省略,所以上面的变量声明也可以写成下面这样。
unsigneda;字符类型char也可以设置signed和unsigned。
signedchara;unsignedchara;注意:C 语言规定char类型默认是否带有正负号,由当前系统决定。这就是说,char不等同于signed char,它有可能是signed char,也有可能是unsigned char。这一点与int不同,int就是等同于signed int。
5. 数据类型的取值范围
上述的数据类型很多,尤其整型类型就有short、int、long、long long四种,为什么呢?
其实每一种数据类型有自己的取值范围,也就是存储的数值的最大值和最小值的区间,有了丰富的类型,我们就可以在适当的场景下去选择适合的类型。如果要查看当前系统上不同数据类型的极限值:limits.h文件中说明了整型类型的取值范围,float.h这个头文件中说明浮点型类型的取值范围。为了代码的可移植性,需要知道某种整数类型的极限值时,应该尽量使用这些常量。
SCHAR_MIN,SCHAR_MAX:signed char 的最小值和最大值。SHRT_MIN,SHRT_MAX:short 的最小值和最大值。INT_MIN,INT_MAX:int 的最小值和最大值。LONG_MIN,LONG_MAX:long 的最小值和最大值。LLONG_MIN,LLONG_MAX:long long 的最小值和最大值。UCHAR_MAX:unsigned char 的最大值。USHRT_MAX:unsigned short 的最大值。UINT_MAX:unsigned int 的最大值。ULONG_MAX:unsigned long 的最大值。ULLONG_MAX:unsigned long long 的最大值。
6. 变量
6.1 变量的创建
了解清楚了类型,我们使用类型做什么呢?类型是用来创建变量的。什么是变量呢?
C 语言中把经常变化的值称为变量,不变的值称为常量。
变量创建的语法形式是这样的:
data_type name;||||数据类型 变量名intage;//整型变量charch;//字符变量doubleweight;//浮点型变量变量在创建的时候就给一个初始值,就叫初始化。
intage=18;charch='w';doubleweight=48.0;unsignedintheight=100;6.2 变量的分类
全局变量:在大括号外部定义的变量就是全局变量,全局变量的使用范围更广,整个工程中想使用,都是有办法使用的。局部变量:在大括号内部定义的变量就是局部变量,局部变量的使用范围仅限于其所在的代码块。
#include<stdio.h>inta=2023;//全局变量intmain(){intb=2018;//局部变量printf("%d\n",a);printf("%d\n",b);return0;}如果局部和全局变量,名字相同,局部变量优先使用,如下:
#include<stdio.h>intn=1000;intmain(){intn=10;printf("%d\n",n);//打印的结果是多少呢?return0;}全局变量和局部变量在内存中存储在哪里呢?
一般我们在学习 C/C++ 语言的时候,我们会关注内存中的三个区域:栈区、堆区、静态区。
- 局部变量是放在内存的栈区
- 全局变量是放在内存的静态区
- 堆区是用来动态内存管理的
7. 算术操作符:+、-、*、/、%
在写代码时候,一定会涉及到计算。C 语言中为了方便运算,提供了一系列操作符,其中有一组操作符叫:算术操作符。分别是:+、-、*、/、%,这些操作符都是双目操作符。
注意:操作符也被叫做运算符,是不同的翻译,意思是一样的。
7.1 + 和 -
+和-用来完成加法和减法的,都是有两个操作数,位于操作符两端的就是它们的操作数,这种操作符也叫双目操作符。
/*加法*/intmain(){inta=10;intb=20;printf("%d\n",a+b);return0;}/*减法*/intmain(){inta=10;intb=20;printf("%d\n",b-a);return0;}7.2 * 和 /
运算符*用来完成乘法,运算符/用来完成除法。除号的两端如果是整数,执行的是整数除法,得到的结果也是整数。
/* 乘法 */intmain(){intnum=15;printf("%d\n",num*4);return0;}/* 除法:除号的两端如果是整数,计算的是整数的除法,不会得到小数,得到的是整除后的商 */intmain(){printf("%d\n",8/2);printf("%d\n",6/4);//进行的是整数除法,得到的结果是 1(商),如果想得到1.5就必须要有一个小数,而且要用%f来打印如下printf("%f\n",6.0/4);//打印的是1.5,%f是用来打印小数的,这种打印的方式,小数点后默认是6位小数的return0;}//除号两端有浮点数计算的是小数除法上面示例中,尽管变量 x 的类型是float(浮点数),但是6 / 4得到的结果是1.0,而不是1.5。原因就在于 C 语言里面的整数除法是整除,只会返回整数部分,丢弃小数部分。如果希望得到浮点数的结果,两个运算数必须至少有一个浮点数,这时 C 语言就会进行浮点数除法。
7.3 %
运算符%表示求模(余)运算,即返回两个整数相除的余值。这个运算符只能用于整数,不能用于浮点数。
/* % --取模(余)运算符,获得整除后的余数 */intmain(){printf("%d\n",10%3);//10/3=3...1return0;}负数求模的规则是,结果的正负号由第一个运算数的正负号决定。
#include<stdio.h>intmain(){printf("%d\n",11%-5);// 1printf("%d\n",-11%-5);// -1printf("%d\n",-11%5);// -1return0;}8. 赋值操作符:= 和复合赋值
在变量创建的时候给一个初始值叫初始化,在变量创建好后,再给一个值,这叫赋值。
/*赋值操作符 = 是一个随时可以给变量赋值的操作符*/inta=100;//初始化a=200;//赋值,这里使用的就是赋值操作符8.1 连续赋值
赋值操作符也可以连续赋值,如:
inta=3;intb=5;intc=0;c=b=a+3;//连续赋值,从右向左依次赋值建议:C 语言虽然支持这种连续赋值,但是写出的代码不容易理解,建议还是拆开来写,这样方便观察代码的执行细节。
inta=3;intb=5;intc=0;b=a+3;c=b;//这样写,在调试的时候,每一次赋值的细节可以很方便地观察/* 赋值和连续赋值 */intmain(){inta=0;//初始化a=10;//赋值,c语言里是从右向左intb=3;intc=5;c=b=a+3;//连续赋值,从右向左依次赋值c=(b=a+3);//跟上面的一样的b=a+3;c=b;//拆开写便于观察运算过程,比较推荐printf("c = %d b = %d\n",c,b);return0;}8.2 复合赋值符
在写代码时,我们经常可能对一个数进行自增、自减的操作,如下代码:
inta=10;a=a+3;a=a-2;这样代码 C 语言给提供了更加方便的写法:
inta=10;a+=3;a-=2;C 语言中提供了复合赋值符,方便我们编写代码,这些赋值符有:
1+=-=2*=/=%=3//下面的操作符后期讲解4>>=<<=5&=|=^=赋值的完整代码例子:
/* 复合赋值 */intmain(){inta=3;//a = a + 5;//a += 5;//这种写法跟上面一样,这是复合赋值 +=//a = a - 5;//a -= 5;//*= /= %=printf("%d\n",a);return0;}9. 单目操作符:++、- -、+、-
前面介绍的操作符都是双目操作符,有 2 个操作数的。C 语言中还有一些操作符只有一个操作数,被称为单目操作符。++、--、+(正)、-(负)就是单目操作符。
9.1 ++ 和 - -
++是一种自增的操作符,又分为前置++和后置++;- -是一种自减的操作符,也分为前置- -和后置- -。
在介绍之前,看一下完整且正确的代码例子:
/* 单目操作符++ */intmain(){inta=5;//a = a + 1;//a += 1;复合赋值//++:自增1操作符//a++; //后置++++a;//前置++printf("%d\n",a);return0;}9.1.1 前置 ++
inta=10;intb=++a;printf("a = %d b = %d\n",a,b);计算口诀:先加一,后使用。
a 原来是 10,先 +1,a 变成 11,再使用就是赋值给 b,b 得到的也是 11,所以计算结束后,a 和 b 都是 11,相当于这样的代码:
inta=10;a=a+1;b=a;printf("a = %d b = %d\n",a,b);9.1.2 后置 ++
inta=10;intb=a++;//++的操作数是a,是放在a的后面的,就是后置++printf("a = %d b = %d\n",a,b);计算口诀:先使用,后加 1。
a 原来是 10,先使用,就是先赋值给 b,b 得到了 10,然后再 +1,a 就变成了 11,所以结束后 a 是 11,b 是 10,相当于这样的代码:
inta=10;intb=a;a=a+1;printf("a = %d b = %d\n",a,b);完整代码:前置 ++ 和 后置 ++ 的区别
/* 前置++和后置++的区别 */intmain(){inta=5;//int b = a++;//后置++:先使用,后加1 打印结果是6 5//b = a, a = a + 1intb=++a;//前置++:先加1,后使用 打印结果是6 6//a = a + 1, b = aprintf("a = %d b = %d\n",a,b);return0;}9.1.3 前置 –
前置--和上面的前置++用法一样,只是++是加 1,而--是减 1。
计算口诀:先减 1,后使用。
inta=10;intb=--a;//--的操作数是a,是放在a的前面的,就是前置--printf("a=%d b=%d\n",a,b);//输出的结果是:9 99.1.4 后置 –
后置--类似于后置++,只是把加 1 换成了减 1。
计算口诀:先使用,后减 1。
inta=10;intb=a--;//--的操作数是a,是放在a的后面的,就是后置--printf("a=%d b=%d\n",a,b);//输出的结果是:9 109.2 + 和 -
这里的+是正号,-是负号,都是单目操作符。运算符+对正负值没有影响,是一个完全可以省略的运算符,但是写了也不会报错。
inta=+10;//等价于 int a = 10;运算符-用来改变一个值的正负号,负数的前面加上-就会得到正数,正数的前面加上-会得到负数。
inta=10;intb=-a;intc=-10;printf("b=%d c=%d\n",b,c);//这里的b和c都是-10inta=-10;intb=-a;printf("b=%d\n",b);//这里的b是1010. 总结
到这里,C 语言数据类型与变量的基础内容就梳理完了。回顾一下这条主线:
- 数据类型决定了数据在内存中的存储方式与占用空间,内置类型包括整型、字符型、浮点型和布尔型,自定义类型则有数组、结构体、枚举和联合体。
sizeof是计算类型或表达式长度的关键字,结果类型为size_t,打印时推荐使用%zd。signed与unsigned用来修饰字符型和整型,前者带正负号,后者只能表示零和正整数,同样长度的内存下unsigned能表示的最大值翻倍。- 取值范围可以通过
limits.h和float.h中的常量来查询,写可移植代码时应尽量使用这些常量而不是硬编码数字。 - 变量是类型的具体落地,分为全局变量(静态区)和局部变量(栈区),同名时局部变量优先。
- 操作符方面,算术操作符
+ - * / %要注意整数除法与取模的规则;赋值操作符支持连续赋值与复合赋值;单目操作符++ --则要分清前置与后置的"先加后用"与"先用后加"。
这些内容看似零散,实际上环环相扣:类型决定长度,长度决定取值范围,取值范围又影响我们在实际场景中的类型选择。建议在学完每一节后都动手把示例代码敲一遍,尤其是sizeof的打印结果和前置/后置++的对比,亲手跑一遍比看十遍更有效。
基础打牢之后,就可以继续往下学习分支与循环语句、函数、数组等内容了。数据类型与变量是后续所有语法的地基,值得多花一点时间反复练习。