ps:无需做太多准备,您只需要准备好您的编译器,时看时操作就好,这很重要。
目录
1.初识C语言
什么是C语言(此处简单讲下历史和背景,可跳到下小节)
所谓C语言,是一门沟通的基础语言,就像人和人之间交流用汉语或英语等,C语言就是人和计算机交流的基础语言,属于上千计算机语言的一种,但因应用广泛而家喻户晓,地位常年不倒。
我们在使用电脑时,是用不同的操作系统(windows,Linux等)下载应用软件,通过驱动层,与硬件(电脑)建立联系。而我们要学习的C语言,是基于应用软件为上层,其余为下层的下层广泛开发(底层开发)。以下详见图解(欢迎讨论区讨论)
计算机语言发展:二进制指令->汇编指令->B语言->C语言(由低级到高级)
防止C语言被用坏掉,美国设置了国际标准,简称ANSI C,于是衍生出C89,C90,C99,C11。但前两者使用广些。
编译器和编辑器:前者可简单理解为处理代码,后者则处理文字。本人使用编译器是MSVC隶下的VS2022(如果需要安装教程评论区可以提出)。
第一个C语言程序
从这里开始就要正式讲代码啦,我用的vs2022,听者稍安勿躁,让我给您娓娓道来。
一、
1.首先让我们来打开软件->2.创建新项目->3.选择空项目,下一步,然后给自己的第一个项目起一个华丽的名字吧!注意这里,最好不要默认路径,代码是我们的武器,文件夹是我们的武器架,路径就是我们放武器的位置!如果哪天“打仗”了,你找不到自己的武器,那不就寄了嘛->4.直接上图
选择C++文件,继续起个小名字叭(注意末尾处.c这是说明咱们要写C语言代码),这里的位置就不推荐改了,然后就可以写代码啦。
二、
#include <stdio.h>
int main()
{
printf("hehe");
return 0;
}
首先我们来看这样一段小代码
1.
main:他是一个函数,叫主函数,我们的编译器在编译代码时就是从这里下手的,可以理解为头部,所以不可以有两个。
int:他是一个整形的声明,这里表示他是个什么样的头,这个详细后面说。
{}:大括号,表示这个函数内容,可以理解为身体。
return 0;:一个函数的结束标志(也可以没有,分情况),表示返回值是0,他就像一个潜规则一样,我们在主函数里的返回值都是0,你当然可以无视潜规则,写其他的数也可以,理解为脚脚。
2.
printf:一个最常用的输出函数,用法如图所示,在他后面放上(),然后在里面打上“”就可以了。这里要注意一点,代码里所有的符号都是英文,中文的达咩,这里是拿了别人的东西呦,卖个关子。
3.
;:分号他有什么用嘞,看似不起眼实则很重要,每个语句后面都需要有一个分号。
#include:他就像我们的手一样,拿来吧你!是用来声明你要使用的库函数,上面的printf就是stdio里的,白嫖也是要动手的,用法就是在后面加上<>,。
stdio.h:就是我们的小金主了,后面我们会频繁的用到他,所以请记住他,使用库函数时请在后面加上.h,以来表示自己诚恳的态度,否则人家不给你用。
最后,我们按下ctri+f5,就会有一个hehe打印出来了。
小节总结:写代码个人觉得就是捏小人的过程,所以请善待你每一个代码,回收末尾。
数据类型
什么是数据类型呢,因为我们捏的小人要去帮我们完成不同的事情,而生活的事情哪是1和2就能解决的,所以就有了不同的数据类型。
int:整形=整数,大小是4个字节 |
char:字符型=字母,大小是1个字节 |
double:双精点度=小数(更精准),大小是8个字节 |
float:单精点度=小数,大小是4个字节 |
short:短整形=整数(小点),大小是2个字节 |
long:长整形=整数(长点),大小是4个字节 |
long long:长长整形(更长),大小是8个字节 |
好了,那这些有什么用呢,上面已经说过了,适应环境的,那每个字节又是什么意思的,他的大小概念又是怎么样的呢,往下看。
bit(比特位)->byte(字节)->kb->mb->gb->tb->pb(由小到大)
这又是什么意思呢,已知一字节等于八比特位,一kb等于1024字节,一mb等于1024kb,依次往后都是1024。啊可能就又要问了,这又有什么用呢,昂,我很欣赏刨根问底,简单来说就是节约空间,避免不必要的浪费,要不然500g的大表哥,300g的老头环,直呼受不了。
小节总结:去节约你的空间吧,他就在one piece。
变量和常量
顾名思义,会变的数,不会变的数,不会变的数如Π,e什么的,我们先说变量。
变量分为两种:局部变量 全局变量
第一种:以{ }为界限,相对于内部,外部叫全局变量,内部的叫局部变量。
第二种:以文件为参照,文件外定义的变量可做全局变量,需要对其前面加上extern进行引用(加在应用文件)
需要注意的一些点:1.局部变量和全局变量同时合理存在时,优先使用局部变量。2.存在定义两个相同的局部变量或者定义两个相同全局变量时无法编译。3.变量最好初始化(一般是0),否则是随机值。
常量有好多:
1.字面常量:比如3啊,‘a’啊,字符串什么的很简单明了,就像手抓饼里的烤肠一样,清清楚楚。
2.define定义的标识符常量:简单来说就是给一个符号定义一个值,什么值都可以并且不可改。需要注意的是他用在主函数上面(不是下面(滑稽))。
#define MAX 100
#define STR ”abcdef“
3.枚举常量:简单来讲就是把一些不能变的值整合到一起,如下
enum Color //咱们浅浅的写下三原色
{
RED,
BLUE,
GREEN
};
int main()
{
enum Color c = RED;
RED = 10; //这是错误的写法,简称err
return 0;
}
综合以上两者,就有了神奇的第三者:常变量,及两者为大成哈哈哈(bu shi)。那他怎么表达呢,我们正常定义一个整数不大都是int a=0;嘛,在前面加上 const ,那这个值就改不了了,也就是 const int a = 0;,这里啰嗦一下。如下代码。
int main()
{
int a=0; //一个变量的创建
a=10; //是可以这样改的,所以叫他变量
//int a= 10; 这样就不行,变量那里已经说过了
return 0;
}
咱就是想,一个变量变的不可变之后,是不是就是常量了,提前说,并不是。
这里需要借助数组的知识,先讲了。创建一个数组时,需要定义他的大小,就是1,2,3这些字面常量,既然我们加了const之后,他都不变了,是不是意味着它可以用于数组大小了,我来替大家实验一下,如图:
我们可以看到,出现了必须是常量的警报,那就显而易见了,他虽然不可修改,但他还是个常量,也就是说,就算某某换了身名牌,又花了天价去西巴国整容,但内在和气质还是无法改变,接受自己,改变现实!好,总的来说就是具有常属性的变量。
生命周期和作用域:
{ }内的变量为其作用域(局部变量所在的局部范围即为作用域),也可以说这约是他的生命周期,即不能出圈。
字符串、转义字符、注释
感谢你能看到这里,我总感觉的有点啰嗦了对于文章,但我毕竟不能做标题党嘛。你们对我第一篇引子的阅读量真的惊到我了,给了我很大动力,虽然不缺我一个博主,但大家给点建议或者喷一喷都可以嘛,这对我真的很重要。
一、字符串
我们知道字符用char来创建,那字符串呢,很抱歉,他并没有,他只能存于数组内,如下代码
char arr1[10]="abcdef";
char ch = 'a';
char arr2[10]={'a','b','c','d','e','f'};
他们分别对应字符串、字符、’字符串‘需要注意的是,我们使用第一种用法时,语法为我们自动在最后加上了一个’\0‘做为结束标志,(默默感谢语法大哥),他和最下面的区别就在于这个’\0‘,大家千万不要小瞧这个结束标志。
在这里介绍一个求字符串长度(元素个数)的函数:strlen,用法很简单,在后面加上括号,并且引用头文件<string.h>就可以了,动手的时间到了昂,综合目前我讲的,自己试试,这里给点提示。
printf("%d",strlen(arr1));
printf("%d",strlen(arr2));
好,第一个很容易理解,是六,那第二个呢,怎么会是个这样的数字。这就要看我上面说的了,strlen没有遇到对的’\0‘,所以找啊找,不找到誓不罢休,第一个他正确的找到了,第二个嘞,没有啊,那咋整,继续找呗,然后我们就不知道他在经历多少磨难后,终于找到了对的人,然后给我们返回了他的经历。
二、转义字符
\? | 防止多个问号出现时被解析成三字母词 |
\' | 表示字符常量’ |
\" | 表示字符串内双引号 |
\\ | 防止反斜杠被解释为转义字符 |
\a | 警告字符 |
\b | 退格符 |
\f | 进纸符 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ddd | 表示1-3个八进制数字 |
\xdd | 表示2个十六进制数字 |
我一个一个解释,??也是一个操作符,就是】,但是我就想打印??所以就如下
are you ok\?\?
就像这样,往下三个都是同样道理,我希望读者会或者学会举一反三,请自行理解(实在不会可以讨论区讨论),\a就有意思,他会让你的电脑响一下,嗡一声,挺有意思,剩下的就熟悉就可以了,实在想知道讨论区说一下,我往上加,最后两个要好好解释下
printf("%c","\130") //他会输出X,要自己具体操作哦
这是为什么呢,这就不得不说下ASCII表了,这个表请大家网上搜下,我不好拿。
我们先把八进制数字转换成十进制,就是1*8的二次方+3*8的一次方+0*8的零次方等于88,正好对应表里的X,所以就这么输出来了,剩下那个同理,这里浅说下%d是输出数字,%c输出字符,%p输出地址,%s输出字符串,%f是folat的输出形式,%lf是double的输出形式。
三、注释
大家也看到我上面写的代码块了,加上了//,这个就是注释,他后面的代码就直接失效了,可用于梳理思路啊,对复杂代码进行解释啊,方便别人看啊等等,特别是面试官!!!,方便别人,方便自己,还有一种写法就是/* */,如下
/*
printf(".....");
*/
//printf(".....");
就像这样,两种都是,各有各的好,大家自行使用。
选择语句
他有if,else if,else等咱们直接代码见真章
if()
{
}
else if()
{
}
else
{
}
括号里就是需要判断的条件,如果是真的,执行,否则跳过,这里可以通过字面理解,第一个没通过就看符不符合else if,要是都不符合就执行else,这就是选择语句。
循环语句
咱先了解俩,for和while
for( ; ; )
{
}
第一个空是定义初始值,第二个是执行条件,第三个是每次循环之后对自身的操作。好,点到为止,就说到这。
while()
{
}
while就简单啦,只要符合括号里的条件,就执行{}里的语句。
函数
在开始说函数前先说下scanf这个东西,他是一个读取键盘操作的函数,简单来说就是输入,用法如下
int a=0;
scanf("%d",&a);
这就是把一个我们自己想要的数赋给了a。这样,我们就能玩更多的好玩的啦!要注意的是,要是使用vs编译器的话会有警告,我们仔细看他的报错信息,意思是要我们加上_CRT_SECURE_NO_WARNINGS ,加上就好了。因为他对于vs是一个陌生的函数,就是不支持,需要使用vs自己的,根据他的提示,加上宏定义就好了,注意使用陌生函数一定要调查清楚
我们用的printf啊scanf啊都是人家写好的,所以,要怎样想要实现一个自己想要功能的函数呢,如下
int add(int x,int y)
{
int z=0;
z=x+y;
return z;
}
int main()
{
int n1=0;
int n2=0;
scanf("%d %d",&n1,&n2);
printf("%d",add(n1,n2));
}
这样,一个加法计算器就做好了,怎么去理解他呢,可以说是把原材料给工厂,工厂加工成产品,他们分别对应着n1,n2、自己的函数、z。add后面的括号就是对应输入的类型,完事!
数组
到数组了,回收前文。
数组:是一组相同元素的组合,下标从零开始,如arr[0]=1(根据下列数组),如下
int arr[10]={1,2,3,4};
int表示数组的类型,arr是数组的名字,[ ]里是表示数组的大小(这里可以不写入常量,由系统自己决定(根据元素个数),但必须给其赋值),{ }里是数组的元素,在调用时,可以用变量。
int arr[10]; //err
int arr[ ]={1,2,3,4}; //正确写法
char类型的数组就是不同的写法了,如下
char ch[11]="hello word";
这里考验下大家,ch这个数组里有几个字符......没错是10。至于原因想必我上边已经说过了,这里再说一遍,空格!也是字符。
操作符
算数操作符
+ | - | * | / | % |
顾名思义,加减乘除,我觉得这没什么好说的,就是乘和除符号不一样,使用时多加注意就可以了。值得一说的是%,他叫取模,就是取余数,比如7%3=1。
移位操作符
>> | << |
左移和右移操作符,因为他们涉及到二进制,所以暂时不讲,但是大家不用担心,时机到了我一定会说的。
位操作符
& | | | ^ |
他们分别是 按位与,按位异或,按位或,和上面一样,先馋你们哈哈哈。
赋值操作符
= | += | -= | *= | /= | &= | |= | ^= | >>= | <<= |
除了第一个之外,其他都是简便写法,比如+=就是a=a+1可以写成a+=a。其他以此类推。
第一个就是赋值了,这里再提醒一遍,=是赋值。==是判断。一定要记住=-=。
单目操作符
! | 逻辑反操作 |
+ | 正值 |
- | 负值 |
& | 取地址 |
sizeof | 操作数的类型长度(以字节为单位) |
~ | 对一个数的二进制按位取反 |
-- | 前置,后置自减 |
++ | 前置,后置自加 |
* | 间接访问操作符(解引用操作符) |
(类型) | 强制类型转换 |
我们一个一个了解,但只是点到为止,第一个!,就是反着来的意思,把真的变成假的,把假的变成真的。正值负值的用法和我们在数学里理解是一样的,不在赘述。取地址和解引用是指针知识,我们后续再讲。sizeof是操作符,不是函数,就是算大小的。这里告诉大家一个计算数组大小的一个小技巧》
sizeof(arr)/sizeof(arr[0])
--和++就是对于自身的操作,一般用于循环。需要注意的是前置的意思是先对自身操作在被使用,而后置的意思就是使用,在对自身操作,这里给大家两段代码自己冻手试试(主函数什么的自己写奥)。
int a=10;
int b=a--;
int c=a++;
int x=++a;
int y=--a;
在把他们分别输出,看看自己理解明白没有。~也是涉及到二进制,后续说。强制类型转换就是字面意思,把一个类型转换成另一个类型,虽然说强扭的瓜不甜,但是我就问你这瓜皮子是金子做的还是这瓜粒子是金子做的哈哈哈。用法如下
int a=(int)3.14;
这个时候,3.14这个小数就被扭成了int型,所以a=3。
关系操作符
>= | <= | > | < | != | == |
前面的都好理解,就是大于啊什么的,最后一个是判断,上面说过了,这里再提醒大家,千万不要用错了,==是判断符号,=是赋值。
逻辑操作符
&& | || |
逻辑与(并且)和逻辑或(或者):还是高中知识,两者都成立为真,后者是两者有一个真则整体为真。
条件操作符
exp1?exp2:exp3 |
三目操作符,是三目不是三木哈哈哈,意思就是有三个操作数,二目就是有俩操作数》就这个而言,exp1是一个判断表达式,如果是真,返回exp2,否则返回exp3,如下
a>b?a:b
意思就是a大于b吗?大于返回a,不大于返回b。
逗号表达式
废话不说,上代码!
int a=10;
int b=11;
int c=12;
int d=(c=a-2,b=b-1,c=c+1)
里面的每个表达式逐次计算,值是会发生变动的(要注意),最后返回最后一个表达式的值》
关键字
C语言自己的,不能被创造,使用比较方便,下面一一列举并浅解
和循环有关:for while (do while) break continue
和判断有关:if else switch case default
字符类型:int char double float long short......
其他:goto(请大家记住他,这玩意邪的很,又或许是把神器?)const(附加常属性,就是不能变了)extern(声明外部符号)register(寄存器)static(静态区)return(函数返回值)signed(有符号的)unsigned(无符号的)enum(枚举变量)struct(结构体)union(联合体)void(空,无的意思)sizeof(计算大小)typedef(类型重命名)
那么这些为什么在这里说呢,哪里用到哪里介绍不是更好?只是为了方便大家在命名自己想要的变量时,防止与这些冲突,产生不比必要的误会》
定义变量时,必须由数字,字母,下划线组成,希望大家的变量名有意义》
接下来讲一个好用的关键字:typedef
简单来讲就是一个简化工具(类型重命名),像这样
unsigned int num = 0;
我们每定义一个无符号整型变量就需要敲出一堆字母,但有了typedef,就可以这样
typedef unsigned int all;
int main()
{all i = 0;
return 0;}
只要在主函数前面定义了一下,那么长的无符号整形就简化成了all三个字母,贼方便代码山的使用》同样的,结构体也可以使用,像这样
typedef struct N
{
int date;
struct node* next;
}node; //node n1;
就由struct N n1 简化成了 node n1,我写的变量名比较简单,真正使用的时候不单单只是一个N了》这些就是typedef的用法了》
static
有三种用法:1.修饰局部变量 2.修饰全局变量 3.修饰函数
修饰局部变量时,该局部变量生命周期与程序同寿》static其实改变了局部变量的存储位置,首先我们要知道内存中我们一般分为三个区域用于储存不同的数据类型,画图更直观些》
修饰全局变量时,将会缩小该全局变量应用范围,减小其作用域》意思就是本文件外的全局变量在本文件引用一下,本文件也能用这个全局变量,但是经过该修饰后,这个全局变量就只能在这一个文件使用了,不能再被引用,大大的增加了他的安全性》这里再详细说下具体经过,全局变量是有外部链接属性的,编译时,通过该链接去执行该程序,static修饰时,外部链接属性变成了内部链接属性,所以其他源文件(.c)就不能再使用了》
修饰函数时,同上》
register-寄存器
电脑的存储设备:寄存器(一般kb)->高速缓存(一般mb)->内存(一般几十gb)->硬盘(一般上百gb),从右往左,造价逐渐变高,内存逐渐变小,运行速度逐渐变快》编译时编译器自己分配这些空间,而register是建议该数据放在寄存器中》
#define定义常量和宏
定义标识符常量:
#define NUM 100
printf("%d",NUM);
int arr[NUM]={0};
用法正常就像这样,轻轻松松定义了一个常量。
宏:
简单理解其实和自定义函数差不多,接下来看具体区别》
#include <stdio.h>
//宏
#define ADD(x,y) ((x)+(y))
//自定义函数
int ADD(int x,int y)
{
return x + y;
}
int main()
{
int a=0;
int b=0;
int c=ADD(a,b);
printf("%d",c);
return 0;
}
就像这样,好像宏更简便点》ADD是宏名,x,y是无类型参数,后面是宏体》
指针 危
首先不要慌,虽然是难点要点,但理解后还可以。
每个数据都有自己的地址,这时,有一个变量指向了该地址,这就是指针》咱简单理解就是这样》
接下来再细说,内存会划分为有数个内存单元(大小一个字节)每个内存单元都有一个编号(地址)》
(这里分开理解)在32位电脑上有32根地址线(不是废话),计算机辨别的是二进制,所以32根地址线就分别是0或1,也就是0到2的32次方个字节(00000000000000000000000000000000 00000000000000000000000000000001 ... 11111111111111111111111111111111)也就是4294967296个字节->4194304kb->4096mb->4gb,总共这些内存。(结束)
这里再说下调试,按f10进入调试,按f1进行逐语句调试(就是看清自己的程序每一步都在干什么),在调试(上方)->窗口->监视/内存(只有在调试的时候才有(f10))。可以看到自己的变量每时每刻的变化》这时就可以看到地址了》
int main()
{
int a = 10;
&a;
return 0;
}
代码放这里,大家自己动手试试(推荐)》
这里我们可以看到三列,他们代表的意思分别是地址(0x231ff223)、内存中的数据(34 66 34 98)、不准确的内存数据的解析(??!~)好,回来我们说存放10时的地址,int是四个字节,32个比特位,二进制就是00000000000000000000000000001010,转换成16进制,每四个一转换就是0000000a,也就是0x00 00 00 0a。
好,我们在有了这些概念后,地址也是可以存的,地址也是数值嘛》那他的类型是什么呢,如下
int* p = &a;
p就是指针变量,so存放地址的变量就是指针变量。注意,变量名是p,int*是类型。指针类型是int,*说明是指针》(指针变量就是存放地址,无论什么数值放进去)》
那他有什么用呢,知道一个人地址有什么用呢(嘿嘿,嘿嘿),用法如下
int a =10;
int* p=&a;
*p = 20;
//这个时候,a就是20了
如上所见,指针变量p在解引用(*)后,可以间接改掉存放在里面地址主人的值,这,就是指针的用法。
指针大小
这里不过多讲述,就记住32位平台大小是4,64位平台是8就可以了,如果真的需要评论就可以了,我通宵更哈哈哈。
结构体
真的有人能看到这里吗,真有的话麻烦点个赞鼓励下,我真的太感动了呜呜呜,初始C语言最后一节了奥里给xdm。
结构体就是把单一对象整合在一起的复杂类型,是一种自定义类型,叫struct》
struct stu
{
char name[10];
int age;
char sex[10];
char tele[10];
};
int main()
{
struct stu s = {"zhangsan",20,"nan","4387948"};
printf("%s %d %s %s",s.name,s.age,s.sex,s.tele);
return 0;
}
上面的,就是结构体的创建,主函数里,就是结构体的应用和输出》“.”这个点,就是对其引用》
这里还有另一种写法:
void print(struct stu* ps)
{
printf("%s %d %s %s",(*ps).name,(*ps).age,(*ps).sex,(*ps).tele);
printf("%s %d %s %s",ps->name,ps->age,ps->sex,ps->tele);
}
int main()
{
struct stu s = {"zhangsan",20,"nan","4387948"};
print(&s);
return 0;
}
这是传指针类型参数的写法,->,就是另一种写法,不同的符号,能说的挺少,大家多动手,多刷题就好了。
ok了萨布拉people,这一章终于完成啦!!!
总结:本章仅仅是一个大概,意为其搭建好一个框架,可浅读他人代码,后续详解每一点。其实我也是推荐大家自己冻手写博客的,好处大大的有,而且好像没什么坏处吧?还有就是gitee的注册和维护,用法b站和csdn里都有教程,大家有不明白的地方也可以私我,个人觉得还是很重要的。
鸽了自己这么长时间,属实是对不起了,愣是拖到开课,这一篇文章也是思考良久,删删改改,缝缝补补,后续会慢慢往上添题的。加油,又是充满bug的一天呢。在不给自己输入知识的期间会持续更新小训练哦。
2.分支语句和循环语句
前言:之前我们已经有了整体的概念,现在正式逐步详解。
既然谈这些那我们要先了解什么是语句,那语句分为五种》
1.表达式语句 2.函数调用语句 3.控制语句 4.复合语句 5.空语句》这里我们主说控制语句(用于程序流程的控制以实现程序的各种结构)》
首先C语言是结构化的程序设计语言,而结构化包含三种:顺序结构、选择结构、循环结构》都挺好好理解,第一个就是一条顺,选择就是出现分支了,可以选择了、循环就是一直转圈圈》生活何不可抽象理解成这样》
好,现在说第一个选择语句if
if(判断表达式)
{
//多语句时
}
else if(判断表达式) //可以存在多个
{
//多语句时
}
else
{
//多语句时
}
我来进行逐步解析,第一个判断表达式为真,执行if的结构体内语句,第一个判断表达式为假,判断第二个判断表达式真假,真则执行本结构体内语句,依次类推,不满足所有表达式情况时,直接执行else结构体内语句》
结构体内只有一个语句时(一个语句就是逻辑上只出现一个分号;),大括号{}可以省略》
该判断语句可以嵌套,但一个整体只执行其中一条》
除一般情况下(指判断语句对错) 非零为真,零为假》
如果想多次判断多次输出,可以有多个if,else可以不写》
写法一定要规范,if一定要与对应的else和else if对齐,关于规范这我会开坑,写一本经典的书自己的感悟,什么书就让他成悬念吧》
判断表达式要注意的是,如果是如下情况:1<a<10。并不能这么写,正确写法是a>1 && a<10》
规范这里再说一点,判断某个变量是不是等于一个常量的时候,我们一般是这么写e==3,这里,就可能有人把==写成=,所以我们换个顺序,这样,3==e,这种写法不仅能让编译器帮助你检查是否漏写(常量不能被赋值),还能让其他人认为你的代码更加nb》
第二个,多分枝switch
switch(整形表达式)
{
case1:
break;
case2:
break;
default:
break;
}
说到switch离不开case和break,根据整形表达式的返回值,寻找匹配的case语句(如果返回整形一,就执行case1(注意后面的是英文冒号,case后必须是整形(也可以是字符,但识别的是ASCII值))),
如果有,执行后面的语句,然后退出此循环(break的意思就是退出本次循环,执行动作为跳到大括号外,如果没有,后面的语句都会执行),
如果没有匹配的项,就执行default(如果没有,和case相匹配的项也没有,就会陷入死循环),然后退出》
下面开始说while
while(判断表达式)
{
;
}
判断表达式用处和上面一样,零为假,非零为真,真就一直执行结构体内语句,所以推荐其中有++或者--操作。这里再说一个指令:continue,如果编译器读到他,直接返回该指令循环首处,进入新一轮循环。
今天的最后再说一下getchar()和putchar()他们需要引用头文件<stdio.h>,getchar基本用于读取多余的字符,比如回车的\n...putchar就是输出,只不过比scanf和printf更加粗略一点(个人理解)》getchar的返回值是整形,所以需要用整形接受,并且就算读取字符,它也可以以ASCII形式录取并以此打印,也就是打印字符》
然后这里说下输入缓冲区,这也就是为什么getchar读什么putchar输出什么的原因,在执行该指令,也就是光标闪动的时候,这个指令和我们键盘之间的缓冲区就准备好工作了,她会录取键盘上输入的所有东西,包括/n也就是回车,所以在我们实际操作的时候就有了自动换行》
可是有的时候我们不需要自动换行怎么办呢,很简单,再加一个getchar()就好了,用他吸取多余的\n,可是这样写未免有些搓,但是我们这样:
while((int ch = getchar()) != EOF)
{
;
}
这也算另一种写法吧》
第三个:for,这个是使用率最高的函数,具体用法如下
for(表达式1;表达式2;表达式3)
{
}
for(i=1;i<=10;i++)
{
}
表达式1表示对变量的初始化,表达式2表示该循环的范围,表达式3表示每完成一次循环后的变量变化。用法如第二个,i=1,每次循环之后自身加1,加到11,也就是已经循环10次后,循环就结束了》
最后一个,do while,这个应用场景比较特殊(频率低),如下
do
{
}
while(判断表达式)
上来就是干!先执行do里的语句,然后根据判断表达式是否成立,再决定是否继续do里的循环》
3.函数和递归
1.函数是什么
其实和数学里差不多,说详细点就是,是一个大型程序中的某部分代码,由一个或多个语句块组成。它负则完成其项特定任务而目相较于其他代码,具各相对的独立性。一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
2.库函数
相当于锤子,剪刀,钳子。就是因为经常使用,所以直接创造出来并供人们使用,使用更加方便。更多库函数请看:cplusplus。
3.自定义函数
有些时候这些工具满足不了自己的情况,那就自己造呗。如下:
void a(int x)
{
}
int a(int x)
{
return a;
}
a(b);
大意就是这样,一般用在主函数前方便编译器阅读,第一个在是创建一个名字为a的函数,没有返回值(因为前面加了void),就是干完了就完事了,()内int x表示传参类型。
第二种意思有返回值,返回值为int整形,返回a。return后面也可以加表达式。
第三个就是他们的使用方法。需要注意的是,函数中的参数操作并不会改变主函数中的参数,因为他只是一份临时拷贝,如果需要改变,传地址就好了。
4.函数参数
一.实际参数 首先,它可以是常量,变量,表达式,函数等,然后他是传给函数的参数。
二.形式参数 和局部变量差不多,只有函数被调用时才被分配内存单元。
5.函数调用
一.传值调用:顾名思义,把主函数中的实际数值传给给函数被调用。
二.传址调用:和上面一样,但这个传的是地址。
6.函数的嵌套调用和链式访问
嵌套调用:就是一个函数可以调用另一个函数(简称套娃,和复合导数很像)。
链式访问:把一个函数的返回值作为另一个函数的参数。
printf("%d",printf("%d",printf("%d",43)));
这个是很经典的一个链式访问,大家可以试试输出多少,先猜一猜在去试(滑稽)。
7.函数的声明和定义
函数声明:一般放在头文件中,并且出现在函数使用前。
函数定义:就是定义一个函数,需要返回值是根据其返回类型放在函数名前并且写return,不需要就是前面加void并且不写return。
8.函数递归
这个虽然简单来说就是套娃,但还是实在太难理解,但会用之后就会非常喜欢》
就是函数自己调用自己的这么一个过程,精髓在于把大事化小》
必要条件:
1.有限制条件,并且满足后不再继续》
2.每次调用后越来越接近并且最后回等于限定条件》
接下来看个粒子(拆分打印)
void everyone(int a) //a=1234
{
if(a > 9)
everyone(n / 10);
printf("%d",n%10);
}
这个就是递归了,分两步看,递和归》首先往外递,a是大于9的,除以10后再次调用,然后归》
一张草图,流程就是这样,先走蓝线,再走红线,大家多多理解》
再来一个求字符串长度的》
int my_strlen(char* arr)
{
if(*str != '\0')
return 1 + my_strlen(str+1);
else
return 0;
}
参照上一个,差不多就是这样》
递归是一个比较抽象的东西,言语无法去具体的描述,过多的言语只会更加苍白无力》
4.数组
一、二维数组的创建和初始化
一维数组的创建和初始化想必大家在前面的讲解中已经很了解了,这里着重说一下二维数组,顺便带一下一维数组,下面的也一样》
char [2][3]={1,2,3,4,5,6};
int [][2]={{1,2},{3,4},{5,6}};
二维数组的创建上,使用了两个[],我们可以把它理解为行和列,就像平面直角坐标系一样》上面的例子可以看出,行可以省略,但列绝对不行》当数据不够对应的行和列时,自动补零,目前为止和一维数组一样》其实使用上也没有太大差别,就像数轴和平面直角坐标系的使用一样,并没有太大差异,只是后者能更多的表示一些东西罢了》
一、二维数组的使用
大家还是先自行感受,悟出属于自己的东西
char [2][3] = {{1,2,3},{4,5,6}};
char [1][2] = 6;
之前不是说过二维数组就像平面直角坐标系嘛,这里使用也是一样,需要注意的是,和一维数组一样,下表是从零开始!
一、二维数组在内存中的存储
也可以像一维数组那样去理解,连续存储,一个挨着一个,但是需要注意的是,二维数组以一行为第一个元素,就像一维数组首元素,但他是一行》
数组越界
编译器并不会报错,需要我们自己注意这个问题,因为他后面会有许多随机数》想知道随机数怎么来的过几天会出文章》
数组做函数参数
给函数进行传参时候,千万不要只给一个数组名(int arr[]),和之前说过的一样,形参只是实参的一份临时拷贝,形参的改变并不会影响到实参,以及,数组传参时,只会传首元素地址!所以和之前做法一样,写成指针形式就好(char* arr)》注意在调用函数时直接写arr而不是&arr,具体为什么,以后讲》
5.操作符详解
之前说过前面只是略讲,现在详就说,收!(哈哈哈)
1.算数操作符
+ | - | * | / | % |
也就是这些,加减乘除以及取模,这里真的没有什么要特殊讲的,也就是要点再强调一下。
用除法的时候,要是想要结果是小数,在使用float等的同时,记得带上点零(.0)这样就能告诉计算机要出小数。还有就是取模,就是取余数,并且要求两个操作数必须是整数,多用用就好了。
2.移位操作符
<< | >> |
左移和右移操作符,简单记就是箭头指哪就是什么移动,用法就是补码往右移动或者往左移动你想要的位数后丢弃移动方向的移动次数的位数,然后相反方向自动补零,需要注意的是右移有两种,算术右移和逻辑右移,逻辑右移就是上面那样,补零就完了,和算数右移的差别就是他补的不是零,是看情况的,要是负数,补码开始不是1吗,他就补1,是零就补零。可以把他这么理解:
[00000000000000000000000000000011]1
把框框理解成原本的补码,把1理解为右移了一位,然后前面又自动补零了。
好,这里再说一遍什么是补码,进制有很多,我们平常用的就是十进制,零到九嘛,因为计算机只认识二进制,并且存储一个数的时候是将其以补码形式储存》一个数分为源码,反码,补码,正数源反补相同,负数就源码是十进制转换成二进制,反码就是零变一,一变零,补码就是反码加一》例子:
00000000000000000000000000000111 源码
011111111111111111111111111111111000 反码
011111111111111111111111111111111001 补码
我们所用的操作都是对补码进行的,所以要转换成源码,在转换成二进制,才是我们想要的数字》
3.位操作符
&按位与 | |按位或 | ^按位异或 |
1.第一个就是看两个数的补码一一对应的那位,看两个是不是全是一,是就往下落,最后落出来的二进制数字就是我们想要的结果,例子》
1 1111111111111111111111111111011 //-5的补码
00000000000000000000000000000011 // 3的补码
00000000000000000000000000000011 //新数字的补码
补码减一,然后按位取反得到结果,所以-5&3=3》
按位或 | :只要有一就是一,两个都是零才是零》
按位异或 ^ :相同为零,相异为一》
4.赋值操作符
就是等号及其和其他符号的组合,真没啥说的,但他也是一个知识点》
=:赋值
==:判断
+=:b+=a === b=b+a
其他运算符号依次类推》
5.单目操作符
这块前面已经说的差不多了,前面还补充了波浪号~的使用方法,大家自行理解~
6.关系操作符
同上,==本是这里的,只不过为了区分放上面了》
7.逻辑操作符
同上》
8.条件操作符
同上同上》
9.逗号表达式
b=(c-1,a+c,a-2)
前面的公式也会执行,只不过把最后一个世子赋值给b
10.下标引用、函数调用和结构成员
下标引用 [ ]:常用于数组引用,如arr[1],是访问arr这个数组第二个元素,因为数组下标是从0开始,这里要补充的是已知 arr 直接使用时,调用的时第一个元素地址,所以
arr[7] = *(arr + 7)
arr[7]是数组的第八个元素,后面的是arr从第一个元素向后偏移七个元素,从而指向第八个元素,这个理解不了没有关系,马上就到指针,就明白了》
还有一种有意思的写法,虽然几乎没有人这么写
arr[7] = 7[arr] //两者结果是一样的(因为[ ]有两个操作数)
函数调用操作符就是括号():操作数是函数名和变量,需要注意的是sizeof在使用时可以没有括号,因为他不是函数,而是操作符》
结构体成员操作符有两个:" . " 和 " -> " 具体下面讲,这里说下用法
s.s //结构体对象.成员
*(ps).s //已知ps是指针
ps -> s //结构体指针 -> 成员
11.表达式求值
表达式的求值顺序一部分是由操作符的优先级和结合性决定的(这里在网上是有表的,直接搜操作符优先级就行,这样就知道面对多个操作符时应该怎么做),
同样有些表达式的操作数再求值的过程中可能转换成其他类型》
隐式类型转换:这里需要提及一个知识点,C语言里,整型算数至少默认以整型类型的精度进行的,为了获取这个精度,表达式里的内容在使用之前,都会被转换为普通整型,即整型提升。
这么做的意义在于什么呢,我们要知道CPU通用寄存器的长度就是int的字节长度,而进行char类相加时很可能会导致加出来的不足与一个整型,这就是它的意义。
实例:
char a = 5;
char b = 126;
char c = a + b;
首先我们知道在存储时,用的都是补码,而正数的正反补相同,所以5的补码是
00000000000000000000000000000101
当他是char类型时,只有一个字节,八个比特位,所以他就成为了这样
00000101
相同道理,126就是
01111110
我们再把他们相加
10000011
这就算完事了,最后我们再尝试把他进行整型提升,就是以%d的形式打印
11111111111111111111111110000011
然后再减一,再按位取反,就是我们想要的数字了
这就是整型提升,他只会在暗处进行,我们应该去了解我们的程序每一步都干了什么。
6.指针
敬请期待~
7.结构体
前面说过的不讲,这里进行详细补充》他有几大部分,分别是关键字,结构名,大括号,成员名,变量链表(可以不写)
这是图纸(不占空间)
struct peo
{
char name[20];
int high;
};
这是实际产物(占内存)
int main()
{
struct peo p1 = {"张三","180"};
printf("%s %d",p1.name,p1.high);
print1(p1);
return 0;
}
在上面可以看见 . 的使用,下面来用print1函数验证下->的使用
void printf1(struct peo* sp)
{
printf("%s %d",sp->name,sp->high);
}
最后就是尽量去传结构体地址,这样就会省空间,具体原因请看我过几天的函数栈帧的创建和销毁》