📚 8.16 课程笔记整理
第一部分:C语言概述与硬件基础
1. C语言的应用领域
- 底层开发基石:AI模型底层实现、操作系统、CAD制图软件、编程语言(Java/Python)的底层实现。
- 硬件交互场景:航天军工、电子锁、机器人、无人机等需要直接控制硬件的设备。
- ⭐ 老师强调 :虽然上层应用多用Python/Java,但底层动作对接和核心性能模块依然依赖C语言。C语言是"母语法",理解C语言是掌握计算机底层原理的关键。
2. CPU、内存与显卡 (GPU)
2.1 核心硬件分工
| 硬件 | 职责 | 特点 |
|---|---|---|
| CPU | 中央处理器,负责逻辑运算与整体协调 | 单核强,串行处理 |
| GPU | 图形处理器,负责并行计算 | 核心数多(如RTX 4090有上万CUDA核心),擅长矩阵运算、屏幕渲染、AI训练 |
| 内存 | 程序运行时的临时存储 | 断电丢失,物理最小单元为1字节 |
| 硬盘 | 持久化存储 | 断电不丢失 |
2.2 ⭐ 老师强调:内存管理机制
- 物理单元 :内存最小存储单元是 1字节(8比特)。
- 操作系统分配单位 :为提高效率,OS以 4KB 为单位进行内存分配和读写。
- 比喻:就像去超市买东西,虽然可以只买一颗糖(1字节),但收银台默认用大袋子(4KB)装。
- 影响 :即使定义一个2字节的
short变量,系统也会划出4KB空间,剩余空间被浪费。这就是"内存开销"。
- 数组的优势 :数组将同类型数据集中连续存放,实现高密度存储,极大节省内存空间并提升访问效率。
第二部分:基本数据类型与编码
3. C语言六种基本类型
| 类型分类 | 关键字 | 位数/大小 | 说明 |
|---|---|---|---|
| 整数类型 | short |
16位 (2字节) | 适合存储屏幕坐标等数千级数值 |
| 整数类型 | int |
32位 (4字节) | 最常用整数类型 |
| 整数类型 | long |
32/64位 | ⭐ 注意:位数取决于编译器和操作系统(32位系统下可能为32位,64位系统下为64位) |
| 浮点类型 | float |
32位 | 精度随数值大小变化 |
| 浮点类型 | double |
64位 | 双精度,范围更大 |
| 字符类型 | char |
8位 (1字节) | 存储ASCII编码值 |
⭐ 老师强调重点
- 位数非固定 :与Java不同,C语言的
long、double位数受环境影响。64位系统可兼容32位程序,但数据类型位数由编译器决定。 - 存储空间固定 :同类型变量占用空间相同,与数值大小无关。例如
short x=13和short y=9都占16位,小数值的二进制高位补零对齐。 - 浮点数精度特性 :离零点越近精度越高(密集),离零点越远精度越低(稀疏)。大数值时浮点数精度可能不如整数。
4. 编码与字符存储
4.1 编码本质
编码是一套映射关系,将字符/符号转换为计算机可处理的二进制数据。
- ASCII码 :最短8比特编码。
'A'=65,'a'=97,'0'=48。 - ⭐ 老师强调 :计算机底层存储的是编码值(数字),而非字符图形。显示时系统根据编码值从图形库中还原形状。
4.2 编码兼容性
- 存储与读取必须使用相同编码格式,否则出现乱码。
- 不同软件能互相打开文件的前提是支持相同的编码。
- C语言代码本质是文本文件,只要编码一致(如UTF-8),记事本写的代码也能被IDE正确编译。
4.3 代码示例:字符编码验证
#include <stdio.h>
int main() {
// ⭐ 老师强调:char本质存的是编码值(数字)
char c = '7';
// 以整数形式输出 -> 显示编码值55
printf("字符'7'的ASCII编码值: %d\n", c);
// 以字符形式输出 -> 显示字符'7'
printf("以字符形式输出: %c\n", c);
// char可以参与数学运算和比较
char a = 'A'; // 65
char b = 'B'; // 66
printf("'A' + 1 = %c\n", a + 1); // 输出 B
printf("'A' < 'B' ? %d\n", a < b); // 输出 1 (true)
// ⭐ 转义字符:让特殊符号丧失原有功能变成普通字符
char quote = '\''; // 单引号本身
char newline = '\n'; // 换行符
printf("输出单引号: %c%c", quote, newline);
return 0;
}
运行结果:
字符'7'的ASCII编码值: 55
以字符形式输出: 7
'A' + 1 = B
'A' < 'B' ? 1
输出单引号: '
第三部分:数组与内存布局
5. 数组 (Array)
5.1 核心特性
- 连续存储:元素在物理内存地址上紧密排列。
- 同类型同大小:每个元素占用空间相同,系统可精准计算偏移量。
- ⭐ 老师强调 :数组首地址即第一个元素的地址。访问第n个元素(下标n-1)的地址公式为:
首地址 + (n-1) × 单位长度。
5.2 代码示例:数组内存与越界
#include <stdio.h>
int main() {
int arr[8] = {10, 20, 30, 40, 50, 60, 70, 80};
// ⭐ 数组名即首元素地址
printf("数组首地址: %p\n", (void*)arr);
printf("arr[0]地址: %p\n", (void*)&arr[0]);
// ⭐ 地址计算:第6个元素(arr[5]) = 首地址 + 5×4
printf("arr[5]地址: %p\n", (void*)&arr[5]);
printf("地址偏移量(字节): %ld\n", (char*)&arr[5] - (char*)arr); // 20
// ⭐ 老师强调:索引从0开始,第n个元素下标为n-1
printf("arr[5]的值: %d\n", arr[5]); // 60
// ⚠️ 危险操作:数组越界不会直接报错,但会修改非法内存
// arr[100] = 999; // 溢出攻击风险!可能导致程序崩溃
return 0;
}
运行结果(地址值因环境而异):
数组首地址: 0x7ffd5e3a2b80
arr[0]地址: 0x7ffd5e3a2b80
arr[5]地址: 0x7ffd5e3a2b94
地址偏移量(字节): 20
arr[5]的值: 60
⭐ 老师强调:数组 vs 分散变量
- 定义4个独立
int变量 → 可能消耗4个4KB内存页(16KB)。 - 定义1个含4个
int的数组 → 仅消耗1个4KB内存页。 - 结论:数组的高密度存储不仅省空间,更显著提升程序运行效率。
第四部分:结构体 (Struct)
6. 结构体核心概念
6.1 定义与实例化
- 定义 :由若干基本类型组合而成的自定义数据类型,相当于"设计图纸"。
- 实例化:根据图纸创建的具体对象,每个实例包含所有成员的完整副本。
- ⭐ 老师强调 :结构体是第七种数据类型。声明变量时需加
struct关键字(或使用typedef别名)。
6.2 内存布局
- 成员变量在内存中连续存储。
- 编译器根据定义自动计算各成员偏移量,无需分隔符。
- 结构体大小 ≈ 各成员大小之和(实际可能有内存对齐填充)。
6.3 代码示例:结构体定义与使用
#include <stdio.h>
// ⭐ 定义结构体类型(设计图纸)
struct Device {
short id; // 2字节
int status; // 4字节
float temp; // 4字节
char level; // 1字节
}; // 总计约11字节(实际可能因对齐为12或16字节)
int main() {
// ⭐ 实例化结构体(按图施工)
struct Device d1;
struct Device d2;
// ⭐ 通过 . 运算符访问成员
d1.id = 1001;
d1.status = 1;
d1.temp = 36.5f;
d1.level = 'A';
// d2是独立副本,修改不影响d1
d2 = d1;
d2.status = 0;
printf("d1 status: %d\n", d1.status); // 1
printf("d2 status: %d\n", d2.status); // 0
// ⭐ 查看结构体大小
printf("Device大小: %zu 字节\n", sizeof(struct Device));
// ⭐ 使用typedef简化声明
typedef struct Device Dev;
Dev d3;
d3.id = 1002;
printf("d3 id: %d\n", d3.id);
return 0;
}
运行结果:
d1 status: 1
d2 status: 0
Device大小: 12 字节
d3 id: 1002
⭐ 老师强调:结构体 vs 基本类型
- 基本类型像"原子",结构体像"分子"。
- 结构体申请内存时一次性分配连续空间,而非分开申请。
- 读取时编译器根据定义精准定位每个成员,如同通过目录查找书架上的书。
第五部分:⭐ 老师特别强调的学习建议
🎯 课后必做事项
- 大量练习:大学知识密度是高中的百倍,光靠听课无法消化,必须课下做题巩固。
- 培养质疑精神:主动发现问题、提出问题,不要被动接受。学到哪质疑到哪。
- 不要过度依赖AI :基础知识AI准确率90%,高阶专业知识准确率很低。建议学习初期(至少一年)不使用AI辅助,先扎实基本功。
- 预习指针 :下节课核心内容是指针、函数调用内存传参机制、循环/if/switch细节。指针是重中之重,务必提前预习。
- 先用记事本写代码:避免依赖IDE智能提示,强迫自己理解每个字符和语法,打牢基础后再用高级编辑器。
六、格式化输入输出速查手册
6.1 常用格式占位符(% 开头)
| 占位符 | 含义 | 示例 |
|---|---|---|
%d / %i |
十进制整数(int) | printf("%d", 10); → 10 |
%u |
无符号十进制整数 | printf("%u", -1); → 4294967295 |
%o |
八进制整数 | printf("%o", 10); → 12 |
%x / %X |
十六进制(小写/大写) | printf("%x", 255); → ff |
%f |
浮点数(默认6位小数) | printf("%f", 3.14); → 3.140000 |
%.2f |
浮点数保留2位小数 | printf("%.2f", 3.1415); → 3.14 |
%e / %E |
科学计数法 | printf("%e", 100.0); → 1.000000e+02 |
%c |
单个字符 | printf("%c", 'A'); → A |
%s |
字符串 | printf("%s", "Hello"); → Hello |
%p |
指针地址(十六进制) | printf("%p", &num); → 0x7ffc... |
%% |
输出百分号本身 | printf("100%%"); → 100% |
6.2 常用转义字符(\ 开头)
| 转义字符 | 含义 | 说明 |
|---|---|---|
\n |
换行 | 光标移到下一行开头,最常用 |
\t |
水平制表符 | 相当于按Tab键,用于对齐 |
\\ |
反斜杠本身 | 避免被解析为转义起始符 |
\" |
双引号 | 在字符串中输出双引号 |
\' |
单引号 | 在字符常量中输出单引号 |
\r |
回车 | 光标回到行首不换行,常用于进度条刷新 |
\b |
退格 | 光标左移一位,相当于Backspace |
\a |
响铃 | 终端发出提示音 |
\0 |
空字符 | 字符串结束标志,底层操作必备 |
6.3 经典组合用法
// 整数 + 换行
printf("%d\n", 25);
// 字符串 + Tab + 整数 + 换行
printf("姓名:%s\t年龄:%d\n", "张三", 25);
// 输出:姓名:张三 年龄:25
// 混合文本 + 精度控制 + 换行
printf("温度:%.2f℃\n", 36.567);
// 输出:温度:36.57℃
// 百分比 + 回车刷新(进度条效果)
printf("进度:%d%%\r", 75);
6.4 进阶修饰符(宽度、对齐、填充)
| 写法 | 含义 | 示例输出 |
|---|---|---|
%5d |
右对齐,至少占5位,左补空格 | " 12" |
%-5d |
左对齐,占5位,右补空格 | "12 " |
%05d |
占5位,不足补零 | "00012" |
%8.2f |
总宽8位,小数2位 | " 3.14" |
%10s |
字符串右对齐占10位 | " Hello" |
⭐ 老师强调:scanf 中慎用 \n
// ✅ 正确写法
scanf("%d", &num);
// ❌ 错误写法:会导致程序卡住等待额外输入
scanf("%d\n", &num);
原因 :scanf 格式串中的空白字符(空格、\n、\t)表示"跳过任意数量空白直到遇到非空白字符"。末尾加 \n 会让 scanf 在读取整数后继续等待下一个非空白输入,造成输入阻塞。记住:scanf 格式串末尾不要加 \n。