C语言入门到进阶:环境搭建、指针内存与项目实战全解析
1. 为什么到2025年了我还是要劝你把C语言学扎实每次看到有人在网上问“C语言、C、Java、Python到底学哪个”我都能猜到评论区又是一轮混战。这个问题本身没什么毛病但问法暴露了一个普遍误解把C语言当成了众多编程语言里的“选项之一”仿佛它只是入门时的一个跳板学完就要赶紧换到更“高级”的赛道。实际上C语言根本不在这个赛道上。它是少数几门“你不会它就真的看不懂计算机在干什么”的语言。Python能让你三行代码读完一个文件但你怎么知道那个文件在磁盘上是以什么形式存的Java能让你new出一个对象但你知道这个对象被GC回收之前它的内存布局是什么样的C语言把这一切都摊开在你面前指针就是地址数组就是连续内存结构体就是内存块的某种排列方式。它不给你遮罩层逼你直面机器的真实逻辑。近期的热搜词里我看到“c语言和java和python和c”这个问题被反复搜索说明很多初学者正在做技术选型。我的建议一直很直接如果你想吃计算机这碗饭不管以后做前端、后端、算法还是嵌入式C语言都值得认真过一遍。它不是让你以后主要拿它写业务而是让你的“计算机世界观”变得扎实。有过指针和内存管理的训练之后你回头写Python、Java会突然明白很多框架底层设计到底在防什么。那么这篇内容我就结合自己带新人、做项目、以及最近几年在一些基础教学和实践项目里积累的经验把C语言从环境搭建、核心语法、函数库、指针内存、文件操作到练手项目和面试考点一条线捋清楚。它的定位是“基础但深入”既有面向小白的保姆级步骤也有给已经有基础的人查漏补缺的干货。2. 环境搭建先用对工具别在第一步把自己劝退2.1 编辑器和编译器是两码事很多初学者把“编程软件”理解成一个整体的东西其实它拆开是两个部分编辑器负责写代码编译器负责把代码变成机器能运行的程序。C语言没有像Python那样的“官方自带IDE”所以需要自己组合。搜索结果里有人问“c语言一般用什么编程软件”也有人问“eclipse安装c语言插件”和“vscode配置c语言环境”说明大家都卡在了同一个地方——工具选型。我个人对这几种方案的体验如下方案适合人群优点缺点Visual StudioWindowsWindows下做C/C开发集成度高调试器好用开箱即用安装包大工程概念对新手偏重VS Code MinGW-w64绝大多数入门开发者轻量、跨平台、配置灵活需要手动配置容易折腾CLion想用专业IDE但不排斥付费/学生免费申请代码补全和重构强CMake集成好对新手来说工程概念偏重Eclipse CDT习惯Eclipse生态的人插件安装后即可开发界面老旧配置繁琐不推荐新手Dev-C学校机房老古董轻量太久没维护调试器老旧不建议使用我的结论是最推荐 VS Code MinGW-w64。它拆开看每一个组件都不难自己亲手完成配置的过程本身就是一种学习。热搜里“vscode配置c语言环境”被搜得很多说明这套组合已经是当前的主流但很多人卡在了任务配置和调试配置步骤上。2.2 在Windows上配置全流程附避坑点第一步下载MSYS2而不是直接去搜“MinGW下载”因为很多“MinGW官网下载”老链接已经过时。MSYS2是一个软件分发平台在它的终端里执行一行命令就能装好MinGW-w64工具链pacman -S mingw-w64-ucrt-x86_64-gcc装完之后把C:\msys64\ucrt64\bin加到系统环境变量PATH里。然后在命令行验证gcc --version如果能看到版本号说明编译器装好了。第二步VS Code里安装两个扩展C/C微软官方出品和Code Runner可选用于快速运行单文件。前者提供语法高亮、智能提示和调试支持后者让你通过右上角一个按钮就能编译运行。第三步新建一个hello.c文件写最简单的程序#include stdio.h int main() { printf(hello, world\n); return 0; }关键点来了如果你用Code Runner直接点运行可能会发现中文乱码或者控制台一闪而过。这是因为C语言在Windows下的默认控制台输出编码和编辑器编码不一致。我的建议是在代码中用setlocale或者直接避免在入门阶段输出中文等后面学到文件读写时再统一处理编码问题。这不是什么玄学就是Windows控制台默认代码页的问题。2.3 报错排查从“无法打开源文件”说起热搜里有一个词叫“c语言无法打开源文件怎么解决”这是初学者最常撞上的墙。它通常有两种情况第一种系统找不到头文件报错长这样fatal error: stdio.h: No such file or directory这几乎可以肯定是编译器没装好或者VS Code里没有正确配置 include 路径。Windows下如果你用MSYS2装的GCC正常情况下系统头文件在C:\msys64\ucrt64\include里。如果VS Code的C/C扩展没有自动探测到可以在.vscode/c_cpp_properties.json里手动指定{ configurations: [ { name: Win64, includePath: [ ${workspaceFolder}/**, C:/msys64/ucrt64/include ], compilerPath: C:/msys64/ucrt64/bin/gcc.exe } ] }第二种VS Code提示“无法打开源文件”但能编译通过。这种情况我在实际教学里遇到很多次就是C/C扩展的IntelliSense无法识别include路径但编译器的环境变量是正常的。这种不用慌按上面的方式配置c_cpp_properties.json就能解决。它的本质是编辑器底层的代码索引系统和编译器是两套体系索引找不到头文件不代表编译会失败。3. 语法地基变量、运算、控制流以及那些没人提醒你的坑3.1 先弄清一件事C语言的操作对象是“内存上的数据”很多教程一上来就讲数据类型“int是整型float是浮点型”然后让你背。我教新人的时候习惯换一个角度C语言里每一个变量本质上都是内存里的一块区域。你声明int a 5;就是向操作系统要了4个字节在32位平台上然后把5按二进制编码放进去。后面所有对a的操作都是在操作这块内存。为什么要强调这个视角因为后面学指针、学数组、学结构体凡是学得好的人思维都是“内存视角”。比如数组为什么下标从0开始因为下标就是偏移量首元素地址 偏移量 × 每个元素字节数。这比从1开始更贴近硬件逻辑。字符串abc为什么以\0结尾因为C语言没有内置字符串类型必须自己约定一个终止符来标记边界。3.2 运算符优先级你以为你在算数学其实计算机在执行规则热搜词里有“c语言运算符优先级”和“c语言优先级”说明这个问题困扰过很多人。我强烈建议入门阶段不要背口诀而是记住两条原则不确定就加括号代码是写给人看的清晰比少敲两个字符更重要。搞懂最容易翻车的几组*解引用和的组合、赋值运算符和等于运算符、位运算符/|和逻辑运算符/||。下面这个小例子能说明问题int a 5; int b 10; int c a b; // 位与运算 int d a b; // 逻辑与运算a b是对5和10的二进制位逐位相与0101 1010结果是0。而a b是判断“a为真且b为真”结果是非0true在C语言里用1表示。这两者天差地别。还有热搜词里“!()c语言是啥”其实就是逻辑非运算符!对一个表达式取反非0变00变1。3.3 while和do-while的差异大多数人死记硬背却没真懂“c语言while和do-while区别”被搜到不奇怪因为教科书上的定义都太抽象了。我的记忆方式是while是“先看门再进屋”do-while是“先进屋再看门”。int i 0; // 这个循环体一次都不会执行 while (i 5) { printf(%d\n, i); i; } // 这个循环体至少执行一次 do { printf(%d\n, i); i; } while (i 5);你不需要背它们的行为差异你只需要在写程序时问自己一个问题“这段逻辑是否至少需要执行一次”比如读用户输入直到合法为止不管你第一次输入什么你都至少要读一次那用do-while天然合适。验证密码登录时“先输入一次不对再循环”用do-while也顺手。3.4 控制流里几个隐藏的坑else的悬挂问题if后面如果不加花括号else会匹配最近的未匹配if。我见过真实项目里因为少写一对括号把整个逻辑都改错的案例。所以我的建议是无论 if 下面有几条语句一律写花括号。这看起来让代码变得“啰嗦”了但省下的调试时间远远多过那几行括号的成本。switch-case的穿透case里如果不写break会继续执行下一个case的代码。有时候这是故意设计的比如多个case共享同一段处理逻辑但新手大概率是忘记写了。我甚至在面试中见过有人把“忘记写break导致的穿透”当成“C语言特性”来炫耀这是不对的。for循环里修改循环变量在循环体内修改循环变量不是不行但会让代码极为难读。比如for (int i 0; i 10; i) { if (条件) { i; // 不建议 } }你要真想跳过某次迭代用continue就行了搞明白流程控制的关键字不要靠改循环变量来实现流程跳转。4. 函数与库从scanf到round、fscanf把常用家伙事摸透4.1 scanf用不好的根因输入缓冲区的“坑”热搜里专门有一个“c语言学习之scanf”我就知道大家都栽在这上面了。scanf的问题不是“不会用”而是“用着用着就出鬼”比如连续输入数字和字符时字符可能读不到比如上一轮输入残留的换行符会干扰下一轮。看一下这个场景int age; char name[20]; printf(请输入年龄); scanf(%d, age); printf(请输入姓名); scanf(%s, name);第一行输入25然后按回车这个回车符会留在缓冲区里。第二个scanf(%s, name)遇到空白字符空格、换行会自动跳过所以用%s读字符串通常没问题。但如果你第二个想读的是字符char gender; printf(请输入性别); scanf(%c, gender); // 这里会读到换行符而不是用户输入的字符这就是经典的“scanf吞换行”问题。解决办法有两个要么在%c前面加一个空格scanf( %c, gender)让scanf跳过前导空白要么在每次scanf之后手动getchar()吃掉残留在缓冲区里的换行。我推荐第一种因为更干净。4.2 printf与scanf是“格式化”双雄但别忽视返回值printf和scanf都返回int不看返回值是很多人的习惯。但scanf的返回值其实很好用它返回的是成功读入的变量个数。如果用户输入的内容不匹配格式比如你要求整数他输入了字母scanf会返回0你就可以借此判断输入是否合法让程序更健壮。这是一个写工程代码的人应该养成的习惯。另外搜索热度里还有“c语言fscanf和fprintf函数”它们本质上是scanf/printf的“文件版本”把控制台输入输出换成文件输入输出格式说明符完全一致。这块后面第7节我会专门讲文件读写时详细展开。4.3 数学函数round函数为什么不能直接用热搜里有“c语言round函数”这个问题我上课时也总被问到。明明是数学里常用的四舍五入为什么直接调用就报错#include stdio.h #include math.h int main() { double x 3.6; double y round(x); printf(%.1f\n, y); return 0; }如果你直接编译gcc test.c -o test大概率会报“undefined reference toround”。这不是代码问题而是链接问题round函数在libm数学库里用GCC编译时必须手动链接这个库。正确命令是gcc test.c -o test -lm-lm的l是link的意思m就是math库。这个细节之所以被高频搜索是因为错误提示非常反直觉它不告诉你“缺少库依赖”而是告诉你“round函数未定义”新手根本想不到是编译参数的问题。类似的数学函数还有pow幂、sqrt开方、fabs浮点绝对值它们都在math.h里编译时都需要-lm。4.4 函数设计为什么把代码拆成函数是“划算”的C语言里的函数是组织代码的基本单位。但新人刚开始写程序都是“一股脑写进main里”等到几百行之后就会发现变量名冲突、逻辑混乱、改一处崩全局。拆函数的意义不只是“让代码短一点”而是把复杂的任务分解成可以独立验证的小任务。一道PTA题“字符串逆序”就是个好例子。不拆函数写就是循环里交换字符拆开来写是这样#include stdio.h #include string.h void reverse_string(char str[]) { int len strlen(str); for (int i 0; i len / 2; i) { char temp str[i]; str[i] str[len - i - 1]; str[len - i - 1] temp; } } int main() { char s[100]; fgets(s, sizeof(s), stdin); s[strcspn(s, \n)] \0; reverse_string(s); printf(%s\n, s); return 0; }reverse_string这个函数只做一件事原地逆序字符串。你可以在main里用固定字符串测试它验证通过后再接入“读取用户输入”的部分。这就是模块化思维也是从“写代码”到“设计程序”的第一步。4.5 回调函数函数也能作为参数传递“c语言回调函数详解”能上热搜说明很多人在学到一个名词后不知其所以然。其实回调函数的概念和函数指针绑定在一起函数在C语言里也有地址你可以把函数的指针当作参数传给另一个函数。经典例子是qsort库函数它接受一个比较函数的指针#include stdio.h #include stdlib.h int compare_ints(const void *a, const void *b) { int ia *(const int *)a; int ib *(const int *)b; return (ia ib) - (ia ib); } int main() { int arr[] {4, 1, 7, 3, 9, 2}; int n sizeof(arr) / sizeof(arr[0]); qsort(arr, n, sizeof(int), compare_ints); for (int i 0; i n; i) { printf(%d , arr[i]); } return 0; }这里compare_ints就是回调函数qsort在排序过程中需要比较两个元素的大小但它不知道你的数组元素是什么类型所以把“怎么比较”这个决定权交回给你。这种“框架调用你的代码”的模式在嵌入式里也极其常见定时器溢出回调、串口接收回调、按键扫描回调底层驱动不关心你的业务逻辑它到点了就调用你的处理函数。这就是回调的思想。5. 指针与内存管理C语言真正的分水岭绕不开就硬啃5.1 指针的本质就是地址别把它想复杂很多人学指针学得头皮发麻原因是教材总是用“指针就是指向变量的变量”这种话绕圈子。说人话指针是一个变量它存储的值是另一个变量的内存地址。就像你用门牌号去找房子房子住在哪门牌号本身也是一串数字。int a 10; int *p a; // p 存储了 a 的地址是取地址符*是解引用符。p本身是一个地址值*p是通过这个地址值去访问那块内存里存的数据。就这么简单。但为什么指针难因为指针有两个层级的信息指针的值地址和指针指向的值。初学者容易混淆。比如p; // 地址向后移动移动多少取决于 p 指向的数据类型大小 (*p); // p 指向的那个变量的值加1如果p指向int型变量p会让地址移动4个字节32位平台如果p指向char型变量p只移动1个字节。指针1的含义是“跳过一个该类型的元素”这是实现数组遍历和动态内存访问的基础。5.2 数组和指针是“亲密战友”但别画等号热搜里有“c语言数组变量的类型转换”这个说法不太准确但相关的知识点确实核心数组名在大部分表达式中会“退化”为指向首元素的指针。int arr[5] {1, 2, 3, 4, 5}; printf(%p, %p\n, arr, arr[0]); // 输出相同数组名的值就是首元素地址所以arr[i]本质上等价于*(arr i)。但数组名本身不是指针变量它不能做arr这样的操作因为数组名是常量不是变量。那“类型转换”是怎么回事我猜是有人遇到这种问题char *str hello; int *p str; // 编译警告指针类型不匹配C语言里不同类型的指针不能直接互相赋值。如果你非要把char指针赋给int指针需要显式强制转换int *p (int *)str;但这样做之后通过*p读取数据时会从那个地址连续读4个字节而原来的str指向的只是一个字符。这大概率不是你想要的结果。所以我的建议是强转指针类型前先想清楚你到底在做什么。指针强转不是“类型变一下”那么轻描淡写它改变了程序解释那块内存的方式。5.3 动态内存分配用不好程序就崩给你看热搜里有“c语言内存管理”这是C语言让新手崩溃的重灾区。内存管理的基础就是四个函数malloc、calloc、realloc、free。int *arr (int *)malloc(10 * sizeof(int)); if (arr NULL) { printf(内存分配失败\n); return 1; } // 使用 arr... free(arr); // 用完释放这里有几条铁律malloc之后必须判断是否返回NULL。内存分配可能失败尤其嵌入式环境内存紧张时。malloc和free必须配对。分配了不释放就是内存泄漏程序跑得越久吃得越多。free之后不要再访问那块内存这叫“悬空指针”。释放后把指针置为NULL是个好习惯。malloc开辟的内存不会自动清零需要你自己初始化用memset或calloccalloc会清零。链表、队列、二叉树这些动态结构都是建立在malloc/free之上的。学C语言内存管理这块再痛也得过因为它是你理解“栈内存、堆内存、静态区”三足鼎立内存模型的最低门槛。5.4 一个实实在在的调试心得开多几个调试器不要靠printf硬猜我在带项目时发现新手遇到空指针崩溃/段错误时第一反应是到处加printf。这不叫调试叫碰运气。我建议尽早学会使用VS Code的调试功能在怀疑崩溃的那一行打上断点watch指针变量的值一步步走看它是在哪一行变成了非法地址。用调试器的好处是内存是“看得见”的。你创建了一个数组它有多大边界在哪里访问越界时调试器会明确告诉你“stack smashing detected”还是段错误这些信息比printf输出直观得多。6. 字符串、结构体与文件读写工程里天天用的组合拳6.1 字符串函数看着多核心其实就是“拷贝-比较-拼接-查找”热搜词里有“c语言字符串函数”也有“c语言库函数大全”。字符串处理函数的家族很大但核心就几类功能常用函数注意点求长度strlen不包含结尾的\0拷贝strcpy/strncpystrcpy不安全可能越界拼接strcat/strncat需保证目标空间足够比较strcmp/strncmp返回值是差值不是“等于1”查找strchr/strstr返回指针或NULL我特别想强调strcpy的危险性。很多人写char dest[10]; strcpy(dest, a much longer string);编译能过运行也能“碰巧”不出错但这就是典型的缓冲区溢出。目标数组只有10字节源字符串远超10字节strcpy会把数据写到数组边界之外覆盖相邻的内存。现实世界里很多安全漏洞就是这么写出来的。所以我一般建议新人在不确定长度时优先用strncpy并手动确保最后一个字符是\0。6.2 结构体把零散数据打包成“一个整体”热搜里有“c语言结构体”这是C语言面向“数据组织”的核心工具。说人话结构体就是把多个变量打包成一个新类型。struct Student { int id; char name[50]; double score; };有了这个结构体你就可以创建一个“学生”类型的数组、指针、函数参数一次传递整个学生的信息而不是分别传id、name、score三个零散值。在文件读写、网络数据收发、嵌入式协议解析中结构体的使用率极高——因为现实世界的数据本来就是“成组出现”的。6.3 文件读写从文本到二进制别被换行符坑了热搜里“c语言文件读写操作代码”是高频搜索我干脆给一个完整的例子用fprintf写入几个学生的信息到文本文件再用fscanf读回来。#include stdio.h #include stdlib.h struct Student { int id; char name[50]; double score; }; int main() { struct Student students[3] { {1, Alice, 88.5}, {2, Bob, 91.0}, {3, Cindy, 79.5} }; FILE *fp fopen(students.txt, w); if (fp NULL) { perror(文件打开失败); return 1; } for (int i 0; i 3; i) { fprintf(fp, %d %s %.2f\n, students[i].id, students[i].name, students[i].score); } fclose(fp); // 读回来 fp fopen(students.txt, r); if (fp NULL) { perror(文件打开失败); return 1; } struct Student temp; while (fscanf(fp, %d %s %lf, temp.id, temp.name, temp.score) 3) { printf(%d %s %.2f\n, temp.id, temp.name, temp.score); } fclose(fp); return 0; }这里面有几个容易被坑的点fopen失败时返回NULL必须判断否则后续所有操作都是对空指针操作。w模式是“覆盖写入”如果文件已存在会清空原内容a是追加r是只读。fscanf的返回值是成功匹配并赋值的变量个数用它作为循环条件是安全的不会把文件末尾的EOF错误当成普通数据读进来。6.4 Windows与Linux的换行符差异这个问题很多人直到工作中才碰到。Windows文本文件的换行是\r\nLinux是\n。在Windows上写文本文件\n会被自动转成\r\n读回来时\r\n又被自动转回\n。这些转换是C标准库替你做的。但如果你用rb/wb二进制模式打开文件就不会有任何换行转换。处理图片、音频等非文本文件或者跟嵌入式设备交换通信数据时必须用二进制模式否则数据会被改得一塌糊涂。7. 从基础到实战几个必写的练手项目与面试高频考点7.1 算法练手冒泡排序和它背后的“交换思维”热搜里“冒泡排序c语言”常年上榜说明它几乎是所有算法入门的第一个案例。我给你一个完整规范的写法#include stdio.h void bubble_sort(int arr[], int n) { for (int i 0; i n - 1; i) { int swapped 0; for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; swapped 1; } } if (!swapped) { break; // 没有发生交换说明已经有序提前退出 } } } int main() { int arr[] {5, 2, 9, 1, 5, 6}; int n sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, n); for (int i 0; i n; i) { printf(%d , arr[i]); } printf(\n); return 0; }那个swapped标志位很多基础教程里没有但它体现了一个重要的优化思想已经有序的序列不需要再走完整个循环。这种“提前结束”的思维在工程代码里经常能帮你省下不少无谓的计算。7.2 综合项目网吧计费管理小项目适合作为第一个“真项目”热搜里有“c语言网吧计费管理小项目”这名字一听就像是课设作业但我觉得它其实是特别好的练手题目因为它几乎涵盖了C语言基础的全部知识点结构体定义计费记录、数组存多条记录、循环处理上机/下机操作、switch做菜单、文件操作保存记录、通过时间差计算费用。一个完整的项目流程大致是定义“上机记录”结构体字段包括机号、开始时间、结束时间、费用。用数组保存当前所有开机记录。开机时记录时间关机时计算时长按费率计费。每次操作后把记录写入文件程序启动时从文件加载。设计一个简单的菜单循环1. 上机 2. 下机 3. 查询 4. 退出。等你按这个流程写完整套代码你对基础语法的掌握程度会比刷一百道练习题都扎实。因为它要求你把零散的功能点串联成一个完整流程这是从“会写代码”到“会做东西”的关键一步。7.3 嵌入式方向热敏电阻、ADC滤波与SVPWM热搜里有一组很有意思的嵌入式相关词“热敏电阻制作温度传感器的c语言”、“c语言adc值滤波函数”、“svpwm的c语言程序”。这其实指向的是C语言在嵌入式领域的三个典型应用场景。温度传感器的核心思路是热敏电阻阻值随温度变化用ADC采集电压得到原始数字量再用一个查找表或数学公式换算成温度。ADC采到的原始值往往带有噪声所以需要进行“滤波”。最简单的滑动平均滤波#define FILTER_LEN 5 unsigned int adc_filter(unsigned int new_value) { static unsigned int buf[FILTER_LEN] {0}; static int index 0; static unsigned int sum 0; static int count 0; sum - buf[index]; buf[index] new_value; sum new_value; index (index 1) % FILTER_LEN; if (count FILTER_LEN) { count; } return sum / count; }这段代码用静态变量保存历史数据每次新采样进队旧采样出队求平均。它与C语言基础的关系在于静态局部变量、取模运算、数组索引、函数封装这些全是你前面学的基础知识。SVPWM空间矢量脉宽调制的C语言实现就更工程化了它涉及三角函数、扇区判断、占空比计算。但不管代码多复杂底层还是那些东西结构体装参数、函数拆逻辑、指针传地址。7.4 面试考点这些坑你迟早要踩一遍最后聊聊“c语言面试常见问题”。我面试过不少候选人也带过不少新人发现面试官最爱的几个考点其实很固定sizeof和strlen的区别sizeof是运算符编译时求值返回占用的字节数strlen是函数运行时求值统计字符串长度。对同一个char arr[20] hellosizeof(arr)是20strlen(arr)是5。static关键字的作用修饰局部变量时变量生命周期变为整个程序运行期且只初始化一次修饰全局变量/函数时限定作用域在本文件。const关键字const int *p和int *const p的区别。前者是“指向常量的指针”不能通过p修改目标的值但p可以指向别处后者是“常量指针”p本身不能再指向别处但通过p可以修改目标值。数组和指针的区别、结构体对齐、链表反转、字符串拷贝。这些问题看起来是“背答案”实际上考的是你写代码时有没有思考过底层原理。我见过基础扎实的人从来不用背这些都是平时写代码时自然形成的印象。7.5 学习路径建议课程、刷题与项目三条腿走路基础语法过一遍之后怎么继续深入我建议三条线并行听课翁恺的C语言课程还是那个经典选择无论你在哪个平台看认真跟完并做作业效果会远超自己闷头看书。刷题PTA平台上的C语言题库、GESP的几级题目比如热搜里的物流网络、环线都是很好的验证方式。刷题的重点不是“AC了”而是“为什么AC了”和“为什么WA了”。做项目别只满足于运行在控制台的小程序试着往文件和数据结构方向延伸。哪怕只是给“网吧计费管理”加上链表存储、文件持久化、查找排序功能这个增量就是在向“工程化”靠拢。我自己的体会是C语言学到“能独立做出一个2000行以上、带数据结构和文件操作的小项目”这个程度后面的数据结构与算法课程都会轻松很多。因为C语言逼着你理解了底层再学抽象的东西时你脚下已经有一块坚实的地基了。刚才说到嵌入式那个ADC滤波函数其实还有个小技巧值得多提一嘴静态局部变量的初始化只发生在第一次进入函数时所以那个count在多次调用之间不会重置。我在带新人时经常看到他们把这块写成全局变量虽然也能工作但用静态局部变量把状态封装在函数内部对外部代码更友好也不容易跟其他全局名字冲突。这种“状态封装”的习惯越早养成越好。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →