起因:
在大学的一门叫做《微机原理与单片机接口技术》的课中了解到了嵌入式,现在开始往嵌入式方向学习,目前学了c基础阶段,也遇到了一些问题,在这儿里我记录一下我所学的内容
当然,如有错误,还请各位行业大佬指点一二!
一、计算机的基本结构
输入输出设备(键盘,显示器等等)
CPU处理器(控制器,运算器,寄存器)
存储器、程序
计算机由上面这些构成!
存储器分为内存和外存
内存:工作存储器,容量较小,读写速度快,掉电数据会丢失
外存:长期保存数据,容量较大,读写速度较慢,掉电数据不会丢失
寄存器:CPU内部存储器,容量最小,读写速度最快
二、计算机的数据表示(进制转化)
分为:数值数据、非数值数据
2.1非数值数据(不能够直接进行算数运算的数据)
例如:字符,图片,声音
ASCII码表:规定了每一个字符以哪八位二进制数表示(1字节 == 8bir)
其中我了解到小写字符比大写字符大32--------(小写转大写:减32)
2.2数值数据(可以直接进行算数运算的数据)
下面是各个进制的构成:
十进制、二进制、八进制、十六进制
十进制:逢十进1: 0~9构成
二进制:逢二进1: 0~1构成
八进制:逢八进1: 0~7构成
十六进制:逢十六进1: 0~9,a~f构成
为了区分不同的进制:我们给八进制前加0,十六进制前加0x(0X)
76(十进制) 076(八进制) 0x76(0X76)(十六进制,如果A-F是大写,X就要用写)
2.3进制转化
其他进制转十进制:对应的数乘以对应的指数次幂 之和
二进制转十进制: 1010------- == 0*2^0+1*2^1+0*2^2+1*2^3= 10
八进制转十进制: 054--------- == 4*8^0+5*8^1 = 44
十六进制转十进制:0xaf4------- == 4*16^0+15*16^1+10*16^2 = 2804
十进制转其他进制:除以其他进制倒取余数
十进制转二进制: 除2倒取余 43-----------101011
十进制转八进制: 除8倒取余 43-----------053
十进制转十六进制:除16倒取余 43-----------0x2b
二进制与八进制之间的转换:
可以用3位二进制数表示一位八进制数 0~7-----------000~111
二进制和十六进制之间的转换:
可以用4位二进制数表示一个十六进制数 0~9,a~f(0~15) 0~15-------0000~1111
三、C语言中32个关键字
关键字:系统预先定义好的,有特殊含义的,并且都是小写,不能重复定义(32个)
数据类型:char、short、int、long、float、double、struct、enum、union、void
控制语句:if、else、switch、case、default、for 、do、while、break、continue、goto
存储类型:auto、static、extern、register
const:只读
return:返回函数
signed:有符号数
unsigned:无符号数
sizeof:计算所占内存空间的大小:单位(字节) sizeof(数据类型)//sizeof(变量名)
typedef:给已有的数据类型起别名;
volatile:防止编译器优化
标识符:程序员自己定义的,一般用来定义变量名,函数名,类型名
- 由数字、字母、下划线构成
- 第一个不能为数字
- 不能和关键字重名
(建议见名知意!!!)
四、常量
程序运行期间,不能也不会被改变的量
4.1字符常量
‘a’,‘B’,‘s’
4.2 整型常量
二进制:1010
八进制:066
十六进制:0xaf5
十进制:99,-2
注意:默认情况下,整形常量为有符号数;
无符号的int类型:98U
Long类型:76L
无符号的长整型:78UL
4.3浮点型常量
小数:3.14 0.00001 10000(浮点型常量包含整形常量)
指数:1e+5 == 100000 1e-5 == 0.00001
%g: 选择小数或者指数中比较合适(比较短)的一种情况进行输出
4.4 字符串常量
“hello”占几个字节
‘h’‘e’‘l’‘l’‘o’‘\0’
(字符串用双引号括起来,字符用单引号括起来)
注意:字符串由‘\0’结尾
4.5 标识常量(宏)
宏只是一个单纯的替换!
#define 宏名 表达式
注意:1、宏名一般用大写,为了和普通的变量区分开来吗,当然小写也可以
2、宏后没有分号
五、变量
5.1局部变量
定义在函数(任何)体内的变量
5.2全局变量
定义在函数(任何)体外的变量
5.3存储类型
auto、static、extern、register
Auto:修饰的变量存储在栈区,只能修饰局部变量
Static:修饰的变量存储在静态区,即可以修饰全局变量,也可以修饰局部变量,用static修饰的变量叫做静态变量
extern:修饰的变量存储在静态区,只能修饰全局变量
register:修饰的变量存储在寄存器中,只能修饰局部变量
5.3初始化
初始化:定义变量的时候就给他一个初始值!
int a = 10; //初始化
int a; a = 10;//赋值
注意:
- 全局变量如果没有初始化,他的值为0!
- 局部变量如果没有初始化,他的值为随机值!
(为了避免随机值,我们一般给局部变量初始化为0!)
auto:修饰局部变量,存储在栈区
register:修饰局部变量的,修饰的变量存储在寄存器中,但是他的内存空间很小,因此,当寄存器已满时,就算用register修饰了,还是存储在栈区
extern:修饰全局变量,存储在静态区
程序可以由多个.c构成,但是有且只能有一个main函数
作用:告诉编译器,该变量已经在其他文件中定义过了
Static: 即可以修饰局部变量,也可以修饰全局变量,存储在静态区
- 如果static修饰局部变量:
作用:延长局部变量的生命周期;如果局部变量没有初始化,那么局部变量的值为0;如果初始化了,只能初始化1次;
如果static修饰全局变量:
作用:限制作用域,只能在本文件内使用
5.4生命周期和作用域
5.4.1 生命周期
从开辟空间到空间释放
5.4.2 作用域
使用的范围
局部变量:
生命周期:从定义开始,到模块(距离他最近的大括号)结束;
作用域:大括号内(距离他最近的大括号)
用static修饰的局部变量:
生命周期:从定义开始,到程序结束
作用域:大括号内(距离他最近的大括号)
全局变量:
生命周期:从定义开始,到程序结束
作用域:整个程序
用static修饰的全局变量:
生命周期:从定义开始,到程序结束
作用域:本文件内(本个.c内)
5.5数据类型转换
5.5.1强制类型转换(我们自己去转的)
5.5.2隐式类型转换(编译器去转的)
注意:char、short使用的时候按照:int去用
float在使用的时候按照:double去用
横向箭头:不管有没有进行混合运算,都会进行转化!
纵向箭头:只有在进行混合运算的时候,才会进行转换!
六、运算符
单算移关与,异或逻条赋
单目运算符,算术运算符,左移右移,关系运算符、按位与、异或 、按位或、逻辑运算符、条件运算符、赋值
6.1算术运算符
+、-、*、/、++、--、%
注意:%不能用于浮点数
++在前:先自加,在赋值
++在后:先赋值,在自加
注意:如果++a和a++单独成立一条语句,那么都相当于给a+1
6.2关系运算符
> < >= <= == !=
注意: 0 < a < 5 //error
注意:等号的书写! ==(判断是否相等)
6.3 逻辑运算符
&&、 || 、 !
&&:
表达式1 && 表达式2
截断法则:有一个为假,结果就为假,前一个为假,后面就不在进行运算了
||:
表达式1 || 表达式2
截断法则:有一个为真,结果就为真,前一个为真,后面就不在进行运算了
6.4 sizeof运算符
Sizeof(数据类型) sizeof(变量名)
数据类型如何判断:去掉变量名,剩下的就是数据类型!
6.5 三目运算符
表达式1 ? 表达式2 :表达式3
先去判断表达式1是否成立,如果成立,就将表达式2的值作为整个表达式的值,如果表达式1不成立,则将表达式3的值作为整个表达式的值
8.6 逗号运算符
表达式1,表达式2,表达式3,表达式4,.........,表达式n
从左往右依次去计算每个表达式的值,最后,将表达式n的值作为整个表达式的值
注意:逗号运算符的优先级是最低的,因此在使用的时候加上括号!!
6.7 位运算符
& 、 | 、 ~ 、 ^、 << 、 >>
与运算:&:都为1,才为1(有一个0,就为0)
或运算|:都为0,才为0(有一个1,就为1)
取反(~):0变1, 1变0
异或(^):相同为0,不同为1
左移(<<):
无符号数:高位丢弃,低位补0
有符号数:符号位不变,高位丢弃,低位补0
右移(>>):
无符号数:低位丢弃,高位补0
有符号数:符号位不变,低位丢弃,高位补符号位
七、三大结构
顺序结构,选择结构,循环结构
7.1顺序结构
语句按照一定的先后顺序去执行
7.2选择结构
7.2.1 单分支选择结构
if(表达式)
{
语句1;
}
先判断表达式是否成立,如果成立,执行语句
7.2.2 双分支选择结构
if(表达式)
{
语句1;
}
else
{
语句2;
}
先判断表达式是否成立,如果成立,就执行语句1,否则,执行语句2;
案例:输入年份,输出该年是平年还是闰年
7.2.3 多分支if选择结构
if(表达式1)
{
语句1;
}
else if(表达式2)
{
语句2;
}
else if(表达式3)
{
语句3;
}
。。。
else if(表达式n-1)
{
语句n-1;
}
else
{
语句n;
}
从上往下,依次去判断每个表达式是否成立,如果成立,就执行对应的语句
案例:输入一个成立,判断成绩的等级
85-100:AAAAAAA
70-85: BBBBBBBB
60-70: CCCCCCCC
60以下:DDDDDDD
7.2.4 switch语句
Switch(表达式)
{
Case 标号1:
语句1;
Case 标号2:
语句2;
Case 标号3:
语句4;
。。。。
Case 标号n-1:
语句n-1;
Default:
语句n;
}
注意:
- 表达式不能为float类型;
- 标号必须为常量;
- 当表达式等于标号的时候:执行冒号后面的语句!(如果都不相等,默认执行default后面的语句!)直到switch,case语句结束,或者遇到break语句结束!
结束条件:(停止输出冒号后面的语句的条件)
- 遇到break,跳出switch语句
- Switch,case语句结束
7.3循环结构
重复的去做一件事
三要素:循环的起始条件、循环的终止条件、循环变量的变化
7.3.1 for循环
for(表达式1 ;表达式2 ;表达式3)
{
//循环体;
}
表达式1:循环的起始条件
表达式2:循环的终止条件
表达式3:循环变量的变化
先执行表达式1;然后判断表达式2是否为真,如果为真,则执行循环体,然后执行表达式3,以此反复,直至表达式2为假,跳出循环!
案例:用for循环实现1-100之和
思考:表达式1,表达式2,表达式3是否可以省略?
省略表达式1:在for循环之外:要给循环变量一个初始值
省略表达式2:死循环
省略表达式3:循环体内实现循环变量的变化
注意:表达式1,表达式2,表达式3都可以省略,但是分号不能省略
for循环的嵌套:
7.3.2while语句
While(表达式)
{
循环体;
}
判断表达式是否成立,如果成立,则执行循环体,否则,跳出循环
案例:用while语句实现1-100之和
7.3.3do...while语句
do
{
//循环体
}while(表达式);
先执行循环体;判断表达式是否成立,如果成立,则执行循环体,否则,跳出循环;
案例:用do...while语句不实现1-100之和
区别
while语句:先判断,在执行,语句至少执行0次;
do...while语句:先执行,在判断,语句至少执行1次;
7.3.4 break和continue的区别
(跳出离他最近的一个循环!)
break:跳出switch、case语句;跳出循环
continue:跳出本次循环,进入下一次循环
7.3.5死循环
While(1)
{
循环体;
}
for( ; 1 ; )
{
循环体
}
7.3.6goto语句
无条件跳转语句
一般格式为:
goto 标号;
标号:
八、数组
一组数据类型相同的元素组成的集合(一堆数据类型相同的数据组合在一起!)
特点:1、数据类型相同 2、地址连续(打印地址:%p)
存储类型 数据类型 数组名[元素的个数];
Int a[5];
//相当于定义了一个数组:数组名为:a:数组中有5个元素,这五个元素都是int类型的
数据类型:数组中元素的数据类型
数组的数据类型:int [5]
数据类型:去掉变量名,剩下的就是数据类型!
开辟的内存空间的大小= sizeof(数据类型)*元素的个数
8.1数组初始化
int arr[5] = {1,2,3};
//a[0] = 1, a[1] = 2, a[2] = 3; a[3]和a[4] = ?
在进行部分初始化时,未初始化的部分其值为0;因此,利用部分初始化的特点:给数组进行清零!
数组清零: int arr[100] = {0};
Int a[5] = {1,2,3,4,5};
Int a[ ] = {1,2,3};//在进行全部初始化时,数组元素的个数是可以省略的,有后面赋值的具体的元素个数来决定!
8.2数组的访问
数组名[下标]
注意:下标从0开始
8.3字符数组
字符数组的本质就是字符串;
给字符数组进行赋值:
1、Char str[10] = { ‘h’,’e’,’l’,’l’,’o’ };
Str[5]----->str[9] ==’\0’;
“hello” = ‘h’,’e’,’l’,’l’,’o’,‘\0’
2、Char str[10] = “hello”;
//数组名就是数组首元素的地址
//&str[0] == str
8.4字符的输入输出函数
%s:字符串
Printf(“ %s ”, str);
输出:puts(数组名);
功能:将数组中的内容打印到终端,并且自动换行
注意:打印到‘\0结束
puts后会自动添加换行,而printf不会!
输入:Scanf(“%s”,str);
gets(数组名);
功能:将输入的字符串保存在数组中,并且在末尾自动添加‘\0’
注意:gets不会进行越界检查,如果超出范围,就会操作未申请到的内存空间,段错误
出现段错误的原因:非法操作你没有申请的空间。
8.5二维数组
二维数组:元素为一维数组的一维数组
一组数组类型的元素组成的集合(一堆一维数组组成的数组)
元素:一维数组(元素:一维数组的里面存放的元素的个数以及数据类型必须相同)
数据类型相同 地址连续
存储类型 数据类型 数组名[行数][列数];
九、函数
具有独立功能的模块
为什么要使用函数???
使代码模块化,提高代码的复用率
9.1函数的分类
9.1.1 库函数
printf 、scanf 、strlen 、strcat......
9.1.2引入头文件
#include <stdio.h>、 #include<string.h>
9.1.3 调用函数
Strlen(数组名);
函数名(实际参数);
返回值:字符串的实际长度;考虑:返回值的数据类型,以及返回值是否需要
(如果需要使用函数的返回值:定义一个与其返回值相同的数据类型去接收!)
9.2自定义函数
9.2.1函数定义
存储类型 数据类型 函数名(形式参数列表)
{
函数体;//具体功能的实现
返回值;
}
存储类型:static、extern
数据类型:函数返回值的数据类型(函数返回值:不写默认为:int)
函数名:见名知意(满足标识符的命名规则)
形式参数列表:实现功能所需要的参数,需要调用者传入
函数体:具体功能的实现
返回值:若没有返回值,返回值可以省略(类型:void),如果有返回值:有且只能有一个!
9.2.2调用函数
函数名(实参列表);
注意:
- 需要将实参的值传递给形参,实参的个数和数据类型必须和形参一致
- 实参,可以是常量、变量、表达式,但必须是一个确定的值;
- 实参和形参是两块独立的空间(因此可以重名)
- 传参实际上是将实参的值拷贝给形参
- 形参是局部变量,在函数调用时开辟空间,调用结束立即释放
调用时
调用结束
十、指针
10.1什么是指针?
Char是一种数据类型,它是一种保存字符的数据类型(‘a’,‘b’)
Int 是一种数据类型,它是一种保存整形数的数据类型(10,20)
float是一种数据类型,他是一种保存浮点数的数据类型(3.14 2.37)
指针是一种数据类型,它是一种保存地址的数据类型(%p打印出来的地址编号)
10.2什么是地址?
内存分配的最小单元是字节,每一个字节都有一个编号,这个编号就叫做地址!
地址的本质:内存单元的编号!
指针:
指针:指针就是地址
指针的本质:内存单元的编号
10.3 指针常量
Char定义的变量-----------保存字符常量
Int定义的变量----------保存整形常量
float定义的变量-------保存浮点型常量
指针定义的变量-------保存指针常量(地址:专门用来保存内存单元的编号)
10.4 指针变量
如何定义一个指针变量?
存储类型 数据类型* 变量名;
Int * p;
//*用来表示p是一个指针变量
//定义了一个指针变量p,这个p用来保存类型变量的地址
//指针保存谁的地址========等价于===========指针指向谁
数据类型:指针指向的数据类型
指针p的数据类型:int *;
指针指向的数据类型:去掉一个*,在去掉变量名,剩下的就是指针指向的数据类型!
注意:*和&互为逆运算;
p保存了a的地址,作用是什么?
- 间接访问a的值
- 间接改变a的值
10.5 指针的赋值
指针的赋值:相当于改变指针的指向;
注意:对指针变量进行赋值时,要注意类型匹配!
10.6空指针
没有指向的指针:NULL(值为0的指针,就认为该指针没有指向)
注意:0号地址禁止操作
要操作就改变指针的指向:
10.7、野指针
不知道指向哪里的指针
局部变量:没有初始化:随机值
局部指针:没有初始化:野指针
如何避免野指针?
在定义指针时:初始化为NULL;
10.8值传递
案例:封装函数实现两个数的交换
10.9地址传递
总结
在近十天内学习了c语言的基础阶段,
哈哈哈,学习C语言就像是和一只顽皮的猴子搏斗!有时候你觉得自己已经掌握了它,结果它又给你来个“语法错误”!但是不管怎样,C语言学习总结就是:坚持不懈,笑到最后!😄🐵
继续加油,继续努力,更上一层楼!