(两万字)带你详解C语言数据类型和变量

今天我们讲关于C语言数据类型和变量的章节,今天文章比较长,希望读者能认真看并有所收获,如果有什么错误希望编程大佬们能有所指点,同时感谢大家的支持


一.数据类型的介绍

首先第一个问题是我们为什么要写代码呢?其实就是为了解决生活中存在的问题。

比如:在电脑上网购的时候每个商品出现的页面,有名字,编号和价格。如果在c语言中我们想要写出这商品页面代码的时候,如想要写代码来描述价格20元。首先我们的代码就要有描述价格的这种能力,还有名字,编号的能力,这些数据都有不同的类型,所以C语言提供了丰富的数据类型,帮助我们用来描述。

在这里简单的说一下数据类型:在C语言中使用整形类型来描述整形,使用字符类型来描述字符,使用浮点型类型用来描述小数。我们要知道其中类型的意义就是相似的数据所拥有的共同特征,这样编译器在运行的时候知道了数据的类型才知道怎么操作处理数据

在本章节主要探讨内置数据类型,就是C语言自己本身携带的数据类型。内置数据类型是C语言自带的可以直接使用,而自定义数据类型是需要创造才能使用的,接下来我们来介绍一下内置数据类型。

1.内置数据类型

字符型

char就是字符类型,还有有符号的[signed] char和无符号的unsigned char

整型

整型类型有:short [int] 短整型,int   整型,long [int] 长整型,long long [int] 更长的整型  最后这个是在c99标准下才有的,带括号是表示可以省略的意思。

浮点型

该类型主要描述的是小数,float 单精度浮点型,double 双精度浮点型,long double更长的浮点型。

布尔类型

它是专门用来表示真和假的类型的,在c99标准中也引入了布尔类型,在C语言早期零是假,非零是真,关于布尔类型是后面才引入使用的。布尔类型:_Bool,它在使用的时候必须包含头文件<stdbool.h>,它的变量取值是true或者false,在vs编译器下true被定义为1,false为0,它的类型也可以被定义为bool来使用。

对布尔类型来做一个例子看一看

在这里插入图片描述

从中我们可以看到,if语句判断条件flag是否为真假进行打印。如:flag是true进行打印,是false不打印。if语句不懂的先不用管后面会将到,能理解布尔类型用法就行。

大致我们认识的这些内容并没有深讲,等以后再说,现在我们知道了这些类型,其实类型的用途是进行创建变量,不同类型的变量要用不同类型进行创建才符合我们的语法,之后我们马上就要讲变量,我们先讨论数据类型的长度。

2.各种数据类型的长度

对于每种数据类型都有自己的长度,所以用不同的类型所创建的变量,变量的长度也不会相同,这就表示着变量所存储的数据范围就会有所差异。那我们怎么来研究变量的长度呢?首先我们来介绍一下sizeof操作符

sizeof

sizeof操作符,它既是一个关键字也是一个操作符,是专门用来计算sizeof的操作符数的类型长度的,它的单位是字节,这个很重要要记住,它的操作数可以是类型,也可以是变量或者表达式。如:sizeof(类型),sizeof (表达式)。

关于sizeof的使用,我们直接上例子来解释

在这里插入图片描述

其实在括号里面可以放变量,括号是可以省略,但是最好带着既然该公式有括号就有它的作用,我认为应该不同的编译器用法不同吧,同样括号里也可以放变量对应的类型,但是这个时候括号不能省略
我们来看打印的结果是4,这时候我们就要问了这4到底是表达什么意思呢?
我们知道sizeof的单位是字节,所以4就是表示4个字节。

科普小知识:什么是字节?
在计算机中的单位有:
Bit - 比特位   它是最小的单位
Byte - 字节.  1Byte=8Bit
KB.               1KB=1024Byte
MB.               1MB=1024KB
GB.               1GB=1024MB
TB.                1TB=1024GB
PB.                1PB=1024TB
1024=210次方。
计算机能识别的是二进制,而二进制数字是由零和一组成的,存放一个二进制位需要的空间就叫一个比特位,一个字节就有8个比特位,也就是说一个字节最多能存放8个二进制位。

那这个时候我们就知道了4个字节其实就是32个比特位

在这里插入图片描述

如图上的这个 int a = 10 表达式,它的意义就是向内存申请4个字节的空间存放的10,因为是int类型,创建变量的时候编译器看见是 int 就是认为要申请4个字节空间,来存放我们的整形值。a就是这4个字节空间的名字,它是为了我们方便使用,用a的时候就是表示指使用这4个字节空间
比如:就像我们有一本书要放书架,就要有一块空间能放进去,或者宿舍有一块空间能让人住进去,所以当我们有一个整形存放的时候,要向内存申请空间把它放进去,并且要有名字方便找到使用。

int a = 10;该表达式实际上是表示创建变量a是向内存申请4个字节的空间,存放的是10,其实变量创建的本质上就是内存的申请,

我们知道了int类型占4个字节,那其他类型的呢?

在这里插入图片描述
从上面我们会产生疑问:为什么int类型跟long类型长度一样大呢?long是表示长整型所占字节不应该比int大吗?

我们要知道,在C语言标准规定下sizeof (long)>=sizeof(int),这要取决于不同的编译器,long长度有可能4或8字节,该编译器是在VS2022 X64环境下 long的长度是4,所以长度相同。在同样情况下double类型和long double类型,所占字节相同,也是同样情况的。

根据你想储存的数据选择适当的类型,记住所选的越适当的类型匹配度越高空间利用率就越高,如果选的比较大的空间,用来存比较小的数据,这样的话空间就浪费了。

小知识:如果我们仔细看的话,在我们打印的时候出现了警告,它意思是sizeof打印的结果不应该用%d格式打印出来,它算出的类型是size_t的类型,不是整型,应该用%zd格式进行打印,它是用来打印sizeof返回值的,知道打印格式就行了。

sizeof中表达式不计算

在这里插入图片描述
为什么sizeof算出的长度为什么是二呢?

我们要知道sizeof内部的表达式是不会真实计算的,b+1它是int类型的,s是short类型的,int长度是4,short长度是2,比如:就像一个杆子,它有4米,但是箱子有两米,你硬要是把杆子放进箱子里面,那就要将杆子截断,正如当int值放在short里面的时候,int长度比short长度大,所以放的时候有可能会发生数据丢失,通俗来讲也就是把它放在谁里面谁就说了算,也就是箱子说的算,这个表达式最终还是short类型决定的,所以算出来的长度是2,这就是通过类型的推导所算出的结果。


二.signed和unsigned

signed和unsigned这两个关键字修饰的是字符型和整型类型的,不包括浮点型。signed表示一个类型有正负号,包含负值,unsigned表示该类型不带有正负号,只能表示0和正整数。对于int类型,它默认带有正负号,等同于signed int。

知识点
sizeof的计算结果是size_t类型的。sizeof 运算符的返回值,C语言只规定是无符号整数,并没有规定具体的类型,而是留给系统自己去决定,sizeof到底返回什么类型。不同的系统中,返回值的类型有可能是 unsigned int,也有可能是unsigned long,甚至是 unsigned long long,对应的 printf()占位符分别是 %u 、%lu
和%1lu。这样不利于程序的可移植性。C语言提供了一个解决方法,创造了一个类型别名 size_t,用来统一表示sizeof 的返回值类型。对应当前系统的 sizeof 的返回值类型,可能是 unsigned int,也可能是 unsigned long long

在这里插入图片描述


三.数据类型的取值范围

上述的数据类型很多,尤其数整型类型就有shortintlonglong long 四种,为什么呢?
其实每一种数据类型有自己的取值范围,也就是存储的数值的最大值和最小值的区间,有了丰富的类型,我们就可以在适当的场景下去选择适合的类型。如果要查看当前系统上不同数据类型的极限值:
limits.h文件中说明了整型类型的取值范围。float.h这个头文件中说明浮点型类型的取值范围。可以在Everything中搜索这两个文件就可以查到我们想要的数值。
为了代码的可移植性,需要知道某种整数类型的极限值时,应该尽量使用这些常量。
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

整数变量声明为unsigned的好处是,同样长度的内存能够表示的最大整数值,增大了一倍。 比如:16位的 signed short int的取值范围是:-32768到32767,最大是32767; 而 unsigned short int的取值范围是:0~65535,最大值增大到了65,535。不需要记相关类型的最大者和最小值,如果要用的着去搜就行了,可以在Everything中搜索limits.h头文件中,里面有详细的数值。表示是一个int类型整型能表示的最大值是多少?最小值多少?他的边界在哪里?所以一个类型是有边界的,不能放一个超出它的范围,特别大的数。

int等价于signed int,但是char不等价于signed char,但是char到底是signed char还是unsigned char 是取决于编译器的。但大部分的编译器上char == signed char。

那我们知道这类型之后有什么用呢?类型是用来创建变量的,接下来我们来讲变量


四.变量

1.变量的创建

在c语言中把经常变化的值称为变量,把不变的值称为常量
如变量:年龄,体重,时间都会发生变化。
常量:名字,身份证号,不发生变化。

变量创建的语法形式:data_type name,第1个是数据类型,第2个是变量名,看下面的举例:

在这里插入图片描述

变量创建的本质是:向内存申请空间。如我们创建变量是为了存放一个数字,既然要储存数据我们就要有空间把它放进去,所以要申请空间,所以创建变量的时候就是向内存申请空间,那我们申请多大的空间呢?是他前面类型说的算,如编译器看到int来申请空间了,int长度是4个字节,那就申请的就是4个字节的空间。如char ch当创建变量的时候,ch是一个字节空间,因为char类型长度为1,所以当看到char类型的时候,ch变量将内存申请空间就是一个字节的空间。空间大小类型说的算,变量类型的长度,它这就是为创建变量所申请的空间。

所以变量创建的本质是向内存申请空间,空间的大小取决于变量的类型

当变量在创建的时候就给一个初始值,就叫初始化。如:

在这里插入图片描述

2.变量的分类

全局变量是在大括号外部定义的变量就是全局变量,全局变量的使用范围很广,在整个工程中想使用可,都是有办法使用的。局部变量是在大括号内部定义的变量就是局部变量,局部变量它的使用范围是比较局限的,只能在自己的局部范围内使用

例子
在这里插入图片描述

在这里插入图片描述

看上面打印的结果是a不能被使用打印了,因为a这个变量是一个局部变量的a,是在单独一个大括号里面的。他只能在这个局部范围内使用,出了这个范围就不能使用了。比喻来说,局部变量就像自己家的私家车只能自己使用,而全局变量就像共享单车谁扫码都可以使用。它在这个局部范围内创建,只能在这个局部范围内使用。

那我们到底是尽量去使用全局变量还是局部变量呢?

我建议能使用局部变量,尽量使用局部变量,必须使用全局变量的时候,再使用全局变量。因为全局变量使用的范围比较广,它在哪个地方都可以使用,如果我们都去改这个变量的时候,最终它被变成什么我们就不知道了。就像共享单车,谁都可以骑那它就比较坏的快,出问题就比较大,可控性比局部变量差一些。还有一点是全局变量一旦创建之后,一直占据内存,直到程序结束,而局部变量进入这个范围,创建变量,而出这个范围,变量所占的内存就被回收了,所以局部变量的内存利用率会更高一些。

在这里插入图片描述

当局部和全局变量名字相同的情况下。局部变量优先使用

拓展小知识1

全局变量和局部变量在内存中储存在哪里呢?就是创建变量的时候向内存申请空间,但是我们在哪里向内存申请空间呢

关于内存区域的划分的大概,在学习C语言中只关注这三个区域就行了。如图:
在这里插入图片描述

知识点2

如果一个局部变量不初始化,它的值是多少?

在这里插入图片描述

