1·signed unsigned
2·数据类型及其取值范围
3·变量
4·操作符
5·强制类型转换
6·printf 和 scanf 详细介绍
1
在介绍数据类型之前,先介绍signed 和 unsigned。
signed和unsigned都是C语言中的关键字,它们的作用是决定字符或者整型的正负号。
signed(有符号)表示该数据可正可负,unsigned修饰一个数据的时候表示该数据只能是正数或者0。
而int,整型,是默认带有符号的,你可以写成signed int,也可以写成int,两种写法都是正确的。写成unsigned int 就是表示非负整数了,当然,也可以省略int ,直接写unsigned。
但是这两个的区别不止这个,还有取值范围。unsigned就厉害了,直接让正数取值范围扩大了一倍,因为数在计算机是二进制存储的,开头的数是表示符号,0表示正,1表示负。
inunsigned int类型比int类型的容量大是因为,unsigned int是无符号类型的,所以最高位不表示正负,而int类型的最高位是表示正负的。
根据VS.2022环境中limits.h(整型的取值范围。如果是float.h就是浮点型的取值范围)的文件我们来看一下取值范围。
浅浅了解一下进制
16位的 signed short int 的取值范围是:-32768~32767,最⼤是32767;⽽ unsigned short int 的取值范围是:0~65535,最⼤值增⼤到了65,535。
好了,注意,字符类型的signed和unsigned和整型是有区别的。
C 语⾔规定 char 类型默认是否带有正负号,由当前系统决定。 也就是说, char 不等同于 signed char ,它有可能是 signed char ,也有可能是 unsigned char 。 这⼀点与 int 不同, int 就是等同于 signed int 。
2
编程为生活服务,而生活中的元素有不同的种类,编程中也是。
下面介绍不同的数据类型及其取值范围。
如图,数据类型有两大类,今天就内置类型进行介绍,自定义类型后续跟进。
字符
字符在前一篇文章已经进行了较为详细的介绍,这里暂且略过。
整型
整型,整数类型的意思,在C语言中,将整型进行了一个细分,分为短整型,整型,长整型,更长的整型,注意,更长的整型是在C99中引入的。
对应的代码为 short int , int ,long int ,long long int 。当然除了整型的int不可省略,其他可以省略。
既然分为长短,那么取值范围就有区别,我们根据limits.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 的最⼤值。
那么是什么导致它们的取值范围不同呢?
答案是在内存中占的字节数。
PS:计算机最小存储单位是bit,然后是byte,kb,mb,tb...
一个比特存放0或者1,一个字节等于8比特,1 kb=1024byte...
一个整型占4个字节,短整型占2个字节,长整型占8或者4个字节,更长的整型占8个字节。
有人就问了,欸long为什么是两种情况,这是因为C语言中规定占字节数long>=int,取决于多少位系统。
浮点型
不少人看到这个名字觉得好高深,其实就是小数的意思啦,当然,浮点型分为两种,一种是单精度浮点型,一种是双精度浮点型,一听就知道双精度浮点型的精确度要高点。
C语言中规定,float是单精度浮点型,double是双精度浮点型。
浮点型也是有long double的。
布尔类型
C语言原来并没有为布尔值单独设置⼀个类型,而是使用整数 0 表示假,非零值表示真。 在 C99 中也引入了布尔类型 ,是专门表示真假的。
布尔类型的写法有两种,_Bool和bool,取值有false和true。
使用布尔类型要引用头文件<stdbool.h>的,那么进行一下代码演示。
好了,内置类型已经有了一个大概的了解,那么我们现在来了解一下每种数据类型在内存中的内存大小,在此之前,先了解一下sizeof这个关键词。
sizeof
sizeof既是一个关键词,也是一个操作符,可以用来计算数据类型的所占字节数,也可以用来计算数组所占字节数。
先来看看应用场景之数组大小;
arr1数组有5个字符,而一个字符占1个字节,所以输出结果是5。
但是!
sizeof 运算符的返回值,C 语⾔只规定是无符号整数,并没有规定具体的类型,而是留给系统自己去决定, sizeof 到底返回什么类型。不同的系统中,返回值的类型有可能是 unsigned int ,也有可能是 unsigned long ,甚至是 unsigned long long , 对应的 printf() 占位符分别是 %u 、%lu 和 %llu 。这样不利于程序的可移植性。 C 语言提供了⼀个解决方法,创造了⼀个类型别名 size_t ,⽤来统⼀表示sizeof的返回值类型。对应当前系统的 sizeof 的返回值类型,可能是 unsigned int ,也可能是 unsigned long long 。
所以C语言给了一个解决方法,那就是单独设一个类型别名,即sizeof的返回结果是size_t类型,对应的占位符应该是%zd。
sizeof后面可以是类型,也可以是表达式,但是表达式的括号是可以不用加的,类型一定要加括号,如图
虽然sizeof可以计算表达式的所占字节数,但是sizeof里面的表达式是不进行计算的,如图
好了,接下来是不同数据类型的所占字节数;
相信大家也知道代码怎么写了,那么不多说直接上代码
ok数据类型就介绍完了,开启下个内容。
3
C语言中把变化的量称为变量,不变的量称为常量。
这是创建变量的过程。
数据类型决定变量类型。
在VS里面,要求变量最好进行初始化,不然在VS这种对语法要求比较高的环境中,可能会报错。
初始化即是对变量随便赋一个值。
变量也有分类,分为局部变量和全局变量。
局部变量就是放在大括号之内的变量,如;
#include <stdio.h>int main(){ int a=0; return 0;}
这里的a就是局部变量。
全局变量就是放在大括号之外的变量,如;
#include <stdio.h>int b=0;int main(){ int a=0; return 0;}
这里的b就是全局变量。
局部变量的作用范围就只有所在的大括号之内,全局变量的作用范围自然就是整个工程。
那么,当局部变量和全局变量冲突的时候结果如何呢?
可以发现,当局部变量和全局变量冲突的时候,局部变量优先。
那么它们的存储地方呢?
局部变量存储在内存的栈区,全局变量存储在内存中的静态区,这里稍作介绍,后面深入。
4
操作符也有一定的分类,算术操作符,赋值操作符,复合操作符,单目操作符,双目操作符,三目操作符等等,操作符数不胜数,这里就提到的进行介绍。
PS:操作符也被称为运算符,不同的翻译,意思一样
算数操作符,无非就是进行计算所用的操作符,那么包括的操作符有,
+ - / * %
对应的中文就是加 减 乘 除 取模,取模是取余数的意思,这些操作符与生活中的加减乘除有些许差别,但我认为是可以接受的。
这里+ - * 不进行过多介绍,计算机中运算与生活中无异。
重点介绍 / %
先看一串代码:
提问,结果是什么呢?相信不少刚接触C语言的认为答案是1.5,那么结果是否如此呢?
看来不是,而且细心的人会发现6的下面有三个点,当把鼠标移动过去就会有提醒说“整数除法运算应先去尾再转化为浮点型”,原因在于我们在进行除法运算时两个操作数都是整型,C语言中的整数除法是整除,也就是只会返回整数部分,丢弃小数部分。
所以希望得到浮点数的结果,两个运算数必须至少有一个浮点数。如图:
% 取模,即返回两个数相除的余数,但是这个操作符只能用于整数,不能用于浮点数。
但是负数也是可以进行取模的,但是结果的正负号只有第一个运算数的正负号决定,
如图:
赋值操作符和复合赋值
在定义一个变量的时候给一个初始值叫做初始化,创建好变量后再给一个值叫做赋值。
int a = 100;//初始化a = 200;//赋值,使用的就是赋值操作符
所以=就是赋值操作符。
当然,也可以进行连续赋值,如:
int a = 1;int b = 2;int c = 0;c = b = a+3;//连续赋值,从右向左依次赋值
C语言支持这种写法,但是这种代码不易理解,所以建议拆开写。
int a = 5;int b = 9;int c = 0;b = a + 3;c = b;
这种就很容易观察细节了。
复合赋值符
写代码的时候我们经常会对一个数进行自增自减的操作,如下
int a = 0;int b = 0;a = a+3;b = b-2;
C语言中提供了更加方便的写法
int a = 18;a += 4;
代码结果是a=22,同理,还有其他复合赋值符
如: += -= *= /= %=,这些运算思路和+=是一样的
当然,还有这些复合赋值符,如 >>= <<= &= |= ^=,后面进行介绍。
单目/双目/三目操作符
单双三的意思就是有几个运算符,如+ - / %需要两个运算符,就代表这些操作符是双目操作符。
单目操作符:++ -- + -
欸刚才不是说+ - 是双目吗,怎么变成单目了,因为这里的+ -是表示正负号的意思。
现在对++ -- 进行介绍。
++分为前置++和后置++,且++是一种自增的操作符,具体如下代码
#include <stdio.h>int main(){int a = 10;int b = a++;int c = ++a;printf("%d %d %d", a, b, c);return 0;}
它们的区别在于是先使用在自增还是先自增在使用,巧记的方法是谁在前谁就先使用,
前置++,++在前所以a先自增,在进行使用,后置++同理可得。那么运行结果就比较明显了。
如图:
--的作用方式是一样的,就不进行阐述了。
三目操作符,即是有三个操作数的,在C语言中有且仅有一个三目操作符,
实现代码是 (exp1 ? exp2:exp3)exp是表达式的意思。
先执行表达式1,如果结果为真,那么就执行表达式2,如果结果为假,那么就执行表达式3。
看一段比较简单的代码:
#include <stdio.h>int main(){int a = 520;int b = 1314;int c = (a > b ? a : b);printf("%d", c);return 0;}
因为a<b,所以执行表达式3,即c的值是1314。
这些操作符就介绍完了,其他的操作符在之后进行介绍。
4
在操作符中还有一种特殊的操作符是强制类型转换,语法形式很简单,具体实现如下:
int a = 3.14;//a是int类型,3.14是double类型,两边类型不一致,编译器会报警告
int a = (int)3.14;//意思就是把3.14强制类型转换为int类型,计算机读取的时候只会读取整数部分
但强扭的瓜不甜,不到万不得已的时候不要使用,不然可能导致精度的丢失。
5
printf详解
printf的基本用法是把参数文本输出到屏幕,print是打印的意思,f代表格式化,表示可以定制输出文本的格式。
#include <stdio.h>int main(){ printf("Hello C"); return 0;}
上述代码会在屏幕打印Hello C
但是printf不会自动添加换行符,为了让光标移到下一行的开头可以手动添加换号符,如果文本内部有换行,也可以在内部插入换行符,如图:
#include <stdio.h>int main(){ printf("Hello\nC"); printf("Hello\n"); printf("C\n"); return 0;}
这些是最最基本的用法。
那么,现在引用占位符的概念;
所谓占位符,就是这个位置可以用其他值带入,如图
#include <stdio.h>int main(){int a = 1314;printf("%d\n", 520);printf("%d\n", a);return 0;}
占位符的第一个字符统一为%,第二个字符表示的是占位符的类型,如%d表示的是带入的值必须是一个整数。
输出文本里可以有多个占位符,占位符与参数的关系是一一对应的,如果用%c输出整型,那么编译器就会报错。
如果有n个占位符,printf的参数就应该由n+1个参数,如果参数个数小于对应的占位符,printf可能会输出内存中的任意值。
那么,常见的占位符有这些。
• %a :十六进制浮点数,字母输出为小写。
• %A :十六进制浮点数,字母输出为大写。
• %c :字符。
• %d :十进制整数。
• %e :使用科学计数法的浮点数,指数部分的 e 为小写。
• %E :使用科学计数法的浮点数,指数部分的 E 为大写。
• %i :整数,基本等同于 %d 。
• %f :小数(包含 float 类型和 double 类型)。
• %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 类型。
• %lx :十六进制 long int 类型。
• %lu :unsigned long int 类型。
• %lld :十进制 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 类型。
• %% :输出⼀个百分号。
当然,不是所有的都常用,随着代码数的增加慢慢就了解了。
占位符有了一定了解,那么介绍printf()可以定制占位符的输出格式。
1
printf允许限定占位符的最小宽度
#include <stdio.h>int main(){printf("%5d", 123);return 0;}
%5d表示这个占位符的宽度至少为5位,运行结果如图
可以看到,123前面由两个空格,当然宽度值至少为5位,那么改成%2d就不会出现前面由空格的情况
输出的默认值是右对齐,如果想要左对齐,只需要在%后面加一个-号就行。
而对于小数而言,这个限定符会限制所有数字的最小显示宽度。
如图
由于小数的默认精度是小数点后6位,所以前面有4个空格。
2
总是默认显示正负号
默认情况下,printf()不对整数显示+号,只对负数显示-号,如果想要在正数前面输出+号。可以在%后面加个+。
3
限定小数位数
有时候觉得小数点后6位太长了,占位符也可以对其进行限制,如图
所以限定的方式是%后面加.n(n是限制数字)。
当然了,这种写法可以结合限定宽度占位符使用,如图
当当当然了,最小宽度和小数位数这两个限定值都可以通过*代替,通过printf()的参数传入,如图
图中的两个*号就是由参数6 2传入的。
4
输出部分字符串
%s是用来输出字符串的,默认是全部输出,如果只想输出部分内容,可以通过%.[n]s指定输出其中的长度,其中[n]代表一个数字,表示要输出的长度。具体的代码实现:
以上就是printf()函数的较为详细的介绍。
6.1
scanf详解
1
基本用法
scanf同printf一样,原型定义在头文件stdio.h。
scanf用于读取用户的键盘输入,它的第一个参数是一个格式字符串,里面放置的占位符与printf基本一致,而要输入一个数,scanf就必须提前知道用户输入的数据类型,才能进行输入。
它的其他参数就是存放用户输入的变量,有多少个占位符就有多少个变量。
注意:变量前面必须加&(指针变量除外),这个符号是取地址符号,因为scanf传递的是地址,而不是值,即变量的地址指向用户输入的值。
先看两个错误例子
一个是没有先定义,一个是没有加&符号。
所以编译器都进行了报错。
scanf()处理数值占位符的时候,会自动过滤空白字符,包括空格,制表符,换行符等。
如图展示
scanf() 处理用户输入的原理是,用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存 进行解读。
解读用户输入时,会从上⼀次解读遗留的第⼀个字符开始,直到读完缓存,或者遇到第⼀个不符合条件的字符为止。
怎么理解呢?看代码:
空格全部忽略,读取到-13,发现有个点,就不是整型,就跳过,由于遗留的是. ,对应是%f,所以会读取到.45e,%不属于浮点型的有效字符,所以会停在这里。
当然,scanf也可以像printf一样,拥有多个占位符。
2
scanf的返回值
scanf()的返回值是一个整数,表示成功读取变量的个数,如果没有读取到任何项,或者匹配失败,就会返回0。
如果在成功读取到任何数据前,发生了读取错误或者遇到读取文件结尾,就返回常量EOF。
那么,现在进行测试。
这是正常输入的情况
那么,我如果提前停止输入呢?
VS中按3次ctrl+z才结束了输入,可以看到r=2。
如果一个都不输入,输出r的值就是-1,也就是EOF的值。
3
占位符
scanf()常用的占位符如下:
• %c :字符。
• %d :整数。
• %f : float 类型浮点数。
• %lf : double 类型浮点数。
• %Lf : long double 类型浮点数。
• %s :字符串。
• %[ ] :在方括号中指定⼀组匹配的字符(比如 %[0-9] ),遇到不在集合之中的字符,匹配将会 停⽌。
上⾯所有占位符之中,除了 %c 以外,都会自动忽略起首的空白字符。 %c 不忽略空白字符,总是返回当前第⼀个字符,无论该字符是否为空格。
如果要强制跳过字符前的空白字符,可以写成scanf(" %c", &ch),%前面加上一个空格,表示跳过0个或者多个空白字符。
需要特别强调的是%s
它的作用规则是
从当前第⼀个非空白字符开始读起,直到遇到空白字符(即空格、换⾏符、制表符等)为止。
即%s不会包含空白字符,所以无法用来读取多个单词,另外,scanf()遇到%s会在字符串变量末尾存储字符0。
scanf() 将字符串读⼊字符数组时,不会检测字符串是否超过了数组长度。所以,储存字符串时, 很可能会超过数组的边界,导致预想不到的结果。为了防⽌这种情况,使用%s 占位符时,应该指定读⼊字符串的最长长度,即写成%[m]s ,其中的 [m] 是⼀个整数,表示读取字符串的最大长度,后面的字符将被丢弃。
这样就没有数组溢出的风险了。
4
赋值忽略符
有时候输入的可能不符合格式。
如果用户输入2020/01/01,那么scanf()就会解析失败,为了避免,scanf()提供了赋值忽略符,只要把*加在任意占位符的%后面,该占位符就不会返回值。
如图
scanf()介绍结束。
感谢阅读!