目录
引言:
本篇讲述的是C语言基础的一些概念以及使用时的注意事项和细节,当然如果基础的一些在想要执行某些操作时有限制,也会进行一定的拓展。该篇除了指针和数组应该都会讲,然后下一篇会用这些最简单的知识做一个猜数字的小游戏?(应该)当然,那些最常用的一些我不会讲格式怎么样,而是讲使用时候的逻辑关系和注意事项

略提一嘴,在讲解其中操作符和函数等使用的注意事项时我使用的编译器是VS2022的,我比较推荐这个,如果想知道VS如何下载和安装可跳转到这个链接集成开发环境VS2022的安装及简单运用及附加插件_vs2022 安装的扩展如何使用-优快云博客

那接下来就进入正文——————>
一.C语言常见概念
首先,C语言是一门编译型语言,那何为编译型语言呢,就是程序在运行之前,会有一个专门的编译过程,所以写出来的C语言代码是无法直接运行的,它需要经过编译和链接生成可执行程序才能运行。
关键字
C语言有32个常用的关键字,这边列举一下,break case char const continue default do double else float int long if goto for return short sizeof static struct switch typedef void while signed unsigned enum extern register(寄存器) union auto volatile,而在C99之后,又加入了 inline restrict _Bool _Complex _Imaginary等关键字
注:若是初学者的话现在不需要知道这么多关键字,只要知道加粗的这些关键字即可
关键字不能自己创建!!
转义字符
接下来,我们来讲一下转义字符
\n 换行,不必多说
\? 在书写连续多个问号时使用,防止他们被解析成三字母词(现在基本遇不到,除非遇到问题)
那什么是三字母词呢?
俩个 ? 加上一个 ( 或是 ) 即使三字母词,编译器这个三字母词变中括号,导致输出时想要输出 "??)" 结果输出的是 "]" (一般来说是不会遇到的,遇到这种情况在 ? 前加个 \ 即可)

\' 打印单引号
\" 打印双引号
\\ 打印斜杠
这三个转义字符使用起来基本一样,若想打印出来这三个符号都要在前面加\
除了 \' ,若想要打印的 ' 在格式字符串里,则可以不加 \ 也能打印出 '
第一个 " " 那一块就是格式字符串,占位符也是写在格式字符串里面的,关于printf在后面会具体讲,就不展开了
\a 警报,这会使终端发出警报声或闪烁,或俩者同时发生,那警报声是什么呢,就是跟点开某些应用时问你是否允许应用进行更改时候发出的声音一样
\b 光标回退一个字符,但不删除字符
就比如若是寻常输出这段代码,光标会在最后一个字符后面,而加上\b,运行完后光标就会最后一个字符前面(如下图,应该十分明显了)


注:2图虽然调试时候是abcde,光标在e前面,但最终输出时候输出的是abcd,这种情况是输出台有输出空格然后覆盖了(可以简单理解成光标后面有字符就强转为空格),只需要在最后加个\n换行把光标移到下一行去就好了


因为光标是回退一个字符,并不会删除字符,所以,若\b之后还有字符,就会使后面的字符覆盖掉前面的字符,用图来表示应该更能理解,我们将2图中的\b移前,看输出的变化
通过图片,不难看出,一开始打印了abc,但因为\b使光标往前移动了一位,导致后面打印的d将c覆盖,最后打印出了abde
\f 换页符号(现在基本用不到了,就不过多叙述了)
\r 光标回到该行的开头,这个与\b很像,只是\b是光标回退一位,但\r是直接回到该行的开头

最终输出时候结果为“ bcde”而不是“abcde”的情况跟\b问题一样,解决这种情况只需要在末尾加个\n就可以了,如图


当然,若\r后面还有字符,跟\b一样,也会随着光标进行覆盖,只是\r是从头开始覆盖,这里就不再演示了
\t 制表符,光标移动到下一个水平制表位,通常是下一个8的倍数的宽度

