C语言知识体系
- 一、C语言概述(历史、特点、标准)
- 二、数据类型(数据集基本类型、输入输出)
- 三、基本语句、表达式及运算符(条件语句、循环语句、多路分支语句、goto语句)
- 四、数组(一维数组、二维数组、多维数组)
- 五、指针(一维指针、多维指针、数组指针、指针数组、函数指针、函数指针数组)
- 六、内存管理(内存泄露)
- 七、函数(函数的声明、定义及调用、库函数的使用<字符串处理函数、时间函数、随机数函数>)
- 八、编码规范
- 九、预处理(宏定义、宏函数、条件编译)
- 十、关键字(static、extern、register、const、typedef、volatile、inline)
- 十一、位操作(位运算)
- 十二、复合数据类型(struct、union、enum)
- 十三、文件操作(打开、关闭、读写、属性设置)
一、C语言概述(历史、特点、标准)
(一)关于C语言的几个问题:
嵌入式开发为什么选择C语言?
嵌入式的核心技术是移植操作系统、在操作系统上层做开发及在系统底层做开发,而操作系统的核心是内核,内核是由C语言开发的。
内核为什么选择C语言开发?
1、具有访问硬件和控制硬件的能力(最突出的特点)(C与汇编语言均可实现)
2、C语言的运行效率快(C语言是语言运行时语言)
注:C是运行时语言,java是解释性语言(编译后不可被机器直接识别)
3、具有很强的可移植性
C语言与C++对比
1、C是面向结构的语言,C++是面向对象的语言。
注:面向结构性的语言代码的复用性差、代码的维护性差和代码的扩展性差。
面向对象的语言解决的是大而复杂的事情,而面向结构的语言是解决具体的问题。
C语言与汇编语言的对比
1、硬件初始化时,选择汇编语言。
2、硬件的一些复杂操作时候,选择C语言,因为汇编语言是低级语言,没有高级功能。
C语言的开发方向
1、操作系统(上层(库)、底层(BSP、驱动)及实现(内核))
2、硬件
3、中间件(sdk)
(二)C语言的基础知识点
机器码
1、运行效率
2、访问硬件
3、能力
C语言之父
丹尼斯-里奇
C语言语法标准
1、k&RC(c79)
2、c89
3、c99
4、c11
C语言的语法版本
1、GUN C+ASCI C
二、数据类型(数据集基本类型、输入输出)
(一)数据类型的分类
基本数据类型(编译器自带的类型)
1、int(4字节),short(2字节),long(4字节),long long(8字节),char(1字节),float(4字节),double(8字节)
复合数据类型(多个内置类型组成的新类型)
1、数组、struct、union、enum
void类型:void *(万能指针):多态
(二)定义变量(注意编码规范)
1、变量的可读性(int n vs int sum_result)
2、循环变量
(三)基本数据类型的知识点
1、各种数据类型的字节长度
1、int(4字节),short(2字节),long(4字节),long long(8字节),char(1字节),float(4字节),double(8字节)
2、测量数据类型的大小
1、sizeof(这是一个运算符)
3、指针大小是固定的
1、指针指向数据的地址,数据地址的位数由操作系统的位数据决定。例如:64位操作系统的指针大小为8字节
4、数组大小(数组是静态分配空间)
1、数组大小=数组长度 * 数据类型大小
5、字符串长度
1、
char *p = “hello world”;
strlen(char *p)
注:strlen不统计"\0"
(四)各种数据类型的取值范围
1、计算机的数据是以补码形式保存的,目的是解决+0和-0问题。
(五)变量和常量
1、全局变量和局部变量
(1)变量的三个特点:字节长度、生命周期&作用域、存储区域
(2)作用域:可见范围
局部变量:在函数体里定义的变量–所在函数,出了函数不可见
全局变量:在函数体外定义的变量–整个全局(需要extern外部声明)
(3)生命周期:所在内存空间的分配–释放过程
局部变量:所在函数体执行时,分配空间,执行结束,释放空间。
全局变量:所在程序执行时分配空间,执行结束释放空间。
(4)存储区域
(六)数据类型转换
1、隐式类型转换:char->short->int->long->float->double
注:隐式类型转换不安全
2、安全的类型转换
1、首先,进行检查,先检查两个类型是否可以进行转换
2、通过之后在进行转换
3、强制类型转换
1、规则(类型名称)变量名
(七)格式化输出
1、printf的常用类型
1、
%d 10进制整型数
%u 无符号的10进制整型数
%o 无符号的8进制整型数
%x 16进制整型数
%X 16进制整型数,并以大写的ABCDEF表示浮点数
%f 10进制的小数,取到小数一下6位
…
2、printf是行缓冲,满一行或者遇到’\n’,或者缓冲区被强行刷出时,数据才会被输出。
3、printf的使用小技巧
2、putchar
char ch = ‘a’;
putchar(ch) 直接输出一个字符
3、puts
1、给出字符串的首地址,即可输出整个字符串
(八)格式化输入
例:int i = 2;
int func(int a,int b)
{
printf("%d\n",a);
printf("%d\n",b);
}
func(i++,++i);
1、函数传参顺序:从右到左
2、++i被替换成变量名
3、i++被替换成变量的值
三、基本语句、表达式及运算符(条件语句、循环语句、多路分支语句、goto语句)
(1)for、while语句注意事项
1、已知循环次数用for循环,未知循环次数用while。
2、注意循环语句的多分号!
3、循环体里不要直接修改循环变量的值。
4、循环的优化:1)在多重循环中,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU的跨切循环层的次数。2)逻辑判断应当放在循环体外。
5、编码规范:即使循环体只有一条语句,也要加{ }。
(2)switch case语句使用注意事项
1、条件:整数或者字符,表达式。
2、格式:不管case下的语句有多少条,都要加{}。
3、default:每个switch语句都必须要加入default处理。
4、流程:只要匹配到一个case,如果case里没有break,会继续执行后面的case语句。
(3)goto语句使用注意事项
1、尽可能的少使用goto语句,goto语句会破坏代码结构,导致代码可读性差。
2、标签和goto语句之间切记不要出现变量的定义。
3、异常处理时候,常使用goto,因为goto处理速度是最快的。
四、数组(一维数组、二维数组、多维数组)
五、指针(一维指针、多维指针、数组指针、指针数组、函数指针、函数指针数组)
指针的定义
1、指针是什么?
指针是一种数据类型,它可以定义变量,变量保存的值是地址。由于地址长度是固定的,所以指针变量的长度是固定长度;不同地址的步长不一样,需要不同指针类型的变量来保存。
exp:int *p:整型指针变量:p是一个变量,保存的是地址,该地址是整型类型的地址。
2、地址为什么分为不同的类型?
地址具有步长的属性,不用类型的指针变量保存不同步长的指针地址。
3、指针的作用是什么?
由于指针变量可以保存地址,所以可以直接操作地址,即可以直接操作硬件的寄存器地址,从而实现直接访问硬件。
4、指针支持的运算符: *:间接运算符 &:取地址运算符(对应的内存空间和指向的内存空间)
exp:p++:对应的内存空间
(*p++):p指向的内存空间
(1)一级指针
1、指针指向变量的首地址。
2、(*p)++:对指向的空间的值+1。
3、*p:取出p指向空间的内存空间的值。
(2)多级指针
1、定义:保存上一层指针变量的地址。
exp:
int **PP:整型指针的指针变量,pp是一个变量,保存的是地址,该地址对应的内存空间保存的还是地址,这个地址是整型的地址。
2、多级指针针的应用场景
多级指针常应用于函数传参。
(3)指针的运算
1、赋值运算(相同类型的指针之间赋值)
exp:短指针赋值给长指针,会多取数据。长指针赋值给短指针,会丢失数据。
2、万能指针(void *)
万能指针可以接受任何类型指针的值,但是不能进行取值操作,且万能指针的步长是一个字节
3、运算+、-
1、p + 操作数,指针可以与操作数相加或相减。
2、指针不能与指针相加或相减。
(4)字符串
1、字符串的定义:字符串就是首字符的地址。
注意:保存在数据段的or段的常量,不可被修改。
2、字符串函数:
(1)字符串长度函数:strlen
strlen是长整型,最好的定义类型为size_t len = strlen(src);
3、字符串拷贝函数(strcpy vs strncpy)
(1)定义:strcpy(char *dest,const char *src); strncpy(char *dest,char *src,n)
strcpy是将src中的字符串拷贝到dest,strncpy则需要在增加一个拷贝的长度。
(2)缺点:不安全,容易出现越界;只能拷贝字符串,不能拷贝其他数据类型。
4、字符串连接函数(strcat)
(1)定义:strcat(char *dest,const char *src);strncat(char *dest,char *src,n)
strncat比strcat多了一个选择链接的长度。
5、字符串长度比较函数(strcmp)
(1)定义:strcmp(char *dest,const char *src)
比较两个字符串的长度,相等返回0,dest>src则返回正数,否则返回负数。
6、不区分大小写比较字符串(strcasecmp)
7、查找字符串中第一个指定的的字符(strchr)
(1)定义:strchr(const char *s,int c)
8、查找字符串中第一个出现的指定字符(strpbrk)
(1)定义:strpbrk(const char *s,const char *accept)
在s中查找accept中任意字符第一次出现的地址。
9、拷贝内存内容(mencpy)
(1)定义:memcpy(void *dest,const void *src,size_t n)
数组
1、数组的特点
(1)静态分配空间:空间利用率差
(2)所占空间的是物理连续的
2、数组的使用
(1)数组的定义:柔性数组(本身不占用空间,需要空间时再动态分配)
注意:c99标准中的数组是可变长数组,而c89则必须确定数组长度。可变长数组可以用变量定义数组,但是不能修改变量来扩充数组大小。
(2)通常用宏来定义数组的大小,这样可以提高数组的可移植性。
3、数组名的作用
(1)一维数组名:指针常量(元素类型指针),保存数组首元素的地址。
一些区分:
a(&a[0]):数组名,指针常量,保存数组首元素的地址
&a:对数组名取地址,等于数组的地址。
数组的地址用数组指针变量保存。
(2)二维数组:二维数组的定义可以省略行,不能省略列。
一些区分:
aa:二维数组的数组名,并不是首元素的地址。而是二维数组中首个一维数组的地址。
&aa:二维数组的地址。
(3)三维数组:
(4)数组指针变量
应用场景:函数传参时使用
传一维数组名,用元素的指针来接。
传二维数组名,用一维数组指针来接。
传三维数组名,用二维数组指针来接。
六、内存管理(内存泄露)
七、函数(函数的声明、定义及调用、库函数的使用<字符串处理函数、时间函数、随机数函数>)
函数的定义和声明
1、函数的三要素:函数名、函数参数、函数返回值
2、函数的适用形式:函数的声明、函数的定义、函数的调用
(1)函数的定义,最好定义在需要调用此函数的函数之前。此时不需要在前面再度声明。
(2)若定义的函数与当前函数没有任何关系,是一个独立的函数,则在外部单独声明,用include 加头文件调用。
(3)函数的定义:函数名、函数的返回值和形参的类型及变量名
(4)函数的声明:函数名、实参的变量名或者实参的地址。(不分配内存空间)
(5)函数的调用:
首先,通过函数名找到函数的入口地址;函数地址=函数入口地址==&函数名(函数指针)。
其次,给形参分配空间。
然后,传参:将实参的值传递给形参的地址空间。
再然后,执行函数体里的语句。
最后,函数返回并释放空间。
3、函数的注意事项
(1)函数的传参(传参 VS 传地址)