一. 分析技术大纲。
模块一:linux基础编程
C语言 + linux基础 + 开发板使用 + 交叉工具链 + 文件IO + 链表。
系统编程 + 网络编程
模块二:linux高级编程
C++ QT 数据库,串口编程,音视频。
模块三:
STM32 + linux驱动。
二. C语言大纲
- main函数使用事项,变量在内存中变化,基本数据类型,数据类型在内存中储存方式,运算符号。
- ASCII码,表达式,语句,逗号表达式,三目运算,控制流。
- 函数的调用,函数声明,函数实参与形参关系,函数返回值,函数书写规则,函数名定义,函数调用返回的位置。
- 特殊函数:递归函数,回调函数,内联函数,变参函数。
- 数组定义,数组赋值,数组下标,整型数组,字符数组,指针数组,二维数组。
- 指针定义,指针赋值,指针解引用,空指针,野指针,通用类型指针,整型指针,字符指针,二级指针,数组指针,函数指针,const指针
- 结构体定义,结构体变量,结构体指针,结构体变量与指针如何访问成员的,如何计算结构体的占用内存空间大小,结构体数组。
- 联合体使用,枚举类型,宏定义,头文件书写,条件编译,拆分多个.c文件,编译过程。
- 堆区申请与释放,字符串函数使用。
三. C语言程序框架 – main函数使用
-
C语言程序入口: main函数 -> main() -> 程序从这个函数开始执行。
特点:
1)程序必须以main作为主函数的命名。
2)在程序中,main函数可以在任意位置,都是被第一个执行。并不是第一个函数就会被第一个执行。
3)main函数有且仅有一个。 -
main函数的基本框架。
main() -> main函数的函数头
{ -> main函数的函数体
}
- 在linux下,任何函数就像一个任务,任务都会有返回值。
返回值类型?
返回值类型 main()
例子: int main() -> 无论这个函数将来成功,还是失败,都会返回一个int类型的数据给我。
返回值的值如何返回? -> return就可以将值返回,这个值必须与返回值类型匹配。
return + 返回值结果
例子:
int main()
{
return 0;
}
main函数的返回(在main函数执行了return语句)就意味着程序的退出!
4. 在linux中运行程序时,main函数需要命令行给他传递一些参数。
例如:
./main_test aaa bbb -> 执行main_test时,会传递两个额外的参数给程序
1)main函数如何接受额外命令行参数? -> 使用到两个形式参数argc,argv。
argc -> argument count ->参数数目
argv -> argument value ->参数的值
2)运行程序时,默认接收两个参数。
int main()
int main(void)
int main(int argc,char *argv[]) -> 愿意接收命令行传递
例子:
./xxx aaa -> argc=2 argv[0]:"./xxx" argv[1]:“aaa”
./xxx aaa bbb -> argc=3 argv[0]:"./xxx" argv[1]:“aaa” argv[2]:“bbb”
./xxx aaabbb -> argc=2 argv[0]:"./xxx" argv[1]:“aaabbb”
注意:
- %d ->以十进制输出数据 %s 以字符串形式输出
- ./xxx也是算一个参数的,属于argv[0]
- 没有给参数赋值时,就会给参数赋值为NULL/随机值
- 参数与参数之间使用空格分开!
练习:修改程序,实现无论输入多少个参数,都会打印出所有的参数。
#include <stdio.h>
int main(int argc,char *argv[]) // -> 函数头(函数名字+函数形式参数列表)
{ // -> 函数体(函数的实现功能过程)
/* 参数有多少个,就循环多少次 */
int i;
for(i=0;i<argc;i++)
{
printf("argv[%d]:%s\n",i,argv[i]);//会将第一个./xxx也打印出来,所以最好i + 1
}
return 0; //程序运行到这里,就返回一个0作为函数执行的结果。
}
- 头文件
1)程序中一定要写头文件吗?
不一定,当我们在程序中,调用一个函数之前,必须先声明该函数,声明函数的表达式就是在头文件中,也就是说,当我们调用了某些函数时,就需要包含对应的头文件。
2)头文件作用?
对函数进行声明,在函数调用之前首先一定要声明该函数。
3)函数对应的头文件不需要背,只需要通过man手册进行查询即可。
如果说遇到函数的头文件不知道是哪个?
man 3 printf -> man: 使用man手册
3: 代表查看库函数
printf: 代表你需要查询的东西
SYNOPSIS -> 使用格式
#include <stdio.h> -> 该函数对应的头文件
4)如果调用了函数,但是不包含对应的头文件,那么会怎样?
main_test.c: In function ‘main’:
main_test.c:10:3: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
//未声明
编译警告与编译出错区别:
编译警告: 能生成可执行程序。
编译出错: 不能生成可执行程序。
6. 注释 (注释不一定要写,但是写了注释,原则:精简,易懂)
// -> 单行注释
/* -> 多行注释
…
…
*/
7. 缩进与空格
1)每一个复合语句{}都需要进行缩进
int main(int argc,char **argv[]*) // -> 函数头(函数名字+函数形式参数列表)
{ -> 第一次缩进 // -> 函数体(函数的实现功能过程)
/* 参数有多少个,就循环多少次 */
int i;
/* xxxxx */
for(i=0;i<argc;i++)
{ -> 第二次缩进
printf("argv[%d]:%s\n",i,argv[i]);
}
return 0; //程序运行到这里,就返回一个0作为函数执行的结果。
}
2)什么时候需要空格?
头文件与main函数之间
功能模块之间
return语句之前
8. 编译程序
gcc main_test.c -o main_test
9. 运行
./main_test
四. 数据类型
1. 什么是数据类型?
数据类型描述了一个变量究竟是存放什么类型的数据。
数据类型分为基本数据类型和非基本数据类型。
基本数据类型: char short int long float double
非基本数据类型:就是用户自定义类型,例如数组,指针,结构体…
- 研究下基本数据类型在内存中占用的空间?
char -> 字符 ‘a’,‘x’
short -> 短整型
int -> 整型
long -> 长整型
float -> 单精度浮点型
double -> 双精度浮点型
程序在内存上运行时,会不断地申请空间,不同数据类型会占用不同的空间。
究竟这些基本数据类型占用多少个字节?
计算内存空间大小: sizeof()
#include <stdio.h>
int main(int argc,char *argv[])
{
printf("%d\n",sizeof(char));//1
printf("%d\n",sizeof(short));//2
printf("%d\n",sizeof(int));//4
printf("%d\n",sizeof(long));//4 -> 32位:4 不同电脑位数长度不一样 64位:8
printf("%d\n",sizeof(float));//4
printf("%d\n",sizeof(double));//8
return 0;
}
结论: 基本数据类型占用空间的大小由编译系统来决定的。
五. 如何定义变量?
- 公式: 数据类型 + 变量名
数据类型: 从基本数据类型中选择一个,也可以从非基本数据类型中选择。
变量名:有一套定义的规则
1)只能由字母,数字,下划线组成。 a+5
2)不能以数字开头。 5a
3)不能与系统的关键字重名。 return For->正确 for->错误
例子: int a;
定义了一个整型变量 -> 错误!
如何描述一个变量
在内存中连续申请4个字节,然后使用变量a间接访问这片内存空间。
举例子:
int main(int argc,char *argv[])
{
int a; -> 申请4个字节
return 0; -> 释放掉这4个字节
}
2. 内存分配原则? -> 连续空闲不确定。
1)分配内存空间时,内存一定是连续的。
2)分配内存空间时,一定是空闲(之前的变量已经申请过的空间就不会再被申请到)
3)分配内存空间时,位置是不确定。
六. 运算符
/ + - * % = & && | || == != ++ – += -= *= /= %/
= -> 赋值,把等于号右边的值赋值给左边的值。
/ * + -
使用运算符需要注意的地方
% -> 等号的两边不能是小数
== -> 判断==的右边的值是否等于左边的值
!= -> 判断!= 的右边的值是否不等于左边的值
& -> 位与
&& -> 逻辑与
| -> 位或
|| -> 逻辑或
逻辑运算原则:非0即真。
++: 自身加1,再把结果赋值给自身
–: 自身减1,再把结果赋值给自身
a++:先运算整个表达式,再加1
++a:先加1,再运算整个表达式
a–:先运算整个表达式,再减1
–a:先减1,再运算整个表达式
a=10;
+=: a+=5; // a = a + 5 //15
a=10;
-=: a-=5; // a=a-5 //5
a=10;
= a=5// a=a5 // a=105=50
a=10;
/= a/=5 // a=a/5 // a=10/5=2
a=10;
%= a%=5 // a=a%5 // a = 10%5=0
七. 变量的赋值,以及作用域,生命周期。
- 变量的赋值
使用"="对变量进行赋值。把等号右边的值赋值给左边的值。
1)定义变量的同时初始化。
int a = 100;
2)先定义,后初始化
int a; -> 先申请空间,然后使用变量a间接访问内存空间,再赋值一个随机值。
a = 100; -> 把100存放到a所代表的空间中
- 变量生命周期与作用域?
生命周期:这个变量从什么时候开始出现在内存空间中到什么时候从内存释放掉的这个过程。
作用域:这个变量在程序中能够用到的地方。
局部变量 Vs 全局变量
1)什么是局部变量?什么是全局变量?
在程序中函数体内定义的变量,就是局部变量,在函数体外部的定义的变量,就是全局变量。
全局变量在使用的时候一定要从上往下,作用域只能往下,不能往上
int b; -> 全局变量
int main(int argc,char *argv[])
{
int a; -> 局部变量
return 0;
}
2)两者在内存空间中申请的位置?
#include <stdio.h>
int c = 10; -> 10在rodata段,变量c的空间在data段
int d; -> 变量d的空间在bss段
int main(int argc,char *argv[])
{
int a; -> 栈区
int b = 100; -> 栈区,100在rodata段
return 0;
}
3)初始化值
局部变量未初始化 -> 随机值
全局变量未初始化 -> 0
4)生命周期
局部变量在定义在该变量的函数结束时,就会释放空间。
全局变量一开始就会申请,程序结束,全局变量才会释放。
5)作用域
局部变量作用范围就是函数的内部,全局变量作用域就是整个程序。
思考题:
int main(int argc,char *argv[])
- 形式参数属于局部变量,还是全局变量?
局部变量,当该函数返回时,该函数中的形式参数也需要释放。
int fun()
{
int a;
}
int main(int argc,char *argv[])
{
int a;
fun();
}
-
能不能在不同的函数中申请相同的变量名的变量。
能。 -
全局变量作用域只能往下,不能往上。
练习: 画内存分析图,求出以下程序的结果。
int my_fun(int z)
{
z = z/5;
return z;
}
int fun(int x,int y)
{
int c;
c = x + y;
my_fun( c );
return c;
}
int main(int argc,char *argv[])
{
int a = 10,b = 5;
int ret = fun(a,b);
ret = my_fun(ret);
printf("ret = %d\n",ret); //3
return 0;
}