就以这个为例,第一个1到7的\t令宽度为8,第二个1到9的\t就令宽度为16
\v 垂直制表符(基本不用,就不细讲了)
接下来俩种转义字符可理解为字符的八进制或十六进制表示形式(比较重要)
\ddd d d d表示1-3个八进制的数字。如\130的ASCII值为88,对应字符X
\xdd d d表示2个十六进制数字,如:\x30表示字符0
注:\0 也是 \ddd 形式的转义字符,另外这俩种转义字符转化成十进制后的值的值要在ASCII码的范围内
转义字符在计算长度时算一个字符,在算字符串长度时,遇到 \0 就停止,如图,因为遇到的\0,所以最终输出结果为5(不要把\0和\0几或者\0几几搞混了,要注意\0后面没有数字的才算\0)

既然提到\0了,那就提一嘴字符串,\0是字符串的结束标志
如果是用双引号给字符串赋值,那么会在末尾自动加 '\0',但若用的是单引号赋值,那么不会再末尾自动加 '\0' ,要自己手动添加,请看下图,用双引号赋值完的字符串算长度时值未出错,若用单引号赋值完的字符串返回长度时会出错,因为他会接着往下走,直到遇到\0为止。若是放在全局区进行单引号赋值,也不会有问题(因为全局会初始化为全0),但还是建议若有单引号赋值,末尾加上一个 '\0'
语句和语句分类
C语言代码是由一条条语句构成的,其中语句可分为以下五类
1.空语句
空语句是最简单的,一个 ; 便是一条语句
出现地方:这里需要一个语句,但这个语句不用做任何事,就可以写一个空语句(虽然我没用过这语句)
2.表达式语句
就是在表达式后面加上 ; 的语句
3.函数调用语句
调用函数的语句
4.复合语句
成对的大括号中的代码就称为复合语句,也可叫做代码块,如main()函数里的所有语句就是一个代码块,也称为复合语句
5.控制语句
控制程序的执行流程,用来实现程序的各种结构方式(C语言目前支持三种结构:顺序结构,选择结构和循环结构)
C语言有9中控制语句,可分为以下三类:
条件判断语句(又叫分支语句):if语句,switch语句
循环执行语句:do while语句,while语句,for语句
转向语句:break语句,continue语句,return语句,goto语句(这个语句尽量少用,后面会讲原因)
注释
注释你们应该都不陌生,C语言有俩种注释方式,一种是/* */,另一种是//,//是C99新增的。但/* */不支持嵌套。
注:注释会被替换:编译时,注释会被替换成一个空格,不要乱用注释,如图

注:不管是哪种注释,都不可以放在双引号里,都会被视为普通字符,没事注释作用
C语言常见概念就到此为止啦,那接下来就开始讲C语言的数据类型和变量啦

二.数据类型和变量
变量
创建时若是全局变量可不初始化,但局部变量建议初始化,因为全局变量若不赋初值会默认初始化为0 ,但局部变量不初始化会默认随机值。
局部变量,函数参数 放在栈区(临时使用),全局变量,静态变量 放在静态区(长期使用),堆区是用来动态内存管理(有malloc,calloc,realloc,free等函数)的
在创建变量时,除char外其他变量一般都会默认为signed,只有想让该类型变成无符号型时候在类型前加个unsigned即可,但char不同,char在创建时不会默认为signed,char是signed char还是unsigned char取决于编译器。
注:不允许在同一个作用域中定义多个相同名称的变量,但允许在不同的作用域中定义多个相同名称的变量。且不同作用域中定义的变量,在访问时采用就近原则,如图
数据类型
数据类型分为内置类型和自定义类型
内置类型(C自带的)有:字符型,整型,浮点型(float,double,long double(C99引入的)),布尔类型(_Bool或bool)
注:bool 和 char 是一个字节,long double 跟 double 都是八个字节
自定义类型(自己可以设计的)有:数组,结构体(struct),枚举(enum),联合体(union)
如果要查看当前系统上不同数据类型的极限值:
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的最大值
算 类型/变量 长度的sizeof()
sizeof(类型);
sizeof 表达式;
如果()里是表达式或是变量的名字,可以去掉括号,若是数据类型,则不能去掉括号,如图
sizeof 表达式 sizeof后面的表达式不会真实参与运算,即不执行,根据表达式的类型来得出长度大小。例如该代码

为什么用 %zd 打印sizeof的返回值(不用也没事,只是编译器会报警告):
sizeof运算符的返回值,C语言只规定是无符号整数,并没有规定具体的类型,而是留给系统自己去决定,这样不利于程序的可移植性,于是C语言提供了一个解决方法,创造了一个类型别名size_t,用来统一标识sizeof的返回值类型,对应当前系统的sizeof的返回值类型
注:sizeof既是操作符,又是关键字,但不是函数
sizeof的计算结果是size_t(C语言中能够直接使用的)类型的
操作符
算数操作符
算术操作符都是双目操作符
/俩边都是整型得到的的是整数,若有浮点型,则得到浮点型,如图

用%f打印时候小数点后默认是打印6位,想控制打印几位就在f前加.x,x为保留位数(会四舍五入),%lf同理,如图
注:若浮点数用%d打印,会发生截断(不四舍五入),只剩下整数部分

%只能用于整数
负数求模时,结果的正负号由第一个运算数的正负号决定,如图

赋值操作符
这个没有太多要讲的,只需要知道连续复制的时候是从右向左赋值的即可,如图,现将a赋值给b,再将b赋值给c

这里还有一个小细节,留个印象就行,在变量创建的时候给一个初始值叫初始化(不是赋值),后对变量的值进行更改才叫赋值
单目操作符
++,--,+(对值没影响,完全能省略),-(将变量变号)
++若放在变量后面,先执行完该行再自增
++若放在变量前面,先自增再执行该行
--同理
如图,在进行c的运算时,因为++在a后面,所以先运算,再自增,最终使得c为0(注:平时代码不要这么写,这里只是举个例子)
关系操作符
关系操作符,顾名思义,就是用来比较大小关系的
关系表达式若为真返回1,若为假返回0
注:==不要误写成=,可以用一种方式避免这种错误,常量放左边,变量放右边
==不能比较字符串是否一样,比较字符串是否一样用strcmp函数
strcmp(字符串1,字符串2);
若strcmp函数返回值>0表示第一个字符串大于第二个字符串,=0表示俩个字符串相等,<0表示第一个字符串小于第二个字符串,如图

另一个需要避免的错误是:多个关系运算符连用没语法错误,但使用是错误的,因为判断时,他会从左往右判断,若为真会返回1后与后面进行判断,若为假会返回0后与后面判断,举个例子就很明了了,如图,很明显,程序并未走进if语句,那是为什么呢,因为连用后,程序会先判断a>b,得到结果为真,返回1,然后1与c进行比较,为假,所以得到判断结果为假,但我们实际想表达的肯定是满足a>b>c然后进入if语句。所以,这种使用时错误的

条件操作符
又称为三目操作符,C语言中仅有一个三目操作符,即
语句1:?语句2:语句3;(多用于替换if else语句)
若语句1为真,则执行语句2,若语句1为假,则执行语句3
逻辑操作符
C语言逻辑运算符有一个特点,总是先对左边的表达式求值,再对右边的表达式求值,这个顺序是保证的,中途断了,右边不算了,这种情况为“短路”
如何短路:&&运算符遇到假就结束了,||遇到真就结束了
这种仅根据左操作数结果就知道整个表达式结果,不再进行右操作数运算的运算称为短路求值
拓展:逗号操作符
用于将两个或多个表达式连接起来,并按从左到右的顺序依次执行
如图,先执行完b=++c,后执行c++,后执行++a,最后执行a++

当然,跟这个类似的还有逗号表达式,跟这个区别就是逗号表达式要用括号包起来,然后()里的表达式从左向右依次计算,但整个表达式的结果是最后一个表达式的结果,如下图,同样的代码用()括起来变成逗号表达式后,b接受的就是a++的结果,即6,因为a++是先执行再++

scanf函数
先提一嘴,若是VS,用scanf前要在前面加上这行:
#define _CRT_SECURE_NO_WARNINGS
VS支持的是scanf_s,不支持scanf,因为scanf不安全,为什么不安全呢,因为他不会检测你的输入是否有问题(例如字符串数组你只给了10个空间,但你输入了100个长度,他不会管放不放得下,以至于程序出问题)所以用scanf时要注意输入的范围。因为scanf_s只有VS能用,所以这里讲所有编译器都通用的且更常见的scanf
基本用法
scanf处理数字占位符时,会自动过滤空白字符,包括空格,制表符,换行符等
输入时可以用科学计数法输入:如3e4这种,如图

注:若用科学计数法输入,必须要是浮点型
scanf处理用户输入的原理
先将用户的输入放入缓存(可以把他先当做一个临时的区域),等按下回车键后,按照占位符对缓存进行解读。解读用户输入时,会从上一次解读遗留的第一个字符开始,直到读完缓存,或者遇到第一个不符合条件的字符为止
若不理解可以看下边的代码进行理解,输入为"-13.212e13# 0 ",全部放入缓存,遇到 ' . ' 时不符合整型条件,结束读入,接着输入y,从 ' . '开始解读,因为' . '也属于浮点型的一部分,就接着往后读,直到读到 ' # ',不符合条件,结束读入(e是科学计数法的表示法表示10的几次),看最后输出结果即可

注:小数有时候在内存中无法精确保存,所以有时值会近似相等但不绝对相等,所以若是你们输出的值略有差距也是正常的,举个例子来说就是读入的是0.212,但内存里面的小数可能是0.211789,四舍五入才变成的0.212,所以导致给他乘10的几次时,会导致数值略有差异
那么细心的人可能发现了,为什么scanf下面会有绿波浪呢,相信很多人打代码时候也会出现这个绿波浪线,那么是为什么的,其实很简单,就是scanf其实是有返回值的,但我们并没有接收他的返回值,所以报警告了,这个在下面细讲。
scanf的返回值
scanf的返回值是一个整数,表示成功读取的变量个数
如果没有读取任何项,或者匹配失败了,则返回0
如果在成功读取任何数据之前,发生了读取错误或者遇到读取到文件结尾,返回常量EOF(即-1),EOF又称为文件结束的标志
那知道了这些,那如果我们打算法时遇到有些题目要求一直输入,想停止就停止时就可以把scanf放入while语句中,若返回值为0或EOF,就跳出循环即可,具体如图(因为图示中输入的是一个数据,而scanf的返回值是成功读取的变量个数,所以只要判断是否等于1即可)

占位符
不常见的占位符:%[]:在方括号中指定一组匹配的字符(比如%[0-9]),遇到不在集合中的字符,匹配将停止
注:%[]这个占位符只能用于字符串数组,若[]里面写的是数字就是匹配的是几到几的数,如果写的是字符,就是匹配几到几的字符,如图
这是匹配数字范围

这是匹配字母范围

在所有的占位符中,除了%c以外,都会自动忽略起首的空白字符,%c不忽略空白字符,总是返回当前第一个字符,无论该字符是否为空白字符,如图

如果想要强制跳过字符前的空白字符,可以写成 " %c",这样即可跳过字符前的任意个空白字符,如图

默认的%s不会包含空白字符,所以无法用来读取多个单词,这也意味着,scanf()不适合读取可能包含空格的字符串。另外,scanf()遇到%s占位符,会在字符串变量末尾存储一个空字符\0
若想输入带空格的字符串,用gets或fgets(另外,getchar是读取一个字符)
gets(字符串名);
如图

fgets(字符串名,sizeof(字符串名),stdin);
如图

如果实在想用scanf输出,可以用"%[^\n]",这个的用途是遇到\n结束,空格可读,如图

%[m]s,其中[m]是一个整数,表示读取字符串的最大长度,后面的字符将被丢弃,如图

注:当字符串数组大小为x时,该数组最多放x-1个字符,因为末尾要放\0(这也占一个位置),如果超过这个数量,数组就存在越界情况了
赋值忽略符
讲到这里,基本已经解决绝大部分的scanf问题和注意事项了,但是scanf输入的时候要按着格式输入,会很难受,那有解决办法吗,有的,那就是赋值忽略符(*)
一般输入时一定要按scanf的格式来输入,但不想按格式输入时候时可以用赋值忽略符,可以让输入格式更灵活
只要把*加在任何占位符的百分号后面,该占位符就不会返回值,解析后将被丢弃,如图

注:^Z为强行结束输入
printf函数
输出时,参数个数比占位符个数多一个,一开始的 " " 那一串也是参数,叫格式字符串

