334 lines
8.6 KiB
Markdown
334 lines
8.6 KiB
Markdown
# 目录
|
||
- [目录](#目录)
|
||
- [第一章 绪论](#第一章-绪论)
|
||
- [第一节](#第一节)
|
||
- [C语言发展史](#c语言发展史)
|
||
- [C语言特点](#c语言特点)
|
||
- [C语言学习建议](#c语言学习建议)
|
||
- [课程思路](#课程思路)
|
||
- [课程平台](#课程平台)
|
||
- [第二节](#第二节)
|
||
- [神一般的`Hello world!`](#神一般的hello-world)
|
||
- [源文件到可执行文件](#源文件到可执行文件)
|
||
- [gcc](#gcc)
|
||
- [make](#make)
|
||
- [vim](#vim)
|
||
- [第三节](#第三节)
|
||
- [基本概念](#基本概念)
|
||
- [以`Hello world`为例对写程序的思路提出如下要求](#以hello-world为例对写程序的思路提出如下要求)
|
||
- [1. 头文件正确包含的重要性](#1-头文件正确包含的重要性)
|
||
- [2. 以函数为单位来进行程序编写](#2-以函数为单位来进行程序编写)
|
||
- [3. 声明部分+实现部分](#3-声明部分实现部分)
|
||
- [4. `return 0/exit(0)`](#4-return-0exit0)
|
||
- [5. 多用空格空行](#5-多用空格空行)
|
||
- [6. 添加注释](#6-添加注释)
|
||
- [算法](#算法)
|
||
- [程序](#程序)
|
||
- [进程](#进程)
|
||
- [防止写越界,防止内存泄漏,谁打开谁关闭,谁申请谁释放](#防止写越界防止内存泄漏谁打开谁关闭谁申请谁释放)
|
||
|
||
|
||
# 第一章 绪论
|
||
|
||
课程地址,[史上最强最细腻的linux嵌入式C语言学习教程【李慧芹老师】_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV18p4y167Md/?spm_id_from=333.999.0.0&vd_source=4e03f52e94cfa281cde032856b1f93a7)。
|
||
|
||
## 第一节
|
||
|
||
### C语言发展史
|
||
|
||
| 时间 | 发展 |
|
||
| ---- | -------------------- |
|
||
| 1960 | 原型A语言->ALGOL语言 |
|
||
| 1963 | CPL语言 |
|
||
| 1967 | BCPL |
|
||
| 1967 | B语言 |
|
||
| 1973 | C语言 |
|
||
|
||
|
||
|
||
### C语言特点
|
||
|
||
1. 基础性语言
|
||
2. 语法间接、紧凑、方便、灵活
|
||
3. 运算符,数据结构丰富
|
||
4. 结构化,模块化编程
|
||
5. 移植性好,执行效率高
|
||
6. 允许直接对硬件操作
|
||
|
||
|
||
|
||
### C语言学习建议
|
||
|
||
1. 概念的正确性
|
||
2. 动手能力
|
||
3. 阅读优秀的程序段
|
||
4. 大量练习,面试题
|
||
|
||
|
||
|
||
### 课程思路
|
||
|
||
1. 基础概念
|
||
2. 数据类型,运算符和表达式
|
||
3. 输入输出专题
|
||
4. 流程控制
|
||
5. 数组
|
||
6. 指针
|
||
7. 函数
|
||
8. 构造类型
|
||
9. 动态内存管理
|
||
10. 调试工具(gdb,make)
|
||
11. 常用库函数
|
||
|
||
|
||
|
||
### 课程平台
|
||
|
||
* 老师:64位的`redhat6`,`vim`,`gcc4.4.6(make)`
|
||
* 本人:`WSL-debian`,`vscode(remote ssh)`,`gcc12.2.0(make)`,`ohmyzsh(theme:eastwood)`
|
||
|
||
我的`gcc`版本比较新,所以很多提示比较完善,老师这个课程是很早的课,那个时候的gcc提示没有现在厉害,所以还是要结合老师的视频的思路,在当时提示不完善的时候是如何手撕代码分析出来的。
|
||
|
||
|
||
|
||
## 第二节
|
||
|
||
### 神一般的`Hello world!`
|
||
|
||
```c
|
||
//hello.c
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
|
||
#if 0
|
||
void main(void);
|
||
void main(int argc, char **argv);// char *argv[]
|
||
int main(int argc, char **argv);
|
||
int main(void);
|
||
#endif
|
||
|
||
int main(void)
|
||
{
|
||
printf("Hello world!\n");
|
||
|
||
exit(0);
|
||
}
|
||
```
|
||
|
||
用那种`main`得看编译器环境,目前在`linux`的`gcc`下,`int main(void);`常用,需要传参用`int main(int argc, char **argv);`。
|
||
|
||
|
||
|
||
### 源文件到可执行文件
|
||
|
||
#### gcc
|
||
|
||
C源文件=>预处理=>编译=>汇编=>链接=>可执行文件
|
||
|
||
> 所有`#`后面的代码都在预处理解决
|
||
|
||
```bash
|
||
# 单步执行
|
||
gcc -E hello.c > hello.i # 预处理生成hello.i
|
||
gcc -S hello.i # 编译生成hello.s
|
||
gcc -c hello.s # 汇编生成hello.o
|
||
gcc hello.o -o hello # 链接生成hello
|
||
./hello # 运行
|
||
Hello world!
|
||
|
||
# 一次到位
|
||
gcc hello.c # 得到a.out可执行文件
|
||
# 指定名字
|
||
gcc hello.c -o myhello # 得到myhello可执行文件
|
||
```
|
||
|
||
|
||
|
||
#### make
|
||
|
||
```bash
|
||
$ make hello
|
||
cc hello.c -o hello
|
||
# cc就是make选择的编译器,这里是gcc
|
||
$ ls
|
||
hello hello.c
|
||
```
|
||
|
||
|
||
|
||
### vim
|
||
|
||
老师对于常用的`.vimrc`做了一些介绍,笔者建议`vscode`的`remote-ssh`插件,可以在win的vscode编辑器享受linux的环境。
|
||
|
||
|
||
|
||
## 第三节
|
||
|
||
### 基本概念
|
||
|
||
#### 以`Hello world`为例对写程序的思路提出如下要求
|
||
|
||
##### 1. 头文件正确包含的重要性
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
int main(void)
|
||
{
|
||
int *p = NULL;
|
||
int i;
|
||
p = malloc(sizeof(int));
|
||
if (p == NULL)
|
||
return -1;
|
||
printf("Hello world!\n");
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
这里我的gcc版本比较新,所以和老师的不一致,总之老师的意思就是
|
||
|
||
> 不要忽略gcc的警告!!!
|
||
|
||
```bash
|
||
# 通过 -Wall 参数让gcc显示全部警告
|
||
# 注意'W'要大写
|
||
$ gcc hello.c -Wall
|
||
hello.c: In function ‘main’:
|
||
hello.c:7:9: warning: implicit declaration of function ‘malloc’ [-Wimplicit-function-declaration]
|
||
7 | p = malloc(sizeof(int));
|
||
| ^~~~~~
|
||
hello.c:2:1: note: include ‘<stdlib.h>’ or provide a declaration of ‘malloc’
|
||
1 | #include <stdio.h>
|
||
+++ |+#include <stdlib.h>
|
||
2 |
|
||
hello.c:7:9: warning: incompatible implicit declaration of built-in function ‘malloc’ [-Wbuiltin-declaration-mismatch]
|
||
7 | p = malloc(sizeof(int));
|
||
| ^~~~~~
|
||
hello.c:7:9: note: include ‘<stdlib.h>’ or provide a declaration of ‘malloc’
|
||
hello.c:6:9: warning: unused variable ‘i’ [-Wunused-variable]
|
||
6 | int i;
|
||
| ^
|
||
|
||
```
|
||
|
||
* 例如说这个变量`i`定义了但是没用上,就是你清楚来龙去脉可以忽略的警告。
|
||
* 而`malloc`那个警告就是在告诉你`malloc`所属的`stdlib.h`库未引入。
|
||
* C语言程序往往如果能把警告都消除,错误也能解决。例如:
|
||
|
||
```c
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <errno.h>
|
||
|
||
int main()
|
||
{
|
||
FILE *fp;
|
||
|
||
fp = fopen("tmp", "r");
|
||
if (fp == NULL)
|
||
{
|
||
fprintf(stderr, "fopen():%s\n", strerror(errno));
|
||
exit(1);
|
||
}
|
||
puts("ok!");
|
||
exit(0);
|
||
}
|
||
```
|
||
|
||
这个程序运行报段错误,这个时候,查看一下警告就是问题所在。
|
||
|
||
```bash
|
||
$ ./a
|
||
[1] 1960 segmentation fault ./a
|
||
|
||
$ gcc a.c -Wall
|
||
a.c: In function ‘main’:
|
||
a.c:12:41: warning: implicit declaration of function ‘strerror’; did you mean ‘perror’? [-Wimplicit-function-declaration]
|
||
12 | fprintf(stderr, "fopen():%s\n", strerror(errno));
|
||
| ^~~~~~~~
|
||
| perror
|
||
a.c:12:35: warning: format ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘int’ [-Wformat=]
|
||
12 | fprintf(stderr, "fopen():%s\n", strerror(errno));
|
||
| ~^ ~~~~~~~~~~~~~~~
|
||
| | |
|
||
| | int
|
||
| char *
|
||
| %d
|
||
```
|
||
|
||
用`%s`需要的类型是`char *`,但是`strerror(errno)`的类型是`int`,它是所以是`int`,是因为C语言对没有找到原型的函数返回值都是`int`,所以根源在于`strerror`函数没有原型,没有引入需要的`string.h`库。加入这个库重新运行。
|
||
|
||
```bash
|
||
[main][~/LinuxC/Chapter1/Section3]$ make a
|
||
cc a.c -o a
|
||
[main][~/LinuxC/Chapter1/Section3]$ ./a
|
||
fopen():No such file or directory
|
||
```
|
||
|
||
|
||
|
||
##### 2. 以函数为单位来进行程序编写
|
||
|
||
main`是特殊的定义,其实就是一个正在运行的线程。其实对于内核而言,是只有进程的概念,且与我们理解的进程、线程不一样。
|
||
|
||
|
||
|
||
##### 3. 声明部分+实现部分
|
||
|
||
早期的编译器要求变量先定义后使用。
|
||
|
||
##### 4. `return 0/exit(0)`
|
||
|
||
结束当前进程,是给父进程看的。
|
||
|
||
对于带有`return 0`的程序:
|
||
|
||
```bash
|
||
[main][~/LinuxC/Chapter1/Section3]$ ./hello
|
||
Hello world!
|
||
[main][~/LinuxC/Chapter1/Section3]$ echo $?
|
||
0
|
||
```
|
||
|
||
如果不带`return 0`:
|
||
|
||
老师演示的返回值是13,是`printf`的返回的`Hello world!\n`的长度。
|
||
|
||
在我本地无法复现,新版本可能改了。
|
||
|
||
##### 5. 多用空格空行
|
||
|
||
##### 6. 添加注释
|
||
|
||
```c
|
||
//单行注释
|
||
|
||
/*
|
||
* 多行注释
|
||
* 多行注释
|
||
* 多行注释
|
||
*/
|
||
|
||
//大段的注释
|
||
#if 0
|
||
func(){
|
||
|
||
}
|
||
#endif
|
||
```
|
||
|
||
#### 算法
|
||
|
||
解决问题的方法。(流程图,NS图,FSM有限状态机)
|
||
|
||
#### 程序
|
||
|
||
用某种语言实现算法
|
||
|
||
#### 进程
|
||
|
||
#### 防止写越界,防止内存泄漏,谁打开谁关闭,谁申请谁释放
|
||
|
||
|
||
|