C语言初阶语法(总纲)
此篇博客包含了前面C语言所有的基础语法,写这一期的博客不仅是为了记录自己的学习成果也是为了让更多的朋友能学到对自己有用的知识,如果总有一个知识点帮助到了你,给卑微博主点点关注加加油吧!如果文章有什么不足的地方特别欢迎大家指出或者给出好的建议。
文章目录
一.第一个C程序
1.什么是C语言?
人和人交流需要各种语言,例如中文,英语,日语等等;人和计算机交流就需要我们的计算机语言,全世界大概有七千多种语言,中文只是其中一个极具文化传承的大国语言,C语言也是如此,在几千种计算机语言当中脱颖而出,经久不衰,很多初学者都是从这门语言打开编程世界的大门!官方术语:C语言是一门通用的计算机语言,广泛应用于底层开发,C语言能以简易的方式编译,处理低级存储器,产生少量的机械码以及不需要任何运行环境支持便能运行的编程语言。
2. 如何创建一个项目开始写代码
1.打开我们下载好的VS,点击创建新项目(其他版本都可,我这里展示的2019版本)
2.点击空项目,然后点击下一步
3.取一个项目名称,代码路径选择自己好找的地方,然后点击创建
4.点击头文件右击选择添加,然后选择新建项
5.选择c++文件,将后缀改为.c,点击添加
6.完成,现在我们就可以开始写我们的第一个代码了
3.第一个C程序
下面是第一个C程序代码
有些同学可能会写但是却不明白这些代码到底是什么意思?下面我们一起来看一下:
1. include是包含的意思,<stdio.h>是一个头文件,代码整体意思就是包含一个名字叫stdio.h的头文件
2. int是整形的意思,main()是主函数,程序首先是从main函数进去开始执行代码的
3. printf是一个库函数,用于在屏幕上打印信息,库函数要想使用必须要声明一下它的头文件,于是就有了开头的 include<stdio.h>
4. 返回一个整形0,与上面的代码int是有直接联系的,至于为什么要return 0,c语言的传统习惯就是如此,不必深究
接下来可以自己尝试的编写代码并执行(按ctrl+f5)
二.数据类型
1.基础数据类型
类型 | 类型含义 |
---|---|
char | 字符数据型 |
short | 短整型 |
int | 整型 |
long | 长整型 |
long long | 更长整型 |
float | 单精度浮点型 |
double | 双精度浮点型 |
2.内存分配
sizeof关键字:
如何计算各种类型所占空间大小,这里我们就需要用到sizeof这个关键字,sizeof是一种单目操作符,作用于计算类型或者变量所占空间大小
各种类型空间计算:
#include<stdio.h>
int main()
{
printf("%d\n", sizeof(char));
printf("%d\n", sizeof(short));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long));
printf("%d\n", sizeof(long long));
printf("%d\n", sizeof(float));
printf("%d\n", sizeof(double));
return 0;
}
运行程序我们会得到下面这个结果(并不是所有编译器结果都一样,空间大小以自己实操为准)
那么得到的这些结果又是什么意思呢?我们可以查证得知,sizeof计算的单位是字节,但是字节又是多大呢?为了更好的了解程序的底层原理,下面我们来深入研究一下:
我们知道计算机所有的程序最终都是由二进制执行的,无数的0和1组成了我们现在的信息大爆炸时代,一串二进制0000001001中任意一个0或者1它的大小就是1bit(比特位),了解了这个这样我们就能更好的认识计算机中的所有单位,看下图
单位 | 换算 |
---|---|
… | … |
1Tb | 1TB=1024Gb |
1Gb | 1GB=1024Mb |
1Mb | 1MB=1024Kb |
1Kb | 1Kb=1024yte |
1yte(字节) | 1yte = 8bit(比特位) |
看到这里,我们就可以知道上面程序运行的结果所占空间到底有多大了,例如:sizeof(int)=4字节,也就是32个比特位,那么它在内存中就会分配到
00000000000000000000000000000000这样的二进制位置;了解这个之后将会对以后底层原理的深入研究有不少帮助
3.类型意义
C语言中为什么有这么多的类型,我们都知道计算机的出现是为了帮助人们解决生活中的各种问题的,既然程序指向了生活,那么C语言的语法肯定也要贴近生活,例如表达一个人的年龄我们可以用整型int age = 18;一个人的体重用浮点型 double weight = 152.56;名字我们可以用字符类型 char name = jack;类型的意义就在于此
三.常量,变量
1.常量
常量就是不能被改变的量就是常量,例如:
它的值是固定的,不能被修改,10不可能被赋值改成20,这就是常量的一种:字面常量;那还有哪些常量呢?下面我们来看
常量类型:
(1)字面常量
前面我们已经介绍过这种常量了,这种常量存在,但是在应用中并没有什么意义,基本不会用到
(2)const修饰的常变量
这里变量a已经被赋予了常属性(不能被改变)
变量a已经不能被修改,所以它是常量,但a本身是变量的,具有变量的属性,所以这里我们叫它常变量
(3)#define定义的标识符常量
定义的标识符不占内存,只是一个临时的符号,预编译后这个符号就不存在了。预编译又叫预处理,#define 和 #include 一样,也是以“#”开头的。凡是以“#”开头的均为预处理指令,#define也不例外。
(4)枚举常量
一个枚举类型中任意一个未来可能取值的量就叫枚举常量
2.变量
什么是变量:
变量就是能够被改变的量,前面我们认识的数据类型都是用来创建变量的
这些量都是可以随时改变的,所以我们叫它变量
变量的分类:
全局:具有文件作用域的变量. 静态:具有静态存储期或内部链接属性. 局部:具有函数或块作用域的变量. 因而结合起来,通俗来讲,变量只要在{}里面就是局部变量,在{}外面就是全局变量
变量的作用域和生命周期:
作用域:程序设计概念,通常来说,一段程序代码中所用到的变量名字并不总是有效的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
局部变量作用域:
{ }里面就是变量a的局部范围,局部变量的作用域就是变量所在的局部范围
全局变量:
所以我们可以感知一个全局变量是可以在任何文件中使用的(同工程文件),它的作用域就是整个工程,生命周期就是整个工程的生命周期;
四.字符串+转义字符+注释
1.字符串
字符串:由双引号引起来的一串字符称为字符串
字符串有个结束标志\0,在计算字符串长度的时候\0是结束标志,不算字符串内容,例如我们计算下面两个数组长度:
strlen是一个库函数,用来求字符串长度,明明存放的值一样,为什么求长度会不一样呢?我们前面说到了字符串默认结尾有一个\0结束标志,而单个字符没有(‘h’),所以strlen求长度的时候会发生下面这个情况:
2.转义字符
转义字符 | 释意 |
---|---|
\ ? | 在书写多个问号时,防止它们被解析成三字母词 |
\ ’ | 用于表示字符常量‘ |
\ ” | 用于表示字符串内部的双引号 |
\ \ | 用于表示一个反斜杠,防止它被解析为一个转义序列符 |
\ a | 警告字符,发出蜂鸣 |
\ b | 退格符 |
\ f | 进纸符 |
\ n | 换行 |
\ r | 回车 |
\ t | 水平制表符 |
\ v | 垂直制表符 |
\ddd | ddd表示1-3个八进制的数字,例如:\130 x |
\xdd | dd表示2个十六进制数字,例如:\x30 x |
\ddd:将一个八进制数转换为十进制数
\xdd:将一个十六进制数转换为十进制数
3.注释风格
有两种注释风格,一种C语言注释风格,一种c++注释风格
两种注释风格都各有千秋,但是C语言的注释风格有比较大的缺陷,它不能支持嵌套注释,而且随着编译器越来越聪明,C语言注释优点被取代,c++注释风格也逐渐成为大众所喜爱的注释风格
五.选择语句,循环语句
1.选择语句
if else选择:
在C语言中我们常用if else语句来实现选择的情况,我们先看一段代码:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
int input = 0;
printf("你要好好学习吗?好(0)/不好(1)");
scanf("%d", &input);
if (input == 0)
{
printf("好offer\n");
}
else
{
printf("进厂\n");
}
return 0;
}
这是常见的双分支选择问题,如果问题比较复杂,if else是可以实现多分支和嵌套的,例如:
当if(…)内的条件判断式成立,则执行if 下面{ }内的代码,当else if后面的判断式成立,则执行else if后面的代码块({});
switch选择:
有的人可能会有疑问,如果分支太多我们难道要一直else if…下去吗?这里我们就要介绍一下switch选择语句了
值得注意的是输入的变量值只会决定代码执行的入口是哪个语(case),并不能决定代码从哪里停止,所以我在每条语句项后面加上了break跳出当前语句,也并不是所有的情况后面都要加上break,break的运用是灵活的,要根据自己的需求进行调整
这里的default是一个默认选项,当输入的值与所有的语句项都不匹配的时候,选择default这条语句项;
2.循环语句
while循环:
while循环会先进行判断部分,如果判断式成立则进入循环内,当循环内代码执行完后会再次返回判断部分,如果依然成立,继续进行循环,直到表达式不成立
break:
如果在循环中使用break是什么情况?
结果只会打印0 1 2 3 4 5,在while循环中break用于永久跳出循环,当i=5时,循环就会被break终止,后面的有多少代码都不会去执行了
continue:
在循环中使用continue是什么情况?
结果不会停下来,会一直打印5直到电脑死机
continue的作用是跳过它后面的代码,重新回到判断部分进行判断,成立则继续循环,但是代码中i++被跳过,i的值永远是5,永远执行contiune,所以代码进入了死循环
do while循环:
do while 的特点在于每次都会先进行一次循环然后才去判断,也就是说无论如何do while 循环至少会执行一次代码,其他的和while没有任何区别
for循环语句:
for循环是我们经常用到的循环,因为其方便简易,不容易出错的优点,它的初始化部分,判断部分,调整部分都整合在一起,在for中使用continue不会像while中一样死循环,因为for的调整部分永远在前面不会被continue跳过执行;
另外我建议for循环变量取值采用“前闭后开区间”写法,例如
for(i=0;i<11;i++),这样这个11其实有它的意义,代表循环11次
六.函数与数组
1.初识函数
什么是函数?函数有哪些用途?
第一,函数就是 C 语言的模块,一块一块的,有较强的独立性,可以相互调用,换句话说,C 语言中,一个函数里面可以调用 n 个函数,即大函数调用小函数,小函数又调用“小小”函数。. 这就是结构化程序设计,所以面向过程的语言又叫结构化语言。. 第二,函数就是一系列 C 语句的集合,能完成某个特定的功能。. 需要该功能的时候直接调用该函数即可,不用每次都堆叠代码。. 需要修改该功能时,也只需要修改和维护这一个函数即可。.
外卖平台还可能不靠谱,给你返回的东西并没有达到你的预期,或者外卖员中途可能因为某些事情导致你的东西延误
但是对于计算机我们可以绝对信任,你给定的值它一定能给你返回你满意的值,失误只能是人为的,下面我们可以试着写一个简单的返回两数之和值的函数
看上去好像函数的写法复杂的多,其实不然,这段代码还不能体现函数的优点,从长远来看,函数能够一劳永逸,按照上面的普通写法如果我要换两个新的变量呢,还得重新写代码,但是函数就不用,不管你有多少新的变量需要计算,只要需求一样通通只用调用函数就可以了
使用函数能使代码更加简洁方便,函数是一个小模块,需要的时候能够随时调用
2.初识数组
数组的定义:一组相同类型的元素集合就是数组
int (代表这组数组的类型),arr1(取一个数组名字),【10】(代表这个数组有10个元素,也可以不写10,编译器会根据后面元素来分配大小),{}里面就是存放的元素,元素可以不写满,剩下的空间默认为0;
那么当我们想访问某个元素的时候要怎么做呢?
数组是用下标访问的,每个元素都有下标,第一个下标是从0开始的
当i=0的时候,arr【i】就是在访问它的第一个元素,也就是0,然后i每次++循环后我们就可以把arr1的所有元素访问了
七.初识操作符
.算数操作符
C语言是这样表示基本算数操作符的:
+(加)-(减)*(乘)/(除)%(取模,取余)
注意点:/和*使用的时候,如果两端都是整数就执行整数除法(乘法),得到的结果只能是整数,如果想要得到小数,操作符两边必须有一个数是小数才能得到一个小数结果
%取模在后面的学习也经常用到,例如我们要得到一个整数123456里面的第四位数,我们可以利用%解决
a / 100 的结果就是1234,1234%10取余我们就可以得到4,不管需要哪位上的数,我们都可以调整后获得这个数字
移位操作符
(>>)右移操作符(<<)左移操作符,左右移动二进制位,具体是怎么样的我们不详细讲,今天只用了解基础
.位操作符
&(按位与) |(按位或) ^(按位异或)
涉及到指针,我们也只先认识一下,后面来详细讲解
赋值操作符
= ,+=,-=,*=,/=,&=,^=, |=, >>=, <<=
+=的意思就是a=a+5;后面的赋值操作符都是这个意思,当我们都理解那些操作符了(&=,^=, |=, >>=, <<=)的意思就很好理解了
单目操作符
单目操作符:! ,- , + ,& ,sizeof , ~ , – , ++ , *
a+b:+号有两个操作数,叫双目操作符
+2: +只有一个操作数,叫单目操作符
!: 在C语言中,我们是怎么表示真假的,0表示假,非零表示真,!的作用就是把真变成假,假变成真
++ --前后置的理解
(类型)强制类型转换
int a = 3.14; //a是整型,3.14却是浮点型
int a = (int)3.14; //将3.14强制转换成int类型
关系操作符
== (等于) , >(大于) , <(小于) , <=(小于等于) ,=>(大于等于) , ! =(不等于)
这里和数学上的意义都是一样的,没有什么过多介绍的
逻辑操作符
&&(逻辑与) ||(逻辑或)
int a = 0;
int b = 1;
int c = a&&b; //有一个值为0(假)结果就为0,只有都为真,c的结果才为真
int c = a||b; //有一个值为真(非零)结果就为真,都为假,c的结果才为假
老师让你和张三搬书,你和张三才能完成这件事,少一个人不行(&&)
老师让你或者张三扫地,你和张三有一个人就能完成这件事,只要有一个人扫地就行(||)
三目操作符
exp1 ?exp2 :exp3
exp1成立,exp2计算,整个表达式的结果是:exp2的结果
exp1不成立,exp3计算,整个表达式的结果是:exp3的结果
int main()
{
int a = 1;
int b = 2;
int max = 0;
max = a > b ? a : b;//求两个整型的较大值
printf("%d", max);
return 0;
}
a>b成立max的值就是a,不成立max的值就是b
逗号表达式
int main()
{
int a = 5;
int b = 2;
int max = 3;
int d = (a = b + 2, max = a - 1, b += a + max);
//逗号表达式是从左向右依次计算的
//整个表达式的结果就是最后一个表达式的结果
printf("%d", d);
return 0;
}
下标引用操作符
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9 };//这里的【】是数组规定的写法
printf("%d", arr[5]);
//这里的【】叫下标引用操作符,这里打印的是6,因为下标是从0开始的
return 0;
}
函数调用操作符
int main()
{
int a = 0;
printf("%d",a );//调用函数时,函数名后面的()就是函数调用操作符
return 0;
}
调用函数必须后面有(),不然他就不是函数,前面我们提到sizeof是关键字,因为它的格式可以写为 sizoef 5,所以它是关键字而不是库函数
int main()
{
int a = 0;
printf("%d",sizeof 5 );
return 0;
}
八.常见关键字
1.常见关键字有哪些?
关键字 | 用意 |
---|---|
auto | 自动创建自动销毁 |
break | 跳出循环 |
case | switch语句项 |
continue | 跳过后面代码,执行下一次循环 |
const | 修饰常变量 |
default | 默认选项 |
do | do while循环 |
else | if else语句 |
enum | 枚举 |
extern | 声明外部符号 |
for | for循环 |
goto | 跳转 |
if | if else语句 |
register | 寄存器关键字 |
signed | 有符号的 |
static | 静态 |
struct | 结构体 |
switch | switch分支语句 |
typedef | 类型重定义 |
union | 联合体,共用体 |
void | 空,无 |
include,define是关键字吗? 不是,它们是预处理指令
还有一个关键字比较重要volatile,我只提一下,有兴趣的可以下去研究一下
2. 部分关键字解析
auto解析:
int main ()
{
auto int a = 0 ;//auto是自动创建变量自动销毁变量的意思
int b= 0; // 不写auto编译器也会在前面默认隐藏
//一个auto,所以我们只需要知道有这个东西就行了
return o;
}
register普及:
cpu的不断跟新换代,导致从内存读取数据的速度也越来越快,这个时候
对内存的效率要求也越来越高,寄存器的效率就要远远高于其他内存;
register的作用就是再建议编译器将此信息优先存放在寄存器中,达到该信息能够高效访问的结果,当然也只是建议,并不是绝对,不过现在编译器已经很先进了,你不说它也会照做
typedef解析:
typedef是将一个类型重新定义一个新的名字
如果你觉得一个类型使用起来实在过于繁琐,那么你可以使用typedef来重新命名
int main()
{
typedef unsigned int uint;//将unsigned int名字重新定义为uint
uint num = 100;
return 0;
}
static修饰:
如果你希望一个局部变量出了局部范围值保留,请使用static
void good()
{
int a = 0;
a++;
printf("%d ", a);
}
int main()
{
int a = 0;
for (int i = 0; i < 10; i++)
{
good();
}
return 0; //打印结果 1 1 1 1 1 1 1 1 1 1
}
void good()
{
static int a = 0; //static修饰局部变量
a++;
printf("%d ", a);
}
int main()
{
int a = 0;
for (int i = 0; i < 10; i++)
{
good();
}
return 0; //打印结果 1 2 3 4 5 6 7 8 9 10
}
static修饰局部变量,改变了局部变量的生命周期(本质上是改变了变量的存储类型)
经过static修饰的局部变量就由栈区变到静态区,改变了变量的生命周期
同样,static修饰的全局变量或函数,使得这个全局变量或函数只能在自己所在的源文件.c内部可以使用,其他源文件不能使用!
全局变量在其他源文件可以使用是因为全局变量具有外部链接属性,但是被static修饰过后就变成了内部链接属性,其他源文件就不能链接到这个静态的全局变量了!
3.#define定义常量和宏
#define定义常量:
#define add 1000 //这里add就是一个标识符常量,值就是1000
#define定义宏:
#define add(x,y) ((x)+(y)) //宏替换
int main()
{
printf("%d",4*add(2,3));
return 0;
}
在C 语言中,可以采用命令#define 来定义宏。 该命令允许把一个名称指定成任何所需的文本,例如一个常量值或者一条语句。 在定义了宏之后,无论宏名称出现在源代码的何处,预处理器都会把它用定义时指定的文本替换掉。
九.轻松入门指针(附结构体)
1.初阶指针详解
C语言的指针并没有传说的那么难,在教学资源匮乏的时候独自学习理解指针确实会难不少,但是在现在各种优秀的学习资源层出不穷的时候,你就会发现C指针纸老虎罢了!
要理解指针,我们先要了解内存,内存是电脑上特别重要的存储器,计算机所有程序的运行都是在内存中进行的。
我们可以把计算机的内存理解为一个大酒店,酒店有许多独立的房间,每个房间都有它的房间号
一个内存单元的单位是一个字节,不管内存有多大,它都会被分配成许多一个字节大小的小房间,每个小房间都会有一个地址(房号)
我们知道int类型的大小是四个字节,也就是说内存会给int类型分配四个房间(因为每个房间大小是1字节嘛),变量a在内存形象化的就是这个样子:
我们将变量a的地址打印出来看一下:
为什么只有一个地址?不是每个内存单位(1字节)都有一个地址吗?
当我们需要地址的时候,编译器只会将类型在内存中存储的首地址呈现给我们,通过这个首地址我们就能访问整个类型了
pa这个指针变量相当于备份了一份房间地址,通过pa我们随时可以访问到a
我们就可以说pa指向a,这就是指针的概念,这里的pa也是一个普通的变量而已,它和a是一样的,但是是为了存放某个地址,所以我们叫它指针变量,不要对pa产生什么歧义
*‘ * ’符号叫解引用,我们通过pa找到了a,但是只是站到了a的房间门口而已,里面的网友长什么样子我们也不知道,pa唯一的信息只是一个房牌号而已,的作用就是将房门打开访问里面的内容,里面是人是鬼开了门才知道
所以指针就是地址!!!
2.结构体
结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员。请看下面的一个例子:
stu 为结构体名,它包含了 5 个成员,分别是 name、num、age、group、score。结构体成员的定义方式与变量和数组的定义方式相同,只是不能初始化。
像 int、float、char 等是由C语言本身提供的数据类型,不能再进行分拆,我们称之为基本数据类型;而结构体可以包含多个基本类型的数据,也可以包含其他的结构体,我们将它称为复杂数据类型或构造数据类型。
结构体变量:
既然结构体是一个数据类型,那么我们就可以用它定义一个变量
#include <stdio.h>
int main()
{
struct{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
} stu1;//结构体变量stu1
//给结构体成员赋值
stu1.name = "Tom";
stu1.num = 12;
stu1.age = 18;
stu1.group = 'A';
stu1.score = 136.5;
//读取结构体成员的值
printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", stu1.name, stu1.num, stu1.age, stu1.group, stu1.score);
return 0;
}
运行结果: