简介:面向C语言入门学习者的《C语言基础(全)》以单份PDF文档形式整理,适合刚接触编程、需要系统梳理语法与练习代码的读者,也可作为课堂教学或自学复习的参考资料。压缩包内仅含1个PDF文件,大小约1.57MB,内容按周次组织,覆盖计算机与编程语言、最大公约数算法、编译与解释执行、C语言标准沿革、变量初始化与scanf输入、表达式与运算符优先级,以及时间差计算、英制单位转换等示例程序。文档在讲解概念的同时穿插可直接运行的代码片段,并对关系运算、赋值与递增递减运算符、注释与判断结构等基础要点作了归纳,便于读者边读边敲、对照理解。已有13516人浏览学习,说明该资料在入门阶段具有一定参考价值。整体篇幅精炼、结构清晰,适合用作C语言基础知识的快速过一遍与查漏补缺。
1. 从一份 PDF 说起:零基础学 C 语言到底在学什么
很多人第一次搜「C语言 PDF资料」,下回来的是一份叫「基础文档」的东西,翻到第三页就卡住了——补码、指针、位运算,一个比一个玄。我当年也一样,明明只想让电脑帮忙算个数,文档却先讲了一堆硬件。后来才明白,把原因归到文档写得难并不准确,真正的问题是没人告诉你该按什么顺序读。
《C语言基础(全).pdf》讲的就是这件事:让一台只会执行机器指令的机器,听懂你用 C 写下的那几行话。它从「计算机和编程语言」起步,一路讲到变量、输入、判断、循环、数据类型,每个知识点后面都跟着能真正跑起来的小例子——最大公约数、猜数字、英尺换算、整数逆序。
它适合完全没写过代码、但愿意在电脑前坐半小时动手敲的人。想三天速成的,读到第二章就会放弃。
2. 先建立三个概念:程序、编译、解释,还有那条叫 C89 的分数线
新手最容易被劝退的地方,是还没写第一行代码,就被一堆名词砸晕。这一章只做一件事:让你在翻开文档之前,脑子里先有三个能对得上号的画面。概念清楚了,后面所有代码都是一句话的事。
2.1 计算机没你想的聪明:它只会说自己的方言
你写的int u = 32;在电脑看来,和一堆乱码没区别。CPU 真正能执行的,是一串由 0 和 1 组成的机器指令,业内叫机器语言。所谓「程序」,本质就是一份写给计算机的指令清单,只不过有人用机器语言写,有人用接近人话的语言写。
C 语言就是后者。它用英文单词和数学符号描述你想干的事,比机器语言好读一万倍,但代价是:计算机看不懂,中间必须有东西帮忙转一道。这个「转一道」的过程,就是接下来要说的编译和解释。
记住这个链条就够了:你写 C 代码 → 中间工具转换 → 变成机器能执行的指令 → 电脑开始跑。以后遇到任何报错,你都可以问自己一句:现在是卡在「转换」这一步,还是卡在「执行」这一步?
2.2 编译像翻译整本书,解释像同声传译
文档里把这两种执行方式讲得很清楚,我用两个生活场景给你对上号。
解释:借助一个程序,那个程序会试着理解你写的程序,然后按你的要求执行。像一个同声传译站在你旁边,你说一句他翻一句,翻完立刻生效。好处是运行过程中还有机会改源代码,试错成本低。
编译:借助一个程序,就像一位翻译,把你的程序整个翻译成机器语言写的程序,然后这个机器语言程序就能直接执行了。像是把整本书先译好再出版,出版之后读起来又快又稳,运算性能是确定的。
提示:不要把「编译型语言」和「解释型语言」当成两个物种。语言本身没有解释和编译的区分,任何语言都可以解释执行或编译执行,只是具体执行方式不同。文档里那句话很实在——两种执行方式没有非常大的本质区别,只是传统和喜好的问题。
那为什么新手往往被推荐从编译开始?因为编译会一次性把语法错误全挑出来,逼你把代码写规矩。这有点烦,但对建立基本功特别有效。
2.3 C 是工业语言:它不在聚光灯下,但很多东西离不开它
文档里列了一串 C 的主场:操作系统、嵌入式系统、驱动程序、底层驱动、图形引擎、图像处理、声音效果。这些词对新手来说很抽象,换个说法你就懂了——你手机里那些响应速度很快的功能、路由器里默默跑着的那套逻辑、游戏里画面和音效的底层处理,往下挖几层,常常能挖到 C。
文档还有一句大实话值得划重点:日常应用很少直接用 C 语言编写,学习 C 的过程主要是写练习代码,而非真实的软件。这句话能救很多人的心态。你写的那些「算两个数最大公约数」的小程序,看起来毫无用处,但它们是训练肌肉的记忆动作,不是产品。
它给出的两个判断也很有意思:开发效率 >> 学习过程,开发效率 >> 开发乐趣。意思是学 C 的投入产出比不算好看,前期枯燥、成就感来得慢。想清楚这一点再开始,比稀里糊涂报个班然后三天热度要强。
顺带把「标准」这件事说清楚。C 语言发布初期用的是 K&R 标准,1989 年一个标准化组织发布了 ANSI C,第二年这个标准被国际标准组织接受,叫 C89。之后在 1995 年和 1999 年又更新了两次,就是 C95 和 C99。这个标准的意义在于:同样一行代码,在 C89 下可能编译不过,在 C99 下就能跑。你以后看到的报错里如果出现「C99」字样,八成是你在不该定义变量的位置定义了变量。
2.4 上手前的人肉练习:让欧几里得算法在纸上跑一遍
在敲代码之前,先做一次「人肉执行」。文档里给的第一个算法是求最大公约数,步骤只有三条:
- 如果 v 等于 0,计算结束,u 就是最大公约数;
- 如果 v 不等于 0,那么计算 u 除以 v 的余数,让 u 等于 v,而 v 等于那个余数;
- 回到第一步。
拿 u = 32、v = 26 走一遍,用表格记下来:
| 轮次 | u | v | u % v 的余数 | 走完之后 |
|---|---|---|---|---|
| 第 1 轮 | 32 | 26 | 6 | u 变 26,v 变 6 |
| 第 2 轮 | 26 | 6 | 2 | u 变 6,v 变 2 |
| 第 3 轮 | 6 | 2 | 0 | u 变 2,v 变 0 |
| 第 4 轮 | 2 | 0 | 不需要算 | v 是 0,结束,答案是 2 |
三行推演做完,你会发现算法一点都不神秘:它就是在反复做「除法取余」和「两个数往下挪一位」,直到余数变成 0。这一步别嫌土,很多人后面写循环写不明白,就是因为从来没在纸上跑过一次循环。你先在纸上跑通了,再去看代码,会有一种「这不就是我刚才干的事吗」的踏实感。
3. 第一个能跑的例子:二十行代码搞定最大公约数
纸上跑通之后,最想做的一定是让它真的跑起来。这一章的目标很小:让你从「文档里看见代码」变成「屏幕上看见结果」。跑通一次,后面所有例子都是复制这个流程。
3.1 先准备一个能编译的环境
你不需要一上来就折腾复杂配置。只要满足一个条件:你的电脑上有一个能把 C 源码变成可执行程序的编译器。
Windows 上,很多编辑器或集成环境自带编译器,装上就能用,新建一个.c文件直接点运行。macOS 和 Linux 一般自带编译工具,打开终端就能用。命令的名字可能是cc,也可能是别的,不同环境叫法不一样,你只要知道它是「编译命令」就行。
# 把 gcd.c 编译成一个叫 gcd 的可执行文件 cc gcd.c -o gcd # 运行刚才生成的可执行文件 ./gcd-o的意思是「output」,后面跟的gcd是你想起的名字,不写的话很多环境会默认生成一个叫a.out的文件。./表示「运行当前目录下的这个文件」,是告诉终端别去系统别处找。
注意:如果你的终端提示找不到
cc这个命令,把当前环境里编译器对应的命令替换上去即可,这一步只是递个话,不影响你理解后面任何一行代码。
3.2 完整代码:从 32 和 26 开始
#include <stdio.h> // 引入输入输出功能,printf 就来自这里 int main(int argc, char const *argv[]) { int u = 32; // 第一个数 int v = 26; // 第二个数 while (v != 0) { // 只要 v 还不是 0,就继续绕圈 int temp = u % v; // 先算出 u 除以 v 的余数,暂存起来 u = v; // 把 v 的值挪到 u 上 v = temp; // 把刚才的余数挪到 v 上 } printf("%d", u); // v 变成 0 的时候,u 就是答案 return 0; }保存为gcd.c,按上面的两条命令走一遍,屏幕上会出现2。恭喜,你已经完成了一次完整的「写代码 → 编译 → 执行」。
3.3 逐行拆开看:这几行到底做了什么
#include <stdio.h>这一行不是可有可无的装饰。stdio 是「standard input output」的缩写,printf这个往屏幕上打字的函数就住在里面,不写这行,编译器会告诉你printf不认识。
int main(...)是程序的大门。任何 C 程序都从这里开始执行,一个程序只能有一个 main。括号里的argc、argv是给命令行参数用的,新手阶段完全可以不理会,照抄就行,但两个都必须保留。main前面的int表示这个程序结束时会给系统回一个整数,所以最后要写return 0;,0 代表一切正常。
int u = 32;是定义变量并赋值。int表示这是一个整数类型的变量,等号是「把右边的值装进左边的盒子」,不是数学里的恒等。变量必须先定义后使用,而且第一次使用之前应该有值,这是文档里反复强调的一条。
while (v != 0)是循环,意思是「只要括号里的条件成立,大括号里的东西就一遍遍重复」。!=读作「不等于」。
大括号里的三行是整个算法的精髓,也是新手最容易看晕的地方:
int temp = u % v; // % 是取余数,32 % 26 得到 6 u = v; // 原来的 v(26)搬到 u 上 v = temp; // 原来的余数(6)搬到 v 上%不是百分比,是两个数相除之后的余数。这三行合起来的效果,就是把(u, v)这一对数往下挪一格,变成(v, 余数)。先算余数存到 temp,再把 v 给 u,最后把 temp 给 v。顺序不能乱:如果先写u = v;,那么 u 原来的值就被覆盖了,你再也算不出u % v了。这个临时变量的用法,文档里后面还专门讲了一个「交换两个变量」的小套路,思路一模一样。
printf("%d", u);是往屏幕输出。%d是个占位符,意思是「这里要放一个整数」,逗号后面的u就是填进去的值。
3.4 改三个数试试:动手验证你学没学会
光看懂不算会,改一下参数能跑对才算。
第一组:把u改成 100,v改成 75,其它不动。跑出来应该是 25。手算验证一下:100 % 75 = 25,(u, v)变成(75, 25);75 % 25 = 0,(u, v)变成(25, 0);v 是 0,停,答案 25。
第二组:把u改成 7,v改成 7。跑出来应该是 7。这一组能检验你有没有真正理解循环的结束条件——第一轮 7 % 7 = 0,(u, v)变成(7, 0),立刻结束,输出 7。
第三组:把u改成 5,v改成 0。这时候循环一次都不会进,直接输出 5。这也是算法的第一条规则:v 等于 0,u 就是答案。
如果你想让输出好看一点,把printf("%d", u);换成printf("最大公约数是%d\n", u);,\n是换行。这些改动看起来琐碎,但每一个都在强化一件事:代码是可以被你把玩的东西,不是只能看的印刷品。
4. 让程序听你说话:变量、scanf 和四则运算
上一章的程序是「写死」的:想换数字就得改源码重新编译。真正的程序得能接受你现场输入。这一章把互动能力装上,顺便把新手最容易翻车的几个运算符一次说清。
4.1 变量必须先有值:初始化这件事别偷懒
文档里有一句话值得抄下来:所有的变量在第一次被使用之前应该有过初始化。
这很容易理解:变量本质上是一块内存空间,如果没给值就用,里面装的是上一次谁留下来的垃圾数据,程序行为就变得没法预测。新手常见的翻车方式是这样:
int sum; // 只声明,没给初值 sum = sum + 1; // 拿未知数加 1,结果完全不可控改成int sum = 0;就稳了。别指望编译器替你兜底,它不会。
另一件事关于位置:ANSI C 标准只能在代码开头的地方定义变量,C99 则是只要在使用前定义就可以。如果你在教程里看到所有变量都堆在函数最上面,那不是风格问题,是那个年代的标准要求。现在大部分环境按 C99 来,你可以在靠近使用的地方定义,代码可读性更好。
常量方面,C99 起支持const,一般写成全大写:
const int MAX_COUNT = 100; // 以后谁想改它,编译器会拦下来4.2 scanf 的三种返回值:判断用户到底输了什么
scanf是你和程序之间的传声筒,但它的返回值很多新手完全没看。文档把它拆成三种情况,非常实用:
第一种,返回正整数,表示正确输入参数的个数。比如scanf("%d %d", &a, &b);,你输入「3 4」,返回 2;输入「3,4」,能读到 a 读不到 b,返回 1。
第二种,返回 0,表示用户的输入不匹配,一个都没读进去。还是上例,你输入「,3 4」,第一个位置就不是数字,返回 0。
第三种,返回 EOF,这是在stdio.h里定义的常量,通常值是 -1,表示输入流已经结束。Windows 下按 Ctrl+Z 再回车(可能要按两次),Linux 和 Unix 下用 Ctrl+D,就表示输入到此为止。
提示:别小看 EOF。后面你写「不断读入数字直到没有输入」的程序时,判断条件靠的就是它,写错一个符号程序就变成死循环。
4.3 第一个互动程序:英尺英寸换算成米
来看文档里这个例子,它把输入、整数和浮点数混算全串起来了。
#include <stdio.h> int main(int argc, char const *argv[]) { printf("请分别输入身高的英尺和英寸," "如输入\"5 7\"表示5英尺7英寸:"); int foot; int inch; scanf("%d %d", &foot, &inch); // 读两个整数,分别装进 foot 和 inch printf("身高是%f米。\n", ((foot + inch / 12.0) * 0.3048)); return 0; }输入「5 7」,输出大约是1.701800 米。
关键在于inch / 12.0这里的12.0。如果写成12,那就是整数除以整数,结果还是整数。7 除以 12 得 0,整个身高就变成 5 英尺,误差巨大。写成12.0之后,整数会自动转成浮点数参与计算,结果才对。文档里对这条的表述是:当浮点数和整数一起运算时,整数会自动改为浮点数;整数在计算机里运算比浮点数快。
%f是浮点数的输出占位符,和%d不能混用。用错了不会报错,但屏幕上会出现一串让你怀疑人生的数字。
4.4 运算符优先级和那两个容易写错的符号
C 的算术符号和数学里基本一样:+加、-减、*乘、/除、%取余(两个数相除后的余数)、()括号。区别在于乘号不能省略,ab是两个变量名,a*b才是相乘。
优先级从高到低大概是这样:
| 优先级 | 运算符 | 结合方向 | 举例 |
|---|---|---|---|
| 1 | 单目+- | 自右向左 | a * -b |
| 2 | */% | 自左向右 | a * b |
| 3 | +- | 自左向右 | a + b |
| 4 | = | 自右向左 | a = b |
拿不准就加括号,括号永远最优先,而且是给读代码的人看的。
赋值也是运算,也有结果。a = 6的结果就是 6,所以a = b = 6等价于a = (b = 6),从右往左算。这种「嵌入式赋值」虽然能跑,但文档明确不推荐:不利于阅读,容易产生错误。
复合赋值是省字的写法:+=、-=、*=、/=、%=,两个运算符中间不能有空格。total /= 12 + 6;和total = total / (12 + 6);完全等效,右边会先算括号。
递增递减是 C 的特色,++和--,算子必须是变量。前缀和后缀的区别我用一张表说清:
| 表达式 | 运算 | 表达式的值 |
|---|---|---|
count++ | 给 count 加 1 | count 原来的值 |
++count | 给 count 加 1 | count 加 1 以后的值 |
count-- | 给 count 减 1 | count 原来的值 |
--count | 给 count 减 1 | count 减 1 以后的值 |
文档还讲了个冷知识:当年某台小型机上有两条特殊指令,一条递增一条递减,有了++--编译器就能直接翻译成这两条指令,跑得更快。今天这个意义已经不大了,现代编译器你写a = a + 1也会生成同样的指令,保留下来更多是习惯和方便。
最后补一个一定要会的小套路——交换两个变量的值:
int a = 5, b = 6, t; t = a; // 先把 a 存进临时变量,别丢 a = b; // b 的值覆盖 a b = t; // 再把临时变量还给 b少了那个t,两个变量会变成一样的值,这是我在新手代码里见过的高频错误。
5. 新手排查手册:这 5 个坑我几乎每个都踩过
写代码的过程里,出错的次数远多于写对的次数,这很正常。关键是要有排查顺序:先看程序卡在哪一步,再看那一步对应哪类问题。下面这五个坑,覆盖了零基础阶段八成的报错。
5.1 编译根本过不去:先检查标点和分号
现象:点运行,编译窗口一片红字,程序压根没跑起来。
原因:九成是语法层面的小问题——语句末尾漏了分号、大括号不成对、用了中文标点。C 语言的标点符号必须全是英文半角,中文的;,(在编译器眼里是非法字符。还有一种隐蔽情况:字符串里的引号没转义,比如你想打印一个双引号,得写\",直接写"会把字符串提前截断。
解决:先看编译器报的第一条错误,别管后面那一长串,后面的往往是第一条引发的连锁反应。检查报错行和它上一行的结尾有没有分号。用编辑器的搜索替换把中文标点全换成英文。养成「写完一条语句就敲分号」的习惯,比事后找错省事得多。
5.2 编译过了结果不对:整数除法和等号陷阱
现象:程序能跑,但算出来的数离谱。比如算平均身高,结果永远是整数;比如判断条件怎么写都成立。
原因:最常见的是整数除法。7 / 12在 C 里等于 0,不是 0.5833。想得到小数,两个操作数里至少有一个得是浮点,写成7 / 12.0或7.0 / 12。另一个高频错误是把判断相等写成赋值:if (a = b)是「把 b 的值赋给 a,然后看这个值是不是非零」,而你要的是if (a == b)。
解决:凡是涉及除法又想保留小数的,主动把其中一个数写成带小数点的形式。凡是 if 后面跟着单个等号,立刻改成两个。文档里那句提醒很到位:if 只要求括号里的值是零或非零,所以if (a = b)完全可能编译通过,运行时却永远走同一个分支,属于最难找的那类问题。
5.3 程序卡住不动:scanf 的取地址符和输入格式
现象:程序运行后停在那里,你怎么敲都没反应,或者输入了却读到奇怪的值。
原因:scanf的第二个参数必须是变量的地址,也就是要在变量前面加&。写成scanf("%d", a);就是把值当地址传,程序轻则读到垃圾,重则直接崩。另一个原因是输入格式不匹配:scanf("%d %d", &a, &b);中间有个空格,但你输入了逗号分隔,第二个数就读不进去。
解决:先检查每个scanf的参数有没有&。再用返回值确认到底读进去几个:返回 2 说明两个都成功,返回 1 说明只成功一个,返回 0 说明格式完全不对。这个技巧文档里讲得很细,养成「读入之后立刻验证返回值」的习惯,调试时间能少一大半。
5.4 循环跑飞:边界写错和多执行了一次
现象:程序停不下来,或者结果总是比正确答案多一次、少一次。
原因:循环条件写反(while (v == 0)应该是!=)、循环变量忘了更新、do-while和while混淆。do-while是先执行一次再判断,while是先判断再执行。文档里那个求平均数的例子就是典型:用do-while会多读一次,还要在循环里补一个 if 把 -1 排除掉,改成while反而少一次判断。
解决:先问自己这个循环「至少要执行一次吗」。如果必须执行一次,比如把输入除以 10 统计位数,用do-while,注意输入 0 时它会输出 1 位,这符合常识;如果没有把握,用while。另外,手动在循环体里加一行printf把关键变量打出来,看看每一轮的值,比盯着屏幕空想快得多。
5.5 浮点数比较:为什么 1.345 加 1.123 不等于 2.468
现象:两个浮点数算出来明明应该相等,if (c == 2.468)却走进「不相等」的分支。
原因:浮点数在计算机里是近似存储的,float的有效数字大约 7 位,double大约 15 位,多一位都是猜的。你写float a = 1.345f;时它已经在被舍入了,再加一个1.123f,误差就冒出来。
解决:比较两个浮点数,算它们的差,看差是不是足够小:
#include <stdio.h> #include <math.h> // fabs 求绝对值 int main() { float a = 1.345f; float b = 1.123f; float c = a + b; // 差值小于一个极小的量,就认为它们相等 if (fabs(c - 2.468) < 1e-12) { printf("相等\n"); } else { printf("不相等,c=%.10f\n", c); } return 0; }文档里还有一句特别实用的话:算钱的时候用整数,整数是精确的。把金额换算成「分」来算,最后再除法显示成「元」,比用浮点数一路乘除靠谱得多。
6. 下一步学什么:把这份文档当成一张进度表
跑通最大公约数、能自己改参数之后,最容易犯的错是回头从头再看一遍。别这么干。文档的组织方式是跟着周次走的,它就是一张现成的进度表,你只要按顺序往下推就行。
我给你排一条零基础能走完的路线:
| 阶段 | 对应文档内容 | 你要交付的东西 |
|---|---|---|
| 第 1 周 | 计算机和编程语言、C 语言简史、第一个程序 | 跑通一个程序,能说清编译和执行的区别 |
| 第 2 周 | 变量、输入、表达式、运算符优先级 | 写一个能读入两个数并做四则运算的小程序 |
| 第 3 周 | 判断、if、switch-case、注释 | 把分段函数用 if 和 switch 各写一遍 |
| 第 4 周 | 循环、数位数、猜数字、整数逆序 | 写一个猜数字游戏,能统计猜了几次 |
| 第 5 周 | for 循环、goto、正序分解整数、最大公约数 | 用枚举法和辗转相除法各求一次公约数 |
| 第 6 周 | 数据类型、补码、整数范围、浮点、字符、强制转换 | 把前几周的程序全部改一遍,注意类型 |
关于怎么学,我自己有三条经验。
第一,别跳过「手算」。每一段循环,先在纸上跑三轮,写清楚每一轮变量的值,再去写代码。你会发现调试时间大幅下降,因为你脑子里已经有了标准答案,程序一跑偏你立刻知道错在哪。
第二,每学一个语法,就去改上一周的作业。学完 for 循环,把之前用 while 写的阶乘重写一遍;学完类型,把求平均数的程序改成整数除法会错、浮点除法才对的那一版。文档里的例子足够多,不用额外找题目。
第三,报错要读完。新手最常做的是看到红字就改一行重跑,改到不报错为止,结果程序逻辑早就面目全非。文档里那些容易忽略的规则——变量必须先初始化、关系运算的结果只有 0 和 1、==和=不能混——全是血泪经验换来的,遇到报错先回头对照这些规则,八成能找到原因。
如果你现在还没开始跑第一个程序,那就别急着往后看。打开文档的第一周内容,把那段求最大公约数的代码原样抄进去,编译,看到屏幕上那个2跳出来的瞬间,你就已经过了新手最难的那道坎。
从那以后我每次学新东西,都强制自己先跑通一个最小的例子,再回头读概念,读起来完全是两种感觉。希望帮到你。
本文还有配套的精品资源,点击获取