在vs编译器上会报警告不进行打印,但在dev++编译器上它不初始化,打印出来的值是为0,在小熊猫c++编译器上它不初始化,打印出来的是随机值,其实当我们不初始化的话是不知道它的值是多少的,所以说这是devc++这个编辑器是不严谨的地方,不够好,不够专业的地方。

那如果对全局变量不初始化呢?,会有什么结果呢?

其实全局变量没有初始化的时候,它的值默认为0。这是在C语言中规定的,为什么这样规定的就是跟它在内存存储区域有关系,对照我上面画的内存划分的三个区域。放在栈区的变量不会给它初始化,不会给它默认值的,所以就是随机值,放在静态区的变量不给它初始化,就会给它一个

我们写代码为的是就是运算,让代码帮助我们进行运算的,那在代码中如何参与运算呢?接下来我们来介绍算术操作符


五.算术操作符

在我们写代码时候,一定会涉及到计算。
C语言中为了方便运算,提供了一系列操作符,其中有一组操作符叫:算术操作符。分别是:+ - * / %,这些操作符都是双目操作符。注:操作符也被叫做:运算符,是不同的翻译,意思是一样的。

+和-

加号和减号是用来完成加法和减法,如:3+6,3-6,加号减号叫操作符或者运算符,3和6叫做操作数,3叫左操作数,6叫右操作数。加号和减号两边有两个操作数,它又叫双目操作符

在这里插入图片描述

在c语言中,它的计算是从右向计算的,如果创建了变量,算出来的结果赋值给变量。

这个星表示乘号,我们用运算符*来完成乘法。

运算符/用来完成除法。

在这里插入图片描述

为什么第2行不是我们想要的结果呢?,会是因为1.5是个小数不能用%d格式来进行打印吗?

在这里插入图片描述

我们发现这就更错了,还是不对。其实是这样的:6÷4它其实是等于商1余一个2,因为当除号两端都是整数的时候,它只能按整数的方式运算,就是整数除法,得到的结果也是整数,只会返回整数部分,丢弃小数部分,所以当我们6÷4的时候,只是得到了它的商,不会得到小数1.5。

总结一下:除号的两端都是整数的时候是按照整数运算的方式计算的,得到的是,如果希望得到浮点数的结果,两个运算数必须至少有一个浮点数,这时C语言就会进行浮点数除法

在这里插入图片描述
在这里插入图片描述

运算符%表示求模(余)运算,即返回两个整数相除的余值。这个运算符只能用于整数,不能用于浮点数。

在这里插入图片描述

取模得到的是整除之后的余数,小数不能取模。负数取模的规则是:打印结果的正负号由第一个运算数的正负号决定。
在这里插入图片描述


六.赋值操作符

当我创建变量的时候,顺便给给变量一个值,这个叫初始化,这不叫赋值。当变量已经有值了,再给它一个值,这个叫赋值,该等号就是赋值操作符

在这里插入图片描述

赋值操作符也可以连续赋值

在这里插入图片描述

C语言虽然支持这种连续赋值,但是写出的代码不容易理解,建议还是拆开来写,这样在调试的时候方便观察代码的执行细节。

复合赋值符
在写代码时,我们经常可能对一个数进行自增,自减的操作,如下代码:
int a = 10;
a = a+3;
a = a-2;
这样代码C语言给提供了更加方便的写法:
int a = 10;
a  += 3;
a  -= 2;
C语言中提供了复合赋值符,方便我们编写代码,这些赋值符有:
+=   -=  *=  /=  %=
下面的操作符后期讲解
>      >>=      <<=     &=    |=   ^=

在这里插入图片描述


七.单目操作符

我们前面所介绍的操作符都是双目操作符,有两个操作数的,C语言中还有一些操作符只有一个操作数,它们被称为单目操作符,单目操作符是只有一个操作数的操作符。如:++,–,+(正号),-(负号) 就是单目操作符。

++和–
++是一种自增的操作符,又分为前置++和后置++,–是一种自减的操作符,也分为前置–和后置–。
不管是前置++还是后置++都是加1的操作,就i是a++或者++a都会让a自增1

那前置++和后置++作用和区别在哪里呢?

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

从上面可以得到前置和后置对a没区别,但是如果考虑将a赋值给b的结果就要考虑用后置还是前置,你一定要明白前置和后置影响在哪里

+和-

这里的+是正号,-是负号,都是单目操作符。运算符+对正负值没有影响,是一个完全可以省略的运算符,但是写了也不会报错。
int a = +10;等价于 int a = 10;
运算符-用来改变一个值的正负号,负数的前面加上-就会得到正数,正数的前面加上-就会得到负数。

在这里插入图片描述


八.强制类型转换

在操作符中还有一种特殊的操作符是强制类型转换,语法形式很简单,如:(类型),意思就是把不合适的类型进行强转。

在这里插入图片描述

强制类型转化成整数之后,小数部分丢掉只留整数部分。想把它转化成什么的类型就在括号里面放什么类型,括号放它前面,这是语法规定的,这就是强制类型转换。但是如果能设计出类型匹配的情况,就尽量不要让它强转,尽量少用强转,毕竟还是强制的,强扭的瓜不甜。

小知识
float 单精度浮点数  4个字节 存储数值的范围小一点。
double. 双精度浮点数  8个字节 存储数值的范围大。
double能表示的精度更高,float表示的精度低

九.scanf和printf介绍

1.printf

printf()的作用是将参数文本输出到屏幕,printf ---- print. +. format. 它的基本作用是按照指定的格式在屏幕上打印信息,表示定制输出文本的格式。还有printf()不会再行尾自动添加换行符,运行结束后,光标就停留再输出结束的地方不会自动换行,为了让光标移到下一行的开头,可以在输出文本的结尾,添加一个换行符\n

r

如果文本内部有换行,也是通过插入换行符来实现,如下:

在这里插入图片描述
printf()是在标准库的头文件stdio.h定义的。使用这个函数之前,必须在源码文件头部引入这个头文件。

刚才我们打印的是字符串,如果我们想要打印其它类型怎么办呢?这个时候我们来介绍占位符的概念

2.占位符

printf()可以在输出文本中指定占位符。所谓占位符,就是这个位置可以用其他值代入:

在这里插入图片描述

上面示例中,I have %d apples\n 是输出文本,里面的 %d 就是占位符,表示这个位置要用其他值来替换
占位符的第一个字符一律为百分号%,第二个字符表示占位符的类型,%d 表示这里代入的值必须是一个整数

在这里插入图片描述

如%d就是占位符,表示整数,打印的时候他会被100替换,就是用整数替换,打印出100,100会替换到%d位置上去。这就是占位符,就是它先把这个位置占着,并且告诉你它是什么类型,打印时候用后面的相应类型值替换它

在这里插入图片描述

占位符就是先把它给占住,回头再拿来后面的值把它替换掉。替换的值必须是与占位符一模一样的匹配类型才可以。

除了%d占位符之外,还有%s占位符

在这里插入图片描述

上面示例中百分号s表示代入的是一个字符串,所以printf()的第2个参数就必须是字符串

前面相当于一个格式串,按照一定的格式打印数据,有占位符,还有默认打印的信息,占位符对应的信息在后面,中间是用逗号隔开的。我们打印的信息除占位符之外,还有其他的额外的信息

输出文本里面可以使用多个占位符。

在这里插入图片描述

上面示例中,输出文本%s says it is %d o’clock 有两个占位符,第一个是字符串占位符 %s,第二个是整数占位符 %d,分别对应 printf()的第二个参数(lisi)和第三个参数(21)。执行后的输出就是lisi says it is 21 o’clock。
printf()参数与占位符是一一对应关系,如果有n个占位符, printf()的参数就应该有n+1个。如果参数个数少于对应的占位符, printf()可能会输出内存中的任意值。比如上面:在printf括号里面格式串算一个参数,格式串后面的那俩算两个参数,他俩是用于替换格式串中的两个占位符的。

我们知道是可以写多个占位符,但是这些占位符要跟后面替换占位符的值要匹配顺序而且匹配个数才可以的,如果不匹配会发生错误。

3.占位符列举

printf()的占位符有许多种类,与C语言的数据类型相对应。下面按照字母顺序,列出常用的占位符,方便查
找,具体含义在后面章节介绍。
.%a:十六进制浮点数,字母输出为小写。
.%A:十六进制浮点数,字母输出为大写。
. %c:字符。//char
. %d:十进制整数。//int
.%e:使用科学计数法的浮点数,指数部分的e为小写。
.%E:使用科学计数法的浮点数,指数部分的 E 为大写。
. %i:整数,基本等同于%d。
.%f:小数(包含 float 类型和 double 类型)。//float %f double-%lf
. %g:6个有效数字的浮点数。整数部分一旦超过6位,就会自动转为科学计数法,指数部分的e为小写。
%G:等同于%g,唯一的区别是指数部分的E为大写。
. %hd:十进制 short int 类型。
.%ho:八进制short int类型。
. %hx:十六进制 short int类型。
· %hu: unsigned short int 类型。
.%ld:十进制long int类型。
·%lo:八进制long int类型。
·%1x:十六讲制 lona int类型
·%lu: unsigned long int 类型。
.%1ld:十进制long long int类型。
. %llo:八进制 long long int类型。
.%llx:十六进制 long long int类型。
 %llu: unsigned long long int 类型。
· %Le:科学计数法表示的 long double 类型浮点数。
. %Lf:long double 类型浮点数。
.%n:已输出的字符串数量。该占位符本身不输出,只将值存储在指定变量之中。
. %o:八进制整数。
. %p:指针(用来打印地址)。
. %s:字符串。
. %u:无符号整数(unsigned int).%x:十六进制整数。
· %zd: size_t类型。
.%%:输出一个百分号。

print f()可以定制占位符的输出格式。就是占位符可以进行相关的修饰和修改

4.限定宽度

Print f()允许限定占位符的最小宽度

在这里插入图片描述

我们可以看到第2行前面空出的两个空格,算上后面123总共是5位。%5d表示使那个地方空出了两个字符的位置,用空格补齐两个字符,再算上123一共用5个字符的位置,这个就是那个五的作用。但是这个五是指定的最小宽度,表示最少打印5位,不够5位给你补齐5位,超过5位有几位打印几位。所以这就是指定宽度的作用

在这里插入图片描述

前面加一个负号是左对齐,就是左边开始写不够的话,右面补空格。不加负号的话就是右对齐,不够的话左边补空格。

在这里插入图片描述

总是显示正负号

默认情况下, printf()不对正数显示+号,只对负数显示﹣号。如果想让正数也输出+号,可以在占位符的%后面加一个+

在这里插入图片描述
上面实例中,%+d可以确保输出的数值,总是带有正负号。

5.限定小数位数

输出小数时有时希望限定小数的位数。举例来说:希望小数点后面只保留两位的,占位符可以写成%.2f

在这里插入图片描述
在这里插入图片描述

从上面看这样做确实会保留两位,同样的话它也会四舍五入。这个也是需要注意的点。如果你想要小数点后面打印输出几位。,就在%后面输入点几f就行了。

除此之外我们能不能既限定总宽度,又限定小数点后面的位数?

在这里插入图片描述

从上面看,我们是可以完成的,百分号12.2f表示输出字符串最小宽度为12,小数位数为二,所以输出字符串头部的有7个空格。

我们也可以用另一个写法来完成这个操作,最小宽度和小数位数这两个限定值都可以用*替代,通过printf()的参数传入。

在这里插入图片描述
上面例子中,%*.*f的两个星号通过printf()的两个参数6和2传入。

6.输出部分字符串

%s占位符用来输出字符串,默认是全部输出如果只想输出开头的部分,可以用%.[ m ]s指定输出的长度,其中[ m ]代表一个数字,表示所要输出的长度。

在这里插入图片描述