占位符
除常用的%c,%s,%d,%ld,%f,%lf外的一些比较常用的占位符:
%e:用科学计数法的形式打印浮点数,指数部分的e为小写
%E:用科学计数法的形式打印浮点数,指数部分的E为小写
%hd:十进制short类型(用%d其实也可以,我输出短整型是用%d的)
%ho:八进制short类型
%hx:十六进制short类型
%hu:unsigned short类型
%ld:十进制long类型
%lo:八进制long类型
%lx:十六进制long类型
%lu:unsigned long类型
long long类型跟long类似,只要多加个l就好了
总的来说就是 十进制d,八进制o,十六进制x,无符号u(这个记住就可以了,具体类型只需要在前面加对应字母就好了,short加h,long加l,long long加ll)
%p:打印地址(指针)
%Lf:long double类型
%zd:size_t类型(如sizeof的返回值)
输出格式
printf()可以定制占位符的输出格式
限定宽度
printf()可以限定占位符的最小宽度
可在%和类型中间加数字,表示限定宽度为多少(输出为右对齐,左边不足补空格,若长度超过最小宽度,就原样输出)如图

注:若想要左对齐,在数字前加个 - 即可,如图

注:对于小数,这个限定符会限制所有数字的最小显示宽度(小数点也算一位),如图

总是显示正负号
默认输出只会显示-,不会显示+,若想让他显示+,只需在输出格式前加一个+即可,此时不管是整数还是负数都会加上正负号,如图

限定小数位数
在数据类型前加.x即可,x为想要保留几位小数(会四舍五入),如图

注:限定小数位数可以和限定宽度占位符结合使用(要先写宽度再写限定小数位数位数)(有俩种方法,一种为直接写,一种为 "%*.*数据类型",第一个*,第二个*,数据类型)
第一种方法

第二种方法(对比第一种来说更方便,因为宽度和保留几位可以用变量控制也可以直接输数字)


输出部分字符串
也跟限定小数位数一样,写成 .xs 即可,如图

那么,这块的知识也讲完啦,接下来要讲的就是分支和循环方面的啦,是不是很简单

三.分支和循环
分支语句
if语句
这个没有什么需要注意的,只需要知道else总是和他最接近的if进行匹配就可以了
switch语句
当遇到用于判断条件有多个结果的情况时,它把多重的lese if改成更易用,可读性更好的形式,即switch
switch也可以嵌套
注:switch后必须是整型表达式,case后必须是整型常量表达式(字符也可以,因为存的时候存的是ASCII值)
case决定入口,如果想执行完就跳出去要加break语句(不用括号括起来,若没遇到break他会一直执行下去)
注:在switch语句中case语句和default语句是没有顺序要求的,只要你的顺序是满足实际需求的就可以,不过我们通常把default子句放在最后处理
循环语句
在具体讲循环前,先强调一点,就是循环语句末尾不要加 " ; ",在句末加 ";" 的话,就相当于循环里面执行的是空语句,如图,这俩个是等价的,相当于每次都在执行空语句

while循环
这个没有什么好强调的点,就不说啦
for循环
这个只需要知道for循环语句的三个表达式都可写可不写即可
有些说法说,for语句中第三个表达式,++i比i++高效,其实C语言中不存在,在C++中,虽然内置类型(语言本身自带的类型)上也不存在,但自定义类型上确实++i的效率高于i++,毕竟C++有重载,但这不在这片范畴,就不细讲了,记住加粗的那块即可
do while循环
随后便是do while循环了,因为在循环语句中,do while循环是最少使用的,以防忘记就将它的语法写一下
do
语句;
while(表达式);(要有;)
特点是先执行,后判断,不管是否满足条件,都会进入其中走一遭后再判断
死循环时怎么办
相信不少人肯定会遇到执行程序时候死循环了,这时候可以按Ctrl+C中断程序,让程序异常终止,当然也可以直接差点编译框结束进程
goto语句
这个语句能不用就尽量不用,因为会打乱程序的流程
语法是这样的
XXX:
....
goto XXX;
在多层循环的代码中,想快速跳出使用goto就非常的方便了(除此之外尽量能不用就不用)
注:goto语句只能在函数内部进行跳转不能跨越函数进行跳转
结语:
希望以上内容对你有所帮助,感谢观看,若觉得写的还可以,可以分享给朋友一起来看哦,毕竟一起进步更有动力嘛,数组和指针就之后再专门开俩篇或者一篇来讲啦


被折叠的 条评论
为什么被折叠?



