C语言学习大纲
- 先写个框架,后面再根据项目情况进行拓展
- 最近先把c/c++猛过一遍
- 然后开始做几个项目,边做边写
1. C语言基础
- C语言简介
- C语言在嵌入式系统和Linux开发中的重要性
嵌入式系统:
C语言广泛用于嵌入式系统开发,因其能够直接操作硬件并高效管理资源。
嵌入式设备如微控制器、传感器和通信模块通常采用C语言编写固件。
Linux开发:
Linux内核和许多系统工具、库都是用C语言编写的。
C语言在Linux系统编程中被用于编写驱动程序、系统调用接口和高性能应用程序。
- C语言在嵌入式系统和Linux开发中的重要性
- 开发环境
- 安装编译器(如GCC)
安装地址:https://sourceforge.net/projects/mingw/ - 使用集成开发环境(IDE),如Code::Blocks、VSCode、CLion等
- 安装编译器(如GCC)
2. 基本语法
- C程序的结构
- 头文件和预处理指令:
头文件包含库函数声明,如#include <stdio.h>。
常见头文件及其解释:
- 头文件和预处理指令:
1.<stdio.h>
作用:标准输入输出头文件。
包含内容:printf, scanf, fprintf, fscanf, getchar, putchar, fgets, fputs等函数的声明。
2.<stdlib.h>
作用:标准库头文件,包含了通用工具函数。
包含内容:malloc, free, calloc, realloc, atoi, atof, rand, srand, exit, qsort, bsearch等函数的声明。
预处理指令用于定义常量、宏和包含其他文件,如#define和#include。
3.<string.h>
作用:字符串处理头文件。
包含内容:strlen, strcpy, strncpy, strcat, strncat, strcmp, strncmp, strchr, strstr, memcpy, memset等函数的声明。
4.<math.h>
作用:数学函数头文件。
包含内容:sin, cos, tan, asin, acos, atan, exp, log, log10, sqrt, pow, ceil, floor, fabs等函数的声明。
- main函数
主函数是C程序的入口点,通常定义为int main()。
main函数中包含程序的主要逻辑,返回值通常为0表示成功执行。 - 注释
单行注释使用//,多行注释使用/* … */。 - 数据类型
- 基本数据类型(int, char, float, double)
- 基本定义和使用
- 基本数据类型(int, char, float, double)
int:用于表示整数。
int a = 10;
char:用于表示单个字符。
char c = 'A';
float:用于表示单精度浮点数。
float f = 3.14f;
double:用于表示双精度浮点数。
double d = 3.14159;
- 枚举类型(enum)
定义:
枚举类型(enum)用于定义一组命名的整型常量,提升代码可读性和维护性。
语法:
enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY };
使用:
enum Day today = MONDAY;
特点:
1.每个枚举常量的值默认从0开始,依次递增。
2.可以手动指定枚举常量的值。比如第一个默认为1,这样星期一到星期日的值就是1到7。
enum Day { SUNDAY =1, MONDAY, TUESDAY, WEDNESDAY , THURSDAY , FRIDAY, SATURDAY };
- 类型修饰符(short, long, signed, unsigned)
short:修饰整数类型,表示短整型,占用内存通常较小。
short int a;
short b; // 同样表示short int
long:修饰整数类型,表示长整型,占用内存通常较大。
long int c;
long d; // 同样表示long int
signed:表示有符号类型,可以存储正负整数。默认情况下,整数类型是有符号的。
signed int e;
signed f; // 同样表示signed in
unsigned:表示无符号类型,只能存储非负整数,范围是0到最大值。
unsigned int g;
unsigned h; // 同样表示unsigned int
- 变量和常量
- 变量的声明和初始化
变量是存储数据的命名存储位置,可以在程序执行期间改变其值。在C语言中,声明变量的同时可以对其进行初始化。
声明:
- 变量的声明和初始化
int age;
初始化:
int age = 25;
- 常量的定义
定义:
常量(constant)是其值在程序运行期间不会改变的变量。
常量的定义方式主要包括:
1.#define预处理指令
#define是C语言中的预处理指令,用于定义宏常量。它在编译时将所有出现该常量的地方替换为其值。
#define PI 3.14159
#define MAX_SIZE 100
特点:
宏常量没有类型检查。
宏常量不会占用内存空间。
2.const
const关键字用于定义具有类型的常量。const常量在程序运行期间不能被修改。
const double PI = 3.14159;
3.enum
使用枚举类型定义相关的整型常量,适用于表示一组相关值的常量。
- 输入与输出
printf 函数:
用途:格式化输出数据到标准输出(如屏幕)。
printf("Integer: %d, Float: %f, Char: %c, String: %s\n", 10, 3.14, 'A', "Hello");
scanf 函数:
用途:从标准输入(如键盘)读取格式化输入数据。
scanf("%d %f %c %s", &num, &pi, &ch, str);
3. 操作符与表达式
-
算术操作符
用途:执行基本的数学运算。
操作符:+(加),-(减),*(乘),/(除),%(取模)。 -
关系操作符
用途:比较两个值,返回真或假。
操作符:==(等于),!=(不等于),>(大于),<(小于),>=(大于等于),<=(小于等于)。 -
逻辑操作符
用途:执行逻辑运算,返回真或假。
操作符:&&(逻辑与),||(逻辑或),!(逻辑非)。 -
位操作符
用途:对整数类型的位进行操作。
操作符:&(按位与),|(按位或),^(按位异或),~(按位取反),<<(左移),>>(右移)。 -
赋值操作符
用途:给变量赋值。
操作符:=(赋值),+=(加并赋值),-=(减并赋值),*=(乘并赋值),/=(除并赋值),%=(取模并赋值)。 -
其他操作符
-
条件操作符(三目操作符)
用途:简洁的条件判断。
语法:condition ? expression1 : expression2
示例:int max = (a > b) ? a : b; -
sizeof操作符
用途:返回数据类型或变量的大小(以字节为单位)。
语法:sizeof(type or variable)
示例:int size = sizeof(int); -
逗号操作符
用途:顺序执行一系列表达式,返回最后一个表达式的值。
语法:expression1, expression2, …, expressionN
示例:int x = (a = 1, b = 2, a + b); // x为3 -
类型转换操作符
用途:强制转换变量的类型。
语法:(type) expression
示例:float f = (float) 10 / 3; // f为3.3333
-
4. 控制结构
-
条件语句
-
if, if-else, else-if
-
switch-case
用途:用于执行多分支选择操作,根据变量的值执行不同的代码块。switch (expression) { case constant1: // 代码块1 break; case constant2: // 代码块2 break; ... default: // 默认代码块 }
-
-
循环语句
-
for循环
-
while循环
-
do-while循环
用途:用于执行至少一次的循环操作,然后根据条件决定是否继续执行。do { // 循环体代码 } while (condition);
-
-
跳转语句
- break, continue
- goto
- return
5. 函数
-
函数的定义与调用
定义:指定函数的名称、返回类型和参数列表。int add(int a, int b) { return a + b; }
调用:使用函数名称和参数调用函数。
int result = add(2, 3);
-
参数传递
-
值传递
将实际参数的值传递给函数的形参,函数内对参数的修改不会影响实际参数。 -
指针传递
将实际参数的地址传递给函数的形参,函数内对参数的修改会影响实际参数。void increment(int *a) { (*a)++; }
-
-
递归函数
定义:一个函数调用自身。 -
作用域和生命周期
-
局部变量和全局变量
局部变量:在函数或代码块内定义,作用域仅限于函数或代码块内部,生命周期在函数调用期间。void func() { int localVar = 10; }
全局变量:在所有函数外定义,作用域为整个程序,生命周期为程序的整个运行期。
-
静态变量
1.局部静态变量
定义:在函数内部使用static关键字定义的变量。
作用域:局限于函数内部。
生命周期:从程序开始到结束,值在函数调用之间保持不变。
局部静态变量只能在定义它的函数内部使用,函数外部不能引用。void func() { static int count = 0; count++; printf("Count: %d\n", count); }
2.全局静态变量
定义:在所有函数外部使用static关键字定义的变量。
作用域:局限于定义它的文件内部。
生命周期:从程序开始到结束。static int globalVar = 0; void func1() { globalVar++; printf("globalVar: %d\n", globalVar); } void func2() { globalVar += 2; printf("globalVar: %d\n", globalVar); }
全局静态变量可以在同一个源文件中的不同函数之间共享,但不能在其他源文件中访问。
-
6. 指针
对于指针学习有困惑的人,这里是我在网上找到的别人对于指针的理解:
假设你的朋友想知道你住在哪里。把你的家乡想象成内存,里面存储着很多房子(变量),还有定位它们的地址(指针)。你不会向朋友展示你家的照片,并期望他们能很快找到它。因此,你会给你的朋友你家的地址(一个指针),这样他们就能轻松找到它。
现在,让我们用适当的术语来表达:
C 想知道你引用的变量是什么。你有内存,里面存储着很多变量,还有定位它们的地址。你不能给 C 你的变量名,并期望 C 在所有其他变量中找到它,所以你将其地址引用为指针,这样 C 就能轻松找到它。
-
指针基础
定义:指针是存储变量地址的变量。
声明:int ptr; 表示一个指向整数的指针。
获取地址:使用&操作符,例如 ptr = &var;。
解引用:使用操作符访问指针指向的值,例如 *ptr = 10;。 -
指针与数组
数组名:数组名本身就是一个指向第一个元素的指针。
指针运算:可以通过指针遍历数组,例如 *(arr + i) 等价于 arr[i]。 -
指针与字符串
字符串:字符串在C中表示为字符数组,可以使用指针操作。
字符串操作:例如 char *str = “Hello”;,可以通过指针访问每个字符。 -
指针与函数
函数指针:用于指向函数的指针。
声明:int (*funcPtr)(int, int); 表示一个指向返回类型为int,参数为int, int的函数的指针。
使用:例如 funcPtr = &functionName;,调用时 (*funcPtr)(arg1, arg2);。 -
指针数组和函数指针
指针数组:数组的每个元素都是一个指针,例如 int *arr[10]; 表示一个包含10个指针的数组。
函数指针数组:数组的每个元素都是一个函数指针,例如 int (*funcArr[10])(int, int);。
ps:简单辨别
1.简单指针声明:int *ptr; 表示 ptr 是一个指向 int 类型的指针。
2.指针数组:int *arr[10]; 表示 arr 是一个包含10个指向 int 类型的指针的数组。
3.数组指针:int (*ptr)[10]; 表示 ptr 是一个指向包含10个 int 类型元素的数组的指针。
- 动态内存分配
-
malloc, calloc, realloc, free
-
malloc
用途:分配指定字节数的内存,未初始化。
语法:void* malloc(size_t size);
返回值:成功时返回指向已分配内存的指针,失败时返回NULL。int *arr = (int *)malloc(10 * sizeof(int)); if (arr == NULL) { // 内存分配失败 }
-
calloc
用途:分配指定数量和大小的内存块,并初始化为零。
语法:void* calloc(size_t num, size_t size);
返回值:成功时返回指向已分配内存的指针,失败时返回NULL.int *arr = (int *)calloc(10, sizeof(int)); if (arr == NULL) { // 内存分配失败 }
-
realloc
用途:调整已分配内存块的大小,可以扩展或缩小内存块。
语法:void* realloc(void *ptr, size_t size);
返回值:成功时返回指向重新分配内存的指针,失败时返回NULL。int *arr = (int *)realloc(arr, 20 * sizeof(int)); if (arr == NULL) { // 内存重新分配失败 }
-
free
用途:释放之前通过malloc、calloc或realloc分配的内存。
语法:void free(void *ptr);free(arr);
例子:
#include <stdio.h> #include <stdlib.h> int main() { // 动态分配内存以存储10个整数 int *arr = (int *)malloc(10 * sizeof(int)); // 检查内存分配是否成功 if (arr == NULL) { printf("Memory allocation failed\n"); return 1; // 返回错误码 } // 初始化数组 for (int i = 0; i < 10; i++) { arr[i] = i + 1; } // 打印数组 for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } printf("\n"); // 释放内存 free(arr); return 0; }
-
-
7. 数组和字符串
- 一维数组
- 多维数组
- 字符数组与字符串
- 字符串处理函数
8. 结构体与联合体
- 结构体
- 联合体
- 枚举
9. 嵌入式系统编程
- 嵌入式系统概述
- 嵌入式系统的组成和特点
- 嵌入式C语言编程的基础
- 嵌入式开发工具
- 嵌入式开发板(如ESP32, STM32)
- 嵌入式开发环境设置
- 硬件接口编程
- GPIO编程
- 定时器和中断
- 串行通信(UART, SPI, I2C)
- 嵌入式操作系统
- FreeRTOS
- 嵌入式Linux
10. Linux系统编程
- Linux基础
- Linux操作系统概述
- 基本的Linux命令
- Linux系统调用
-
文件操作
打开和关闭文件-
fopen:用于打开文件,返回一个FILE*指针。
语法:FILE* fopen(const char *filename, const char *mode);
实例:
FILE *fp = fopen("example.txt", "r"); if (fp == NULL) { printf("File could not be opened\n"); return 1; }
-
fclose:用于关闭文件
语法:int fclose(FILE *fp);
fclose(fp);
读写文件
1.fgets 和 fputs:用于从文件读取字符串和向文件写入字符串。语法:
char* fgets(char *str, int n, FILE *fp); int fputs(const char *str, FILE *fp);
示例:
char buffer[100]; if (fgets(buffer, 100, fp) != NULL) { printf("Read from file: %s", buffer); } fputs("Hello, World!\n", fp);
2.fread 和 fwrite:用于从文件读取数据块和向文件写入数据块。
语法:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *fp); size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp);
示例:
int data[10]; fread(data, sizeof(int), 10, fp); fwrite(data, sizeof(int), 10, fp);
3.fprintf 和 fscanf:用于格式化输入输出。
语法:int fprintf(FILE *fp, const char *format, ...); int fscanf(FILE *fp, const char *format, ...);
示例:
int x; fprintf(fp, "Value: %d\n", x); fscanf(fp, "%d", &x);
-
-
进程管理
-
内存管理
-
- 多线程编程
- pthread库
- 线程同步(互斥锁、条件变量)
- 网络编程
- 套接字编程
- TCP/IP协议
11. 实践项目
- 嵌入式项目
- 简单的传感器读取与显示
- 温度监控系统
- Linux项目
- 基本的Shell工具开发
- 简单的网络服务器
12. 调试与优化
- 调试工具
- GDB调试
- 嵌入式调试器(JTAG, SWD)
- 性能优化
- 代码优化技巧
- 内存优化
- 多项目/多模块管理化
-
使用头文件和源文件
在C语言中,多模块管理通常通过将代码分割为多个头文件(.h)和源文件(.c)来实现。这种方法提高了代码的组织性和可维护性。-
头文件
作用:声明函数、变量和数据类型。
语法:// file1.h #ifndef FILE1_H #define FILE1_H void func1(); void func2(); #endif // FILE1_H
-
源文件
作用:定义函数和变量。// file1.c #include "file1.h" #include <stdio.h> void func1() { printf("Function 1\n"); } void func2() { printf("Function 2\n"); }
-
使用 extern 关键字
作用:声明在其他文件中定义的变量和函数。
语法:extern int sharedVar; extern void sharedFunc();
-
-
示例项目结构
file1.h:#ifndef FILE1_H #define FILE1_H void func1(); void func2(); #endif // FILE1_H
file1.c:
#include "file1.h" #include <stdio.h> void func1() { printf("Function 1\n"); } void func2() { printf("Function 2\n"); }
main.c:
#include "file1.h" int main() { func1(); func2(); return 0; }
-
编译和链接
编译多文件项目时,需要将所有源文件一起编译并链接。例如,使用GCC编译器:gcc -o program main.c file1.c
-