
写C入门这一年我见过太多人倒在第一周然后逢人就说“C太难了”。说实话C确实不是一门能靠看视频看会的语言它需要你亲手踩坑、亲手修错才能把那些抽象概念变成肌肉记忆。这篇东西不是教科书我是按自己实际带新人总结出来的入门路径写的从环境配置到常见崩溃排查再到几个必练的小项目和算法尽量把每个环节的“为什么”也讲清楚照着走至少不会让你被第一个Hello World卡住。⚠️ 本文内容仅用于技术学习与交流请勿用于任何违反法律法规的用途。1. 入门C前先把路线图摸清楚1.1 C到底在学什么很多新手一上来就背语法背了忘忘了背。其实C入门阶段要掌握的核心东西就几块变量和数据类型、流程控制if、for、while、函数、数组和字符串、结构体、类与对象再加一个STL标准模板库的基本使用。语法只是外壳真正的难点是你开始直接操作内存、理解指针和引用、搞明白堆和栈的区别这时候你才会感受到C比Python、Java这类语言更“贴近机器”。我的经验是把学习目标拆成三个层次第一层是能写出能运行的程序不要求代码优雅第二层是能看懂报错能自己定位到是哪一行出了问题第三层是开始思考性能和资源管理比如一个循环能不能少跑几遍、一个字符串拷贝能不能避免。入门阶段不要急着追求第三层先把前两层踩实。至于“C有没有用”这个问题不用怀疑。游戏引擎、操作系统、数据库、工业软件、量化交易系统底层基本都有C的身影。你如果将来想深入这些领域C就是绕不开的那把钥匙。1.2 开发环境怎么选Dev C、VS Code还是Visual Studio环境配置这一关劝退了很多人。我先给个明确结论只做基础语法练习用Dev C最省心想用现代编辑器、为以后工程开发做铺垫选VS Code配MinGW开发Windows桌面程序直接用Visual Studio愿意付费或学生免费CLion体验最舒服。别纠结“哪个最好”要选“哪个最适合现在的我”。Dev C的问题在于它是很多年前的软件了界面老旧但胜在轻量、免配置下载安装完就能写代码跑起来特别适合第一周用来熟悉语法。VS Code本身不是编译器它只是一个编辑器你需要额外安装C/C扩展和编译器比如MinGW-w64配置好之后功能非常强大补全、调试、格式化都有。Visual Studio对Windows开发者来说是最完整的方案集成了MSVC编译器、调试器、项目管理但安装包很大对新手来说也容易迷失在复杂的工程配置里。CLion是JetBrains家的IDE智能提示和调试体验一流但它是收费的。还有一个容易混淆的点你可能会搜到Dev C、C Builder、Visual C这些名字它们都不是同一件事。Dev C是IDEC Builder是Embarcadero公司的RAD工具Visual C是微软的C工具集平时作为新手不需要在这上面浪费太多精力认准“一个编辑器一个编译器”就够了。2. 环境搭建与第一个C程序2.1 VS Code配置C/C环境的完整流程我推荐新手用VS Code因为它轻量而且以后你写前端、脚本、甚至写文档都能用它。下面是我反复操作过的配置流程Windows环境下亲测可用。第一步去VS Code官网下载安装包安装时勾选“添加到PATH”那个选项后面会省很多事。第二步打开VS Code在扩展商店搜索C/C安装微软官方的C/C扩展包这个扩展提供了智能提示和调试能力。第三步安装编译器。编译器才是真正把代码转成机器能执行文件的那个程序VS Code只是负责让你写得舒服。我一般让新手装MinGW-w64装好后把bin目录路径加到系统环境变量PATH里。第四步写一个测试代码按F5运行VS Code会提示你配置调试器它会自动生成 launch.json 和 tasks.json通常不需要你手动改太多。这里放一个最简的tasks.json示例用来调用g编译当前打开的文件{ version: 2.0.0, tasks: [ { label: build hello, type: shell, command: g, args: [-g, ${file}, -o, ${fileDirname}/${fileBasenameNoExtension}.exe], group: { kind: build, isDefault: true } } ] }配置完成后你会遇到一个超高频报错g不是内部或外部命令。这就是PATH没有配置好。检查MinGW的bin目录下有没有g.exe再检查系统环境变量里有没有这个路径改完环境变量后一定要重开VS Code。提示很多人配置失败不是因为操作错而是因为改了环境变量后没有重启VS Code甚至没重启终端。改完环境变量记住重启一下所有相关程序。2.2 Visual C Redistributable究竟是个啥新手经常会遇到这种情况明明自己会写C代码但运行别人编译好的.exe程序时系统弹窗提示“缺少VCRUNTIME140.dll”或者“需要Visual C Redistributable”。很多人一慌就到第三方网站乱下载安装包这是很危险的。Visual C Redistributable中文叫Visual C运行库它是程序运行时需要的“运行环境”不是编译器也不是IDE。你用VS Code写代码只是说明你的电脑有开发工具但别人用Visual Studio编译的软件默认会依赖微软的C运行库如果对方电脑上没有程序就起不来。解决方法很简单去微软官方网站下载对应的Redistributable安装包选x64版本安装完就能解决大部分缺DLL的问题。这里面有一个新手特别容易走的弯路看到“Visual C”就以为装上之后自己就能写C了。完全不是运行库只是让已编译好的程序能跑起来你写代码依然需要编译器。另外下载时认准官方链接不要在搜索引擎首页点那些带“高速下载”“破解版”字样的推广链接那种地方十有八九是捆绑软件。2.3 第一个程序源码、编译、运行的完整链路我们来看第一个程序代码非常简单#include iostream using namespace std; int main() { cout Hello, C! endl; return 0; }这段代码看起来简单但里面藏着一整套知识体系。第一行#include iostream是头文件引入iostream就是输入输出流库cout和endl都定义在里面。如果把头文件理解成“工具箱说明书”#include就是告诉编译器“我要借用说明书里的工具”。using namespace std是声明使用std命名空间因为cout、endl在std里头不写这行的话你得写成std::cout很多新手遇到“cout未声明”的报错就是因为忘了命名空间。int main()是程序入口任何一个可执行的C程序必须有main函数程序从头到尾只执行它里面的代码。cout是输出对象是插入运算符把字符串“推”给输出流。return 0表示程序正常结束这个0会返回给操作系统非0一般代表异常退出。编译运行这个过程其实包含四个阶段预处理、编译、汇编、链接。预处理负责处理#include和宏替换编译把C代码转成汇编语言汇编把汇编代码转成机器码目标文件链接再把多个目标文件和运行库组装成一个完整的.exe。你现在只需要知道你写的源码和最终执行的程序不是一回事中间隔着一层编译器。以后遇到“编译错误”和“链接错误”是两种截然不同的错误前者是源码写错后者是组装阶段出了问题很多新人会在这两类报错之间糊涂很久。3. 核心语法细节与新手高频坑3.1 字符串与字符数组初始化、转换与常见误区入门阶段字符串是第一个大坑。C里有两种表示字符串的方式一种是C语言风格的东西叫字符数组另一种是C标准库提供的string类型。新手一定要分清。字符数组初始化常见写法char arr1[] hello; // 自动加 \0 char arr2[6] hello; // 5个字母结尾空字符 char arr3[5] hello; // 错误放不下 char* ptr hello; // 字符串字面量不要试图修改它很多人会在这上面栽跟头。字符数组一旦初始化不能用整体重新赋值如果你想改内容要么用strcpy要么用循环逐个赋值。而string就舒服多了string s hello; s world; // 合法直接赋值大量热搜里都有人在搜“c字符串数组初始化”和“c字符串转数组”说明这是公认的易混点。字符串转字符数组常见做法是用s.c_str()获取const char*或者把字符逐字拷贝到std::vectorchar里。下边是一个简单示例#include iostream #include cstring #include string using namespace std; int main() { string s abcd; char buf[10]; strcpy(buf, s.c_str()); // string 转 char 数组 for (int i 0; i 4; i) { cout buf[i] ; } return 0; }强调一个安全习惯使用strcpy或类似函数前一定确认目标数组的长度足够否则缓冲区溢出轻则崩溃重则程序被利用做坏事。这也是很多安全漏洞的来源C的性能和安全往往靠程序员自己把关。3.2 运算符优先级、比较运算符与按位运算C的运算符非常多优先级记不住就会写出反直觉的代码。我有个简单的心法不确定优先级的时候不要背表而是加括号。可读性远比“少打几个字符”重要。需要特别注意的几组运算符和这是新手第一大坑。if (x 1)不会报错但它的含义是把1赋给x然后条件永远为真。比较好的做法是把常量写在左边if (1 x)这样如果你漏写一个等号编译会直接报错。第二组是和是按位与是逻辑与有短路特性没有。第三组是移位运算符、它的优先级比算术低但比关系高很容易写错实际开发中我几乎总是加括号。按位运算在现实中到底有啥用最典型的是权限位掩码。比如用整数的某一位表示一个开关const int READ 1; // 二进制 001 const int WRITE 2; // 二进制 010 const int EXEC 4; // 二进制 100 int flag READ | WRITE; // 001 | 010 011表示可读可写 if (flag READ) { // 判断是否可读 cout 可以读取 endl; }按位与清零、按位或置位、按位异或翻转这几个操作写底层、写协议、写通信时经常用到。入门阶段先混个脸熟不要求立刻精通但面试中“按位与”属于基础操作搜这个关键词的朋友多半是在准备笔试。3.3 if语句、循环与“指定顺序输出”if语句和循环是程序逻辑的地基。写if要养成的习惯是条件里尽量用清晰的真值表达别搞多层嵌套嵌套超过三层我就建议用提前return的方式拆出去。循环也是一样能用for表达清楚的就别用whilefor (int i 0; i n; i)这种写法是默认配置。热搜词里有个“c数字放大”看起来是一道练习图形输出或数组输出的题目。这类题本质上是把一个问题拆成“逐行处理每行处理”两层循环。我拿一个非常常见的“按指定顺序输出数字放大图案”来举例比如输入3输出三行数字的金字塔#include iostream using namespace std; int main() { int n; cin n; for (int i 1; i n; i) { for (int j 1; j n - i; j) cout ; for (int j 1; j i; j) cout j; for (int j i - 1; j 1; j--) cout j; cout endl; } return 0; }这种题目看起来不起眼但它逼着你理清“外层循环控制行、内层循环控制列”的基本模型。很多以后看起来很厉害的小游戏底层也就是循环加判断。我见过有些人一上来就想做愤怒的小鸟结果被图形库和物理模拟劝退。入门阶段老老实实写控制台程序比什么都强。3.4 函数、回调函数与友元函数是代码复用的最小单元。写函数时我特别想让新手记住一个原则一个函数只做一件事。想检查一个数是不是质数就写bool isPrime(int n)想打印数组就写void printArray(int arr[], int len)。名字要直白别写一个叫func1的函数里面塞了几十行。回调函数在热搜里也出现了。回调本质上就是把函数作为参数传给另一个函数让它在合适的时候调用你常用于事件处理、排序比较规则、异步任务。入门阶段你不需要深入但可以先看一眼标准库里的sort怎么用函数指针或函数对象作为比较器#include iostream #include vector #include algorithm using namespace std; bool cmp(int a, int b) { return a b; // 降序 } int main() { vectorint v {4, 2, 5, 1, 3}; sort(v.begin(), v.end(), cmp); for (int x : v) cout x ; return 0; }这个cmp就是一个最简单的回调。以后你学GUI、学异步编程到处都会碰到回调现在先把“把函数名传进去”这个感觉找到。友元这个知识点热搜词里写成了“c 有员”很多人搜是打错字了。友元是类中一种被争议的机制它的作用是让一个外部函数或另一个类访问本类的私有成员。我举个例子class Student { private: int score; public: Student(int s) : score(s) {} friend bool isPass(Student s); // 声明友元函数 }; bool isPass(Student s) { return s.score 60; // 可以访问私有成员 }为什么要限制它因为它破坏了封装。封装的意义是“数据只通过公开接口来操作”友元等于撕开一个口子。实际工程里不到万不得已根本不建议用但考试和面试会考所以入门阶段就当概念了解。4. 实战练手从算法题到小游戏4.1 冒泡排序理解排序必写的第一课冒泡排序是算法入门的必修课也是热搜榜上的常客。它的思路特别简单从头到尾两两比较相邻元素如果顺序不对就交换一轮下来最大的数就像冒泡一样浮到最后面。重复n-1轮数组就排好了。#include iostream using namespace std; void bubbleSort(int arr[], int n) { for (int i 0; i n - 1; i) { bool swapped false; // 优化本趟是否有交换 for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { swap(arr[j], arr[j 1]); swapped true; } } if (!swapped) break; // 已经有序提前结束 } } int main() { int a[] {5, 3, 8, 1, 9, 2}; int n sizeof(a) / sizeof(a[0]); bubbleSort(a, n); for (int i 0; i n; i) cout a[i] ; return 0; }这里我加了一个swapped标志位如果一整轮都没有发生交换说明数组已经有序可以提前退出。这个优化在面试里经常被问到。冒泡排序的时间复杂度是O(n²)面试官一般会接着问它的最好情况、最坏情况最好情况就是已经有序优化后是O(n)最坏情况是逆序O(n²)。空间复杂度是O(1)因为它只在原地交换。新手学冒泡最容易犯的错是内层循环的边界写成n-1导致每轮都和已排好的元素重复比较虽然结果可能没错但不伦不类。我的建议是手写一个6个元素的小数组在纸上逐步模拟两轮循环彻底搞清楚i和j每一步是什么这个时间花得非常值。4.2 随机数生成与猜数字小游戏“c随机数”是入门高频关键词。C语言时代我们用rand()配合srand(time(0))但现代C更推荐用random库。rand的问题在于取值范围和种子设置容易得到重复甚至劣质的随机序列。入门我先给一个基于C11的写法#include iostream #include random using namespace std; int main() { random_device rd; mt19937 gen(rd()); uniform_int_distributionint dis(1, 100); int target dis(gen); int guess; cout 猜一个1-100之间的数 endl; while (true) { cin guess; if (guess target) cout 太大了 endl; else if (guess target) cout 太小了 endl; else { cout 猜中了 endl; break; } } return 0; }mt19937是梅森旋转算法生成的伪随机数生成器配合uniform_int_distribution可以指定范围这是目前C推荐的做法。猜数字游戏是新手最好的第一个小游戏它包含随机数、循环、分支、输入输出、逻辑判断麻雀虽小五脏俱全。热搜里有一堆“c小游戏”“c游戏”“c小游戏编程100例”我觉得要泼一盆冷水控制台程序能做的小游戏其实都是文字交互、猜数字、贪吃蛇、五子棋这类。想做带画面的愤怒的小鸟你得学图形库。Windows上有EasyX这种适合教学练手的库跨平台可以用SDL、SFML做2D游戏往大了走还有Unity和Unreal底层脚本也支持C。路线应该是先做出控制台版本再考虑图形界面别一上来憋大招。4.3 判断质数的三重优化“判断质数c优化”这个热搜词我判断很多人是在准备笔试或者被作业卡住了。先给最基础的写法bool isPrime(int n) { if (n 2) return false; for (int i 2; i n; i) { if (n % i 0) return false; } return true; }第一轮优化不需要循环到n-1因为如果n能被一个大于sqrt(n)的数整除那它一定能被一个小于sqrt(n)的数整除所以循环上限改成i * i n就行时间复杂度从O(n)降到O(√n)。第二轮优化排除偶数。如果n不是2且是偶数直接false然后循环从3开始每次i加2只检查奇数因子效率再翻一倍。第三轮优化进一步压缩到大概每6个数只检查2个除了2和3以外所有质数都可以写成6k±1的形式因为6k、6k2、6k3、6k4都能被2或3整除。所以循环里可以让i从5开始步长6判断i和i2。写法bool isPrime(int n) { if (n 2) return false; if (n 2 || n 3) return true; if (n % 2 0 || n % 3 0) return false; for (int i 5; i * i n; i 6) { if (n % i 0 || n % (i 2) 0) return false; } return true; }这段代码是面试笔试里常见的考察点别看代码短里面是数学和编码能力的结合。大家学的时候不要只背代码要能说出来为什么这样写面试官就爱看这种“知其所以然”的表现。4.4 进阶路标快速幂、单调栈与“切香肠”快速幂是我建议新手学完基础语法后接触的第一个经典算法。它的核心思路是用二进制分解指数比如算2^13指数13的二进制是1101也就是841所以2^13 2^8 × 2^4 × 2^1。每一次平方底数指数右移一位时间复杂度是O(log n)。long long fastPow(long long a, long long b, long long mod) { long long res 1; while (b 0) { if (b 1) res res * a % mod; a a * a % mod; b 1; } return res; }单调栈也是一个经典考点典型场景是“找数组里每个元素右边第一个比它大的数”。它的思想是维护一个栈让栈内元素保持单调递增或递减每个元素进栈出栈一次总复杂度O(n)。这个算法对新手来说有点抽象入门阶段不用死啃知道“有一种叫单调栈的技巧可以高效解决一类区间极值问题”就行了等你刷算法题刷到中等难度时自然会回来找它。至于“c切香肠数学问题”我姑且理解为一道经典综合题把一根香肠切成长度为m的若干段或者更常见的二分答案模型。这类问题的价值在于训练你把数学模型转化为代码的能力。我的建议是拿到这类题先在纸上列出输入输出是什么、边界条件是什么、用什么数据结构再动手写代码先想后写而不是打开编译器就乱敲。5. 常见错误与崩溃问题排查实录5.1 崩溃记录C0000005访问冲突是怎么回事热搜词里有“c#调用c出现access violation c0000005”这个报错非常典型。C0000005是Windows的“访问冲突”异常本质是你的程序访问了一块它没有权限访问的内存比如数组越界、访问空指针、引用已释放的内存。用C#调用C时出现这个原因通常是P/Invoke层出了问题。常见情况有几种一个是调用约定不匹配C#那边CallingConvention指定错误第二个是结构体布局不一致C和C#对结构体的内存对齐理解不同数据进去就错位第三个是C返回的指针指向的内存被释放了C#拿到的是个悬垂指针一用就炸。排查方法我按顺序分享第一步缩小范围确认是第一次调用就崩还是连续调用多次后崩后者多半是内存被污染。第二步用调试器C#工程可以把“本机代码调试”打开这样崩溃时会停在C侧能看到具体是哪个函数。第三步检查所有指针参数的传递方式C里如果接口定义为int* ptrC#那边要用ref int或IntPtr千万别写成传值。第四步如果C内部new了对象确保C#那边有对应的释放函数并用IntPtr保存指针调用完显式释放防止内存泄漏和二次释放。注意遇到“访问冲突”第一反应不要是“是不是C崩了”而是先问自己“这块内存到底归谁管、生命周期到哪结束”。90%的崩溃都是生命周期问题。5.2 链接错误、缺少运行库与“系统日志异常”新手在环境配置阶段最常见的第二种报错是“无法解析的外部符号”或“LNK2019”这属于链接错误不是编译错误。它通常意味着你调用了某个函数但没有对应的实现代码参与链接或者库文件没有引入。解决办法通常是检查代码里函数名是否拼写一致、检查头文件有没有对应的实现、检查项目配置里的附加库路径是否正确。再说说Visual C Redistributable的问题。上一章讲了概念这里补充一个实际经验系统里多装几个版本不冲突2015、2017、2019、2022的运行库可以共存。如果一个程序提示缺dll优先装最新的、x64和x86都装上因为32位程序也需要对应的32位运行库有些人只装了x64结果32位程序继续报错就会陷入“装了半天还缺”的死循环。热搜词里还有一个“捕获到标准c异常。有关详细信息请参见系统日志文件”的错误提示这个常出现在专业软件比如UG NX的C相关场景中。遇到这种情况核心思路是先看系统事件查看器里的应用程序日志和程序自带的日志目录定位到具体是哪个模块抛的异常然后检查对应的环境依赖是否完整有没有缺系统补丁或运行库。这类错误本质是程序内部抛了C异常并不一定是你的代码问题但排查思路是一样的先定位再补依赖最后才怀疑代码逻辑。5.3 新手错误速查表我把新手入门阶段最高频的报错整理成一个速查表方便你直接对照错误类型典型现象解决方法中文符号编译报“未定义的标识符”或者诡异的语法错误检查括号、分号、引号是否为半角mian拼错链接报“无法解析的外部符号 main”检查main是否拼写正确忘记写return程序行为怪异但不是马上崩main返回0其他函数按类型返回数组越界输出乱码、崩溃或者不崩但结果不对检查下标是否超出数组长度string忘加头文件“string未定义”#include stringcin/cout没写命名空间“cout未声明”写using namespace std;或用std::前缀数组名复制给另一个数组编译报错用循环、strcpy或改用string/vector指针不初始化就用随机崩溃指针定义时初始化为nullptr用前判空编译成功但点击运行闪退控制台一闪就没了程序结束前加cin.get()或直接查看运行结果这张表值得收藏但更重要的是你要学会读报错信息。C编译器的报错虽然乱七八糟但第一行往往就是关键不要只看最后一行要从第一行开始看。VS Code里点一下带红色波浪线的位置下面输出面板会告诉你具体在哪个文件和哪一行。逐条解决解决一条少一条这是每个C新手都必须经历的阶段。6. 从入门到进阶给新手的学习路线与个人体会6.1 学完这些之后怎么走数据结构、STL与项目基础语法学完会写冒泡排序和猜数字之后下一步就是数据结构和STL了。结构体链表是迈入数据结构的第一课。结构体用struct定义链表就是结点之间用指针连起来之所以用结构体是因为每个结点除了数据还要存一个指向下一个结点的指针。struct Node { int data; Node* next; Node(int d) : data(d), next(nullptr) {} };这个知识点看着简单实际上是指针理解的试金石。很多人第一次写链表插入删除都会崩因为忘了更新上一个结点的next指针或者new出来的结点忘了初始化。我的建议是先用纸笔画出结点和箭头的图再对着图写代码通过后再去IDE里调试验证。STL里面最重要的是vector、map、string、sort。vector能当动态数组用map能做键值映射STL的sort排序效率极高而且可以配合自定义比较函数就是前面讲回调的那个思路。很多人学了C半年才开始正儿八经用STL太晚了这些是C给你的基础设施早点用早点舒服。项目驱动是最有效的学习方式。我的建议路线控制台猜数字控制台学生成绩管理系统控制台贪吃蛇图形界面的待办事项工具然后根据方向选进阶路线。想走游戏方向就去学Unity或UE的C编程想走后端或底层就去研究网络编程和系统API想搞工业软件就了解CAM/CAE相关的二次开发。C的应用场景非常宽先把自己脚下的路走通再抬头看方向。6.2 面试与“C八股”怎么准备“c八股”这个热搜词我猜很多人是在准备面试。所谓八股其实就是高频考点集合比如虚函数表怎么工作、const和static分别有什么用、智能指针实现原理、什么时候用vector什么时候用list、深拷贝浅拷贝的区别、new和malloc的区别、移动语义和右值引用。这些确实是C面试的常客。但我想跟你说句掏心窝子的话背八股不如写代码。你光背“new会调用构造函数malloc不会”但没写过内存泄漏的程序你根本记不牢。我面试候选人的时候最看重的是能不能把一个问题拆成代码以及能不能定位问题。八股可以背但要在会写代码的基础上背不然面试官追问一个“为什么这样设计”你就露馅了。网上总结的C八股文非常全动辄几十个问题但你把它当字典查不要当教材背。建议在学习过程中遇到一个相关概念就去查对应章节然后自己写一段代码验证比什么记忆都牢固。6.3 我的几点实际体会最后分享几点我自己的真实体会。第一学C一定要习惯看报错。报错不是羞辱你是你最忠实的导师。我见过太多人写错了就慌了到处复制粘贴结果把问题改得更复杂。正确姿势是读第一行报错找到文件和行号再看旁边的代码上下文很多时候就是差一个分号、少一对括号。第二调试技能比背语法重要。VS Code里打断点、查看变量值、单步执行这些能力越早学越好。你如果只会printf大法排查复杂问题会非常痛苦。学会看调用栈嘉定你的程序崩了你能看到是从哪个函数一层层进入出事地点的这个技能在真实工程里是无价之宝。第三忘掉“我太笨了”。C确实有一堆让人崩溃的点指针、引用、常量、移动语义、重载这些概念叠加起来很容易劝退人。但你遇到的每一个问题前人几乎都遇到过、都讨论过、都写过博客。养成搜索的能力学会用英文关键词搜索把Stack Overflow当字典你会发现难的不是C而是没人告诉你下一步干什么。C入门这条路没有捷径但有方法。方法和路径我已经尽我所能写在这里。接下来就轮到你去打开编译器亲手写第一行代码了。⚠️ 本文内容仅用于技术学习与交流请勿用于任何违反法律法规的用途。如果你坚持读到了这里有一个小经验必须送给你每天花半小时写代码哪怕只是复现今天讲的示例也比周末狂看三小时有效得多。编程是手艺活手不练全是空中楼阁。