现在信息给我们知道怎么打印在屏幕上了,那怎么给变量输入值呢?接下来我们就要讲scanf函数

7.scanf

当我们有了变量,我们需要给变量输入值就可以使用scanf函数,如果需要将变量的值输出在屏幕上的时候可以使用printf()函数,下面看一个例子:

在这里插入图片描述

首先我们对变量进行初始化为零,因为我用得vs编译器不支持变量没有值,如果不初始化运行的话会报错误,当我们想要给变量输入我们想要的一个值的时候,就要用到scanf函数。从上面观察来看是scanf函数和printf函数在语法上也是有点相似,scanf双引号里面放占位符,双引号后面的是取得变量的地址,就像送外卖小哥一样,他要把外卖送到家里就要有你家里的地址,同样我们变量要想给它一个输入值的话就要知道变量的地址,然后把这个值放进去

因为该变量是整形变量所以放的值应该是整型,所以该格式放的是%d的占位符表示往n变量中输入的是一个整数值。&叫取地址操作符,它取出的是变量的地址,然后根据scanf函数把输入的值放在该地址中,然后相当于放在变量n中了。我们要知道scanf函数在用的时候也要有顺序匹配,类型匹配

scanf()函数用于读取用户的键盘输入。程序运行到这个语句时,会停下来,等待用户从键盘输入。用户输入数据、按下回车键后,scanf()就会处理用户的输入,将其存入变量。它的原型定义在头文件 stdio.h。

scanf()的语法跟 printf()类似。例子:scanf(“%d”,&i)。

它的第一个参数是一个格式字符串,里面会放置占位符(与printf()的占位符基本一致),告诉编译器如何解读用户的输入,需要提取的数据是什么类型。这是因为C语言的数据都是有类型的,scanf()必须提前知道用户输入的数据类型,才能处理数据。它的其余参数就是存放用户输入的变量,格式字符串里面有多少个占位符,就有多少个变量。上面示例中, scanf()的第一个参数 %d,表示用户输入的应该是一个整数。 %d 就是一个占位符,%是占位符的标志, d表示整数。第二个参数&i表示,将用户从键盘输入的整数存入变量 i。注意:变量前面必须加上&运算符(指针变量除外),因为 scanf()传递的不是值,而是地址,即将变量i的地址指向用户输入的值。如果这里的变量是指针变量(比如字符串变量),那就不用加&运算符。

在该函数格式字符串中也可以有多个占位符

在这里插入图片描述

在上面示例中格式字符串中表示用户输入的前两个是整数后两个是浮点数,如上面输入的4个值,这4个值依次放入i j x y这四个变量中。在该函数格式控制中要根据该变量的类型来放占位符,同时也要有顺序匹配

普及小知识。
-4.0e3这是科学计数法的表示形式
-4.0e3   --->   -4.0  *  10的三次方。

该函数处理数值占位符时**会自动过滤空白字符,包括空格,制表符,换行符等,所以,用户输入的数据之间,有一个或多个空格不影响scanf()解读数据,**另外,用户使用回车键,将输入分成几行,也不影响解读。

在这里插入图片描述
在这里插入图片描述

该函数处理用户输入的原理是:用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存进行解读,解读用户输入时会从上一次解读遗留的第1个字符开始,直到读完缓存或者遇到第1个不符合条件的字符为止

例子

在这里插入图片描述

刚开始读取这个数据的时候,空白的会被过滤掉,然后开始解读-13再往后的话就是点那就要变成小数了,因为格式解读的是整数,再往后读的话格式就不能匹配%d 了,所以遇到点儿之后就不再往后解读了。第2次用函数,由于对应的占位符是%f,该格式要解读的是小数,因为上一次是从点开始结束的,所以要从点开始重新解读,当遇到井号的时候,因为井号不是数字,读取就要结束了,后面也不再读取了。

.45e12表示的是0.45*10的12次方。0.45实际上在内存中无法精确的保存,只能保存相近的值,会无限接近,但有误差,所以第3行打印的这种结果,至于为什么以后会讲。

8.scanf的返回值

在这里插入图片描述

从上面来看表示该函数会有一个返回值,该返回值的类型为int,所以在使用scanf函数时会返回一个整型值。scanf()的返回值是一个整数,表示成功读取的变量个数。如果没有读取任何项,或者匹配失败,则返回0。
如果在成功读取任何数据之前,发生了读取错误或者遇到读取到文件结尾,则返回常量EOF(-1)。EOF- end of file 文件结束标志

在这里插入图片描述

有错误的读取,没成功的

在这里插入图片描述

全都没有成功读取

在这里插入图片描述

EOF

在这里插入图片描述

scanf()常用的占位符如下,与 printf()的占位符基本一致。
· %c:字符。
· %d:整数。
· %f: float 类型浮点数。
· %lf: double类型浮点数。
· %Lf: long double 类型浮点数。
.%s:字符串。
.%[]:在方括号中指定一组匹配的字符(比如%[0-9]),遇到不在集合之中的字符,匹配将会停止。

scanf()将字符串读入字符数组时,不会检测字符串是否超过了数组长度。所以,储存字符串时,很可能会超过数组的边界,导致预想不到的结果。为了防止这种情况,使用%s占位符时,应该指定读入字符串的最长长度,即写成 %[m]s,其中的[m]是一个整数,表示读取字符串的最大长度,后面的字符将被丢弃。

在这里插入图片描述

从上面的例子可以看到,本来该字符数组最多可以放5个字符,我们超过了5个,scanf函数仍然继续往后放,所以就造成了非法访问,越界的问题,导致程序崩溃发出警报,胆子很大,它不会管你有没有多少空间,只要你给它输入多少它就读多少,都给你放进去。这就是该函数不安全的地方,在读取的数据的时候,不会看目标空间中能够存的下数据。

%c
我们在举例上面所有占位符之中,除了%c以外,都会自动忽略起首的空白字符。但%c不忽略空白字符,总是返回当前第一个字符,无论该字符是否为空格。如果我们想要强制跳过字符前的空白字符,可以写成scanf(" %c",&ch),即%c 前加上一个空格,表示跳过零个或多个空白字符。

下面我举一些例子,使更好理解一些。

在这里插入图片描述

读取的是一个空格,所以a打印的是一个空格,空格也是一个字符。

在这里插入图片描述

在这里插入图片描述

%s

下面要特别说一下占位符%s,它其实不能简单地等同于字符串。它的规则是,从当前第一个非空白字符开始读起,直到遇到空白字符(即空格、换行符、制表符等)为止。因为%s 不会包含空白字符,所以无法用来读取多个单词,除非多个 %s一起使用。这也意味着, scanf()不适合读取可能包含空格的字符串,比如书名或歌曲名。另外, scanf()遇到%s 占位符,会在字符串变量末尾存储一个空字符\0。

我们也来举一些例子来看

在这里插入图片描述
在这里插入图片描述


十.赋值忽略符

有时用户输入可能不符合预定的格式。

在这里插入图片描述
我们希望用户这样输入的格式,这种打印结果,并不是我们想要的,其实是读错了。

在这里插入图片描述

当它读取杠的时候2024后面却没有杠是空格所以读取错误停止读取,后面那俩值都没有读到。所以你输入的一定要匹配格式串中的格式,才能打印出你想要的结果。

在这里插入图片描述

上面示例中,如果用户输入 2020-01-01,就会正确解读出年、月、日。问题是用户可能输入其他格式,比如
2020/01/01,这种情况下,scanf()解析数据就会失败。为了避免这种情况, scanf()提供了一个赋值忽略符(assignment suppression character)I*。只要把*加在任何占位符的百分号后面,该占位符就不会返回值,解析后将被丢弃。

在这里插入图片描述

%*c表示我会读这个字符,但读完我不会把它存起来,又把它删掉了,其实在这里就是感觉它想过渡一下,就算把它读了,也不是想要它,主要是想读后面的值。

OK,总算结束了,感谢大家的支持!!!

  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值