一.数据类型的介绍
1.数据类型的背景
正如大家所熟知的,编程是用来解决生活中实际问题的。在日常生活中,有许多种类的数据。比如一个常见的实际问题:高三18班期末考试需要统计分数,那么其中就有许多变量:姓名,分数,科目名,排名等等······计算机语言为了解释这类型的问题,便创造出了数据类型,用于创建不同类型的变量从而接收这些不同种类的数值。
像上述例子中的姓名和科目名就是一种字符的数据变量,用char可以定义字符型的变量,例如:王华,李明 或者 语文,英语等;分数是浮点型的数据变量,double可以定义浮点型的变量,例如:522.5或者478.99等;排名是一种整型的数据变量,int可以定义整型变量,例如:237和432等。所以C语言提供了丰富的数据类型用来创建不同种类的变量,从而存储不同种类的数据。
2.数据类型的盘点
数据结类型分为内置类型和自定义类型。其中内置类型分为 字符型,整型,浮点型,布尔类型;自定义类型分为 数组,结构体类型,枚举类型,联合体类型。
内置类型:C语言本身就具有的数据类型
自定义类型:自己创造出的数据类型(后期博客展现)
3.初识内置类型
(1)字符类型
字符类型,它的英语发单词是character,所以也叫char类型。
(2)整型类型
整型类型分为四种,分别为短整型short ,整型int ,长整型long ,更长的整型long long 。
上述的短整型,长整型,更长的整型表示其实应该是short int, long int, long long int。只不过上面表示的形式为简写形式也最为常见
(3)浮点型类型
浮点型类型分为三种,分别为单精度浮点型float,双精度浮点型double,更高精度浮点型long double。
浮点型的解释:因为生活中小数的小数点可以随便移动,例如:3.14=31.4*10^(-1)。所以生活中的小数就是浮点数。
(4)布尔类型
C语言中原来并没有为布尔值(表示真假的变量)单独设置类型,而是使用整数0表示假,非零表示真。在C99中专门引入了布尔类型,专门表示真假。它可以用_Bool表示。
布尔类型的使用需要包含头文件<stdbool.h>
布尔类型变量的取值是true或者false。
C语言中布尔类型的定义:
cpp
#define bool _Bool
#define false 0
#define true 1
代码演示:
cpp
#include <stdio.h>
#include <stdbool.h>
int main()
{
_Bool flag =false;
if(flag)
printf("你好!");
else
printf("Hello");
return 0;
}
代码解释:
定义一个布尔类型的变量,名为flag,并且赋值为false。当flag为真则打印:你好!,否则打印Hello!。
运行结果为:Hello!
4.各种数据类型的长度
(1)背景:
每一种数据类型都有自己的长度,使不同的数据类型能够创建出长度不同的变量,变量的长度不同,存储的数据范围就有所差异。
(2)sizeof 操作符
sizeof是C语言中的关键字,也是一种操作符。它是用来计算sizeof的操作数的类型长度的,单位是字节。sizeof操作符的操作数可以是数据类型,也可以是变量或者表达式。但是sizeof中表达式不计算!!!
sizeof (类型)
sizeof(表达式)
sizeof(数据类型)
字节:计算机中内存的单位
bit:比特位 8字节=1比特位(一个二进制位的存储需要一个bit的空间)Byte:字节 1 KB=1024 Byte
KB 1MB=1024KB
MB 1GB=1024MB
GB 1TB=1024GB
TB 1PB=1024TB
PB .......
...
(3)运用sizeof计算各数据类型的大小
cpp
#include<stdio.h>
int main()
{
printf("%zd\n", sizeof (char)):
printf("%zd\n", sizeof (short)):
printf("%zd\n", sizeof (int)):
printf("%zd\n", sizeof (long)):
printf("%zd\n", sizeof (long long)):
printf("%zd\n", sizeof (float)):
printf("%zd\n", sizeof (double)):
printf("%zd\n", sizeof (long double)):
return 0;
}
代码运行的结果为:1 2 4 4 8 4 8 8
①疑问一
为什么 long / long double 的数据类型的大小和 int / double 数据类型的大小相同?
原因是:C语言标准规定 sizeof( long )>=sizeof ( int )
sizeof ( long double )>=sizeof ( double )
②疑问二
为什么用sizeof关键字时侯,需要用到%zd而不是%d?
sizeof的计算结果是size_t类型的数据。sizeof运算结果的返回值在C语言中被规定为无符号整数,(无符号整数也就是非负整数的意思,因为数据类型的大小一定是非负整数,所以在C语言中被规定为无符号整数)并没有规定具体的类型。而是交给系统自己决定。不同系统中返回值类型有可能是unsigned int(无符号整型),也有可能是unsigned long,甚至是unsigned long long,对应的占位符分别为%u %lu %llu。这样不利于程序的可移植性(程序的可移植性:在不同编译器中类型不同,不能随意的变换:我的代码在你的编译器上无法编译,你的代码在我的编译器上也无法编译)。
于是C语言提供了一种解决办法,创造了一个类型别名size_t的返回值类型。对应当前的系统的sizeof的返回值类型,可能是unsigned int 也有可能是unsigned long long 。
③疑问三
为什么sizeof中的表达式不计算???
cpp
#include<stdio.h>
int main( )
{
short s=2;
int b= 10;
printf("%zd\n",sizeof(s=b+1));
printf("s=%d\n",s);
return 0;
}
代码运行的结果为:2 s=2
sizeof中如果放的表达式,那么表达式不会真实计算。而是根据表达式的类型进行推断的。比如上面的例子:b是int类型的数据,+1之后依然是int类型的数据。但是再把它赋值给short类型数据堵塞变量s中,那么最后就会是short类型的大小。又因为sizeof中的表达式不参与运算,则s的值依然是2。
拓展:对于有符号整型的打印,占位符应该为%d,对于无符号整形的打印,占位符应该是%u.
二.signed和unsigned
C语言中,使用signed 和unsigned关键字修饰字符型和整型数据的。signed关键字,表示一个类型带有正负号,包含负值;unsigned关键字,表示不带有正负号,只能表示零和正整数。对于int类型而言,默认是带有正负号的,也就是说signed int=int。由于这是默认情况,关键字signed一般都省略不写,但是写了也不一定算错。
C语言中char类型默认为signed char 或者为unsigned char,是不确定的,取决于编译器的不同。(大部分情况下char=signed char)
整型变量声明为unsigned的好处是:同样的内存长度能够表示的最大整数值增加了原来的一倍。比如16位的signed short int 的取值范围是-32768~32767。32位的signed int 的取值范围可以参看limits.h给出的定义
下面是VS2022环境下,limits.h中的相关定义:
cpp
#define SHORT_MIN (-32768) //有符号16位整型的最小值
#define SHORT_MAX (32767) //有符号16位整型的最大值
#define USHRT_MAX 0Xffff //无符号16位整型的最大值
#define INT_MIN (-2147483647-1) //有符号整型的最小值
#define INT_MAX (2147483647) //有符号整型的最大值
三.数据类型的取值范围
上述的数据类型很多,尤其数整型类型就有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 的最⼤值。
四.变量
1.变量的概念
什么是变量呢?
C语言中把经常变化的值叫做变量,不变的值称为常量。比如年龄就是一个变量,
2.变量的创建和初始化
变量创建的方法:数据类型 + 变量名;例如下方的变量创建:
cpp
int age; //整型变量
char ch; //字符变量
double weight; //浮点型变量
变量的初始化:变量在创建的时候,给一个初始值就是变量的是初始化。例如:
cpp
int age =18;
char ch ='w';
double weight =48.8;
unsigned int height =180;
创建一个整型变量age,将其初始化为18;创建一个字符类型变量ch,将其初始化为w······
3.变量的分类
变量分为全局变量和局部变量。
在大括号内部的叫局部变量,局部变量的适用范围比较局限,只能在自己的局部范围(大括号内)内使用;全局变量是在大括号外部定义的变量,适用范围更广,整个工程都是可以使用的。
如果局部变量的变量名和全局变量的名字相同,会怎么办呢???
cpp
int m=1000;
int main( )
{
int m=100;
printf("%d",m); //这里的结果会是多少呢???
return 0;
}
代码的运行结果为:100
当全局变量和局部变量在该位置都可以使用时;局部优先。
4.内存的介绍
内存分为栈区,堆区,静态区,不同的变量存储的位置不同。如图所示:
五.算数操作符:+ - * / %
算数操作符也叫做双目运算符。操作符也叫做运算符。
1.加法减法运算符:+ -
加法符号和减法符号两边都是有2个操作数的,位于操作符两端的就是它们的操作数,这种操作符也叫双⽬操作符。以下是加减法运算符的运用:
cpp
#include <stdio.h>
int main()
{
int x = 4 + 22;
int y = 61 - 23;
printf("%d\n", x);
printf("%d\n", y);
return 0;
}
2.乘法运算符:*
cpp
#include <stdio.h>
int main()
{
int num = 5;
printf("%d\n", num * num); // 输出 25
return 0;
}
3.除法运算符:/
运算符 / ⽤来完成除法。 除号的两端如果是整数,执行的是整数除法,得到的结果也是整数。
cpp
#include <stdio.h>
int main()
{
float x = 6 / 4;
int y = 6 / 4;
printf("%f\n", x); // 输出 1.000000
printf("%d\n", y); // 输出 1
return 0;
}
上面示例中,尽管变量 x 的类型是 float (浮点数),但是 6 / 4 得到的结果是 1.0 ,而不是 1.5 。原因就在于C语言里面的整数除法是整除,只会返回整数部分,丢弃⼩数部分。如果希望得到浮点数的结果,两个运算数必须至少有一个浮点数,这时C语言就会进行浮点数除法。
再看一个例子:
cpp
#include <stdio.h>
int main()
{
int score = 5;
score = (score / 20) * 100;
return 0;
}
上面的代码,你可能觉得经过运算, score 会等于 25 ,但是实际上 score 等于 0 。这是因为 score / 20 是整除,会得到⼀个整数值 0 ,所以乘以 100 后得到的也是 0 。 为了得到预想的结果,可以将除数 20 改成 20.0 ,让整除变成浮点数除法。
cpp
#include <stdio.h>
int main()
{
int score = 5;
score = (score / 20.0) * 100;
return 0;
}
4.取模运算符
运算符%表示求余数运算:即返回两个整数相除的余值。这个运算符只能用于整数,不能用于浮点 数。
cpp
#include <stdio.h>
int main()
{
printf("%d\n", 11 % -5); // 1
printf("%d\n",-11 % -5); // -1
printf("%d\n",-11 % 5); // -1
return 0;
}
取余运算符的正负与前面操作数的正负相同!!!
六.赋值操作符
在变量创建的时候给一个初始值叫初始化,在变量创建好后,再给一个值,这叫赋值。
cpp
int a = 100;//初始化
a = 200;//赋值,
这里不要将赋值和初始化搞混了,两者存在本质的区别!!!
1.连续赋值
赋值操作符也可以连续赋值,如:
cpp
int a = 3;
int b = 5;
int c = 0;
c = b = a+3;//连续赋值,从右向左依次赋值的
最后一句代码就是连续赋值,这种代码不方便理解,建议拆开来写。就像下面那样写:
cpp
int a = 3;
int b = 5;
int c = 0;
b = a+3;
c = b;
2.复合操作符
在写代码时,我们经常可能对一个数进行自增、自减的操作,如下代码:
cpp
int a = 10;
a = a+3;
a = a-2;
将a+3的值赋值给a;
将a-2的值赋值给a;
这种代码的写法C语言提供了一种更简便的方法:
cpp
int a = 10;
a += 3;
a -= 2;
七.单目操作符
前⾯介绍的操作符都是双目操作符,有2个操作数的。C语言中还有一些操作符只有一个操作数,被称为单目操作符。 ++、--、+(正)、-(负) 就是单目操作符。
1.++和--
++是一种自增的操作符,又分为前置++和后置++,--是一种自减的操作符,也分为前置--和后置--.
(1) 前置++
cpp
int a = 10;
int b = ++a;//++的操作数是a,放在a的前⾯的,就是前置++
printf("a=%d b=%d\n",a , b);
计算口诀:先+1,后使用。
a原来是10,先+1,后a变成了11,再使用就是赋值给b,b得到的是11,所以经过计算之后,a和b都是11,相当于这样的代码:
cpp
int a = 10;
a = a+1;
b = a;
printf("a=%d b=%d\n",a , b);
(2)后置++
cpp
int a = 10;
int b = a++;//++的操作数是a,是放在a的后⾯的,就是后置++
printf("a=%d b=%d\n",a , b);
计算口诀:先使用,后+1。
a原来是10,先使用(赋值给b),b得到了10,然后再+1,然后a变成了11,所以经过计算之后a是 11,b是10,相当于这样的代码:
cpp
int a = 10;
int b = a;
a = a+1;
printf("a=%d b=%d\n",a , b);
(3)前置--
如果前置++搞明白了,前置--和前置++是相同的道理。
计算口诀:先-1,后使用。
cpp
int a = 10;
int b = --a;//--的操作数是a,是放在a的前⾯的,就是前置--
printf("a=%d b=%d\n",a , b);//输出的结果是9 9
(4)后置--
后置--类似于后置++,两者有着相同的计算原理。
计算口诀:先使用,后-1。
cpp
int a = 10;
int b = a--; //--的操作数是a,是放在a的后⾯的,就是后置--
printf("a=%d b=%d" ,a,b); //输出的结果是: 9 10
2.+和-
这里的+和-都是符号为正负的意思,是单目操作符。(操作数只有一个)
(1)+
运算符+对原变量的正负没有影响,该运算符是一个完全可以忽略的运算符,写了也没有错误。注意:这里的+是正负的意思,而不是加的意思。
cpp
int a = +10;
int a = 10;
上面两行代码是等价的!!!
(2)-
- 是用来改变一个值的正负的操作符,负数的前面加上 -, 就会把这个操作数变成正数,正数的前⾯加上 - ,就会把该操作数变为负数。
cpp
int a = -10;
int b = -a;
printf("b=%d\n", b);
八.强制类型转换
1.概念:
强制类型转换,就是在编程过程中,需要把一种数据类型强制转换成另外一种数据类型,这种操作就叫做强制类型转换。
2.语法形式
(需要转换的数据类型)左边的蓝色字体就是强制类型转换的语法形式。
3.代码演示
cpp
int a = 3.14;
//a的是int类型, 3.14是double类型,两边的类型不⼀致,编译器会报警告
int a = (int)3.14;
//意思是将3.14强制类型转换为int类型,这种强制类型转换只取整数部分
上述代码运行之后,a就会变成3!!!
4.注意事项
俗话说,强扭的瓜不甜,使用强制类型转换是万不得已的时候才会使用,如果不需要强制类型转化
就能实现代码,尽量不要使用。