在前面的讨论中,我们探讨了编程语言的十大通用概念。为了更深入地理解这些概念,本文将通过实际的代码示例来帮助你更好地掌握它们。
简单回顾#
编程的本质#
编程的本质是通过 编写和组织代码,以实现计算机按照特定逻辑和步骤执行任务。这涉及到使用编程语言与计算机沟通,管理和处理数据,并控制程序的执行流程。编程的基础构件包括编程语言、数据结构和控制流,它们共同作用以实现功能并解决问题。
程序的本质#
程序的本质是由 数据和算法构成 的,数据是程序处理的信息,算法则是处理这些数据的具体步骤和方法。程序通过将数据和算法结合起来,执行特定的操作或任务,从而实现功能和解决问题。理解数据和算法的相互作用是编写高效、功能强大的程序的关键。
编程语言的通用概念#
| 序号 | 名称 | 解释 |
|---|---|---|
| 1 | 变量 | 存储数据的基本单位,用于引用和操作值。 |
| 2 | 数据类型 | 定义变量可以存储的数据类型,如整型、浮点型等。 |
| 3 | 控制结构 | 通过条件和循环语句控制程序的执行流程。 |
| 4 | 函数 | 封装可重用代码块的机制,用于执行特定任务。 |
| 5 | 操作符 | 执行运算的符号,如加法、减法等。 |
| 6 | 输入/输出 | 与外部环境交互的方式,如读取输入和输出结果。 |
| 7 | 错误处理 | 处理程序运行时出现的异常情况的机制。 |
| 8 | 注释 | 对代码进行解释的文字,不影响程序的执行。 |
| 9 | 作用域 | 变量或函数在程序中的可见范围。 |
| 10 | 模块化 | 将代码分解成独立的模块以提高代码的组织性和可维护性。 |
预备知识积累#
老朋友 —— 冯·诺依曼#
计算机是一种电子设备,因此只能识别两种基本状态:通电和断电。在最初的 ENIAC 计算机中,程序是通过大量开关和连接电线来设置的。这种方式导致每次修改程序都需要花费大量时间来重新设置开关状态和电线连接。

为提高效率,工程师们开始探索将程序和数据存储在存储器中的方法。数学家 冯·诺依曼提出了这一思想,并将其系统化,形成了存储程序计算机模型,即冯·诺依曼架构。这一模型使得计算机能够更加灵活和高效地处理程序和数据。
他通过 0 和 1 来表示计算机能够识别的断电和通电两种状态,所有计算机识别的指令和存储的数据都是由这两种二进制状态组成的。
啊?指令、机器码 & 汇编#
指令是计算机处理器能够识别和执行的操作命令。指令可以告诉处理器执行某个特定的操作,比如算术运算(加法、减法)、数据传输(从内存读取数据或将数据写入内存)、逻辑操作(AND、OR)、控制流操作(跳转、调用子程序)等以下是不同架构(x86/x86-64 和 ARM)常见指令的简化分类表:
类别 x86/x86-64 指令 ARM 指令 数据传输指令 MOVLDR算术运算指令 ADDADD逻辑运算指令 ANDAND控制流指令 JMPB系统调用指令 INTSVC机器码是指令的具体编码,由一串二进制数字组成的。但是直接用二进制表示机器码既冗长又不易理解。为了简化,程序员和开发人员通常用十六进制来表示这些机器码。以下是用机器码打印 hello world 的示例
那为什么不直接用十进制呢?
十六进制:每个十六进制数字可以表示 4 位二进制数。例如,十六进制的
0xF对应二进制的1111。这样,一个字节(8 位)可以用两个十六进制字符来表示,这使得机器码的表示更加紧凑和简洁。十进制:每个十进制数字只能表示 4 位二进制数的部分(从 0 到 9),因此要表示一个字节(8 位)的机器码,可能需要更多的数字(例如,从 0 到 255 的值)。
示例:
十六进制表示:
0x90代表机器码中的 NOP 指令(No Operation),它在二进制中是10010000,这是一个字节的表示方式。十进制表示:
144是十六进制0x90的十进制表示。但在机器码中,直接使用0x90更为简洁和易读。
汇编语言是机器码的助记符表示,便于人类阅读和编写。它不是第一个编程语言,但它是第一个能够直接操作计算机硬件,同时提供一些编程便捷性的语言。
以下是用汇编语言打印 Hello Word 的示例
section .data msg db 'Hello, World!', 0x0A ; 字符串数据,包含换行符 len equ $ - msg ; 计算字符串长度 section .text global _start _start: ; 写入消息到标准输出 mov eax, 4 ; 系统调用号 (sys_write) mov ebx, 1 ; 文件描述符 (stdout) mov ecx, msg ; 指向消息的指针 mov edx, len ; 消息长度 int 0x80 ; 调用内核 ; 退出程序 mov eax, 1 ; 系统调用号 (sys_exit) xor ebx, ebx ; 退出码 0 int 0x80 ; 调用内核总结:
- 指令 是高层次的概念,表示要执行的操作。
- 机器码 是指令的具体编码,是由操作码和操作数组成的二进制表示。
- 汇编语言 是机器码的助记符表示,便于人类阅读和编写。
计算机:汇编语言(完了,读不懂) \(\to\) 机器码(欧耶,我的大脑可以看懂)
编译器 & 解释器 ?#
- 编译器是一种将
源代码(用编程语言编写)转换为目标代码(通常是机器码或字节码)的程序。目标代码可以直接在计算机上运行,CPU 根据机器码执行相应的指令,而不需要再解释。
与之对应就有解释器
解释器是一种逐行读取、分析和执行源代码的程序。它将源代码逐条翻译为机器码并立即执行,而不生成可执行文件。
二者对比
属性 编译器 (Compiler) 解释器 (Interpreter) 执行方式 一次性将源代码转换为机器码 逐行解释和执行源代码 产出 可执行文件 直接运行源代码,不生成可执行文件 执行速度 快,因为已转换为机器码 慢,因为需要逐行解释 错误检测 在编译阶段捕获语法和语义错误 在运行时捕获错误 平台依赖性 平台依赖,编译后的文件通常不能跨平台使用 通常跨平台,只需在目标平台上有相应的解释器 调试速度 编译时间较长,但运行快 无需编译,调试更方便但运行慢
还分高低贵贱 ?#
刚开始学可能会听过编程语言的 “高级” 和 “低级”,但那并不是指语言的 “高低贵贱” 。它们是用来描述语言抽象层次的术语:
高级语言:接近人类自然语言,更易读、易写、易理解,适合用于开发应用程序,如 Python、Java、JavaScript 等。高级语言通过编译器或解释器转换为机器代码来执行。
低级语言:更接近机器语言,直接与硬件交互,如汇编语言和机器语言。低级语言的编程更复杂,但可以提供更高的执行效率和更精确的硬件控制。
因此,“高级”和“低级”只是指抽象层次和对硬件的接近程度,而不是语言的优劣。
为什么偏偏选 C 语言?#
它是了解其它语言底层实现原理必备语言
C语言之所以被认为是了解其他编程语言底层原理的必备语言,是因为:
接近计算机硬件:C 语言很 “接地气”,它能直接跟计算机硬件 “对话”。学会了 C 语言,你就能理解计算机是怎么管理内存、怎么处理数据的,这些东西是很多其他编程语言看不见的 “幕后工作”。
操作系统的语言:很多操作系统,比如 Windows 和 Linux,都是用 C 语言写的。所以,学 C 语言就像是在学习这些操作系统的 “母语”,能帮你更好地理解它们是怎么运作的。
很多语言的 “老祖宗”:C 语言就像是很多现代编程语言的 “老祖宗”。很多编程语言,比如 C++、Java、Python,都是基于 C 语言发展起来的。如果你懂 C 语言,你就能更容易地理解这些语言的规则和用法。
能写出高效的程序:用C语言写的程序往往更快,因为它允许你直接控制计算机的资源(比如内存)。如果你想知道为什么一些程序运行得这么快,学习 C 语言会很有帮助。
理解内存和指针:C 语言让你直接管理计算机的内存,这是大多数其他语言不允许你做的事情。通过学习 C 语言,你可以理解计算机是如何 “记住” 和 “存储” 数据的,这在理解其他语言的运行原理时非常有用。
总的来说,学习 C 语言就像是在学习计算机的 “母语”,它能帮助你深入理解计算机的运作方式,明白其他语言是怎么在它的基础上构建起来的。

环境搭建#
要想成功运行你的 C 语言程序需要写代码的程序、编译代码的程序
编辑器:记事本、word、vscode
编译器:GCC、clang
而 IDE (集成开发环境)二者都有
Windows#
不要使用 VSCode 或者 Visual Studio
由于只是入门课,所以不需要 复杂 或者 庞大 的工具来编写。甚至可以说整个大学期间都用不上。
这里推荐 DevC++ 这款工具,界面简单,不需要配置编译器,适合初学者。

最新版本为 6.3,选择带 编译器 的版本下载安装。
Linux#
由于已经掌握了 Linux 基础,Linux 不用安装 任何软件即可开始写代码。
第一个 C 程序#
和众多教程一样,先打个小怪
#include <stdio.h>
// 我的第一个 C 语言程序
int main() {
printf("Hello, World! \n");
return 0;
}
编译阶段#
预处理阶段:
编译器首先会处理预处理指令
#include <stdio.h>,它会将标准输入输出库stdio.h的内容包含到当前源代码中。stdio.h库定义了printf函数等标准输入输出函数的原型和宏定义。注释处理:
编译器会忽略注释部分
// 我的第一个 C 语言程序。注释的目的是帮助程序员理解代码,编译器在编译时会将其忽略。语法分析:
编译器会从上到下分析代码的语法结构。首先,它会看到
int main(),这是程序的主入口函数。编译器知道它应该在这里开始执行程序。语义分析和符号表生成:
在语法分析通过后,编译器会进行语义分析。它会检查
printf函数的调用是否正确,并确保参数和返回类型匹配。它会检查所有变量和函数的声明和定义。生成中间代码:
编译器接着生成中间代码或抽象语法树,这些中间表示形式是后续优化和机器码生成的基础。
代码优化:
编译器可能会进行一些优化,例如删除多余的指令,简化表达式,或在
printf的调用中优化字符串的处理方式。生成目标代码:
最后,编译器将中间代码转化为目标代码(机器码),并生成可执行文件。
执行阶段#
程序入口
main():执行从
main函数开始。调用
printf函数:printf("Hello, World! \n");会将字符串Hello, World!输出到标准输出(通常是屏幕)。printf函数会将格式化字符串参数发送给操作系统,以显示到控制台。返回值
return 0;:return 0;表示程序成功执行,返回代码0通常表示程序正常退出,任何非零值通常表示异常退出。
总结#
- 包含头文件:
#include <stdio.h>表示引入标准输入输出库的头文件,其中包含了printf函数的声明。 - 注释:
// 我的第一个 C 语言程序是一个单行注释,用于提供对程序的简要描述。 - main 函数:
int main()是程序的入口点。 - printf 函数:
printf("Hello, World! \n");是一个输出语句,它将字符串 “Hello, World!” 和一个换行符打印到标准输出设备(通常是控制台)。 - 返回值:
return 0;表示main函数成功执行并正常结束,返回值为 0。在 C 语言中,返回值为 0 通常表示程序成功执行。
通用概念在 C 的体现#
为了方便理解和演示,我会以:
注释 \(\to\) 输入/输出 \(\to\) 变量 \(\to\) 数据类型 \(\to\) 操作符 \(\to\) 控制结构 \(\to\) 函数 \(\to\) 模块化 \(\to\) 作用域 \(\to\) 错误处理
的顺序进行讲解
注释#
注释是在所有计算机语言中都非常重要的一个概念,可以用来解释某一段程序或者某一行代码是什么意思,方便程序员之间的交流沟通。
注释的类型有单行注释和多行注释
1. 单行注释#
- 单行注释前加 //
// 单行注释
2. 多行注释#
- 多行注释用 /**/ 包裹起来
/*
xxxxxx
多行注释
xxxxxx
*/
3. 嵌套注释#
- 注释可以嵌套,如下:
#include <stdio.h>
// 我的第一个 C 程序。 // 这是一个嵌套的单行注释 /* 这是一个嵌套的多行注释 */
/*
main 函数是 C 语言程序的入口点,程序从这里开始执行。
int main() 函数的返回值是 int 类型,一般情况下,返回 0 表示程序执行成功,非 0 表示程序执行失败。
// 这是一个嵌套的单行注释
*/
int main()
{
printf("Hello, world!\n"); // printf() 函数用于输出到屏幕
return 0;
}
注意:多行注释不能嵌套多行注释
输入/输出#
输入/输出是指程序与外部世界(如用户、文件、网络等)进行交互的过程。
1. 工具箱 stdio.h#
在 C 语言中,输入和输出(I/O)操作是程序与外部世界(如用户、文件、网络等)进行交互的基本方式。标准输入输出(Standard input and output)库 stdio.h 提供了多种函数来实现这些操作。

这个文件在 /usr/include 目录下
2. 输出工具 printf#
printf (format)函数用于格式化输出到标准输出(屏幕)。
用法:printf(format_string, arg1, arg2,..., argn)
- format_string:格式化字符串,包含了输出内容的格式和顺序。
- arg1, arg2,…, argn:格式化参数,用于填充格式字符串中的占位符。
例如:printf("Hello, %s!\n", "world");
3. 输入工具 scanf#
scanf (format)函数用于格式化从标准输入(键盘)读取输入。
用法:scanf(format_string, arg1, arg2,..., argn)
- format_string:格式化字符串,包含了输入内容的格式和顺序。
- arg1, arg2,…, argn:格式化参数,用于填充格式字符串中的占位符。
例如:scanf("%d %s", &num, str);
4. 示例:#
#include <stdio.h>
int main()
{
// 定义一个整型变量 num 和一个字符数组 str,用于存储字符串,最大长度为100
int num;
char str[100];
printf("Enter a number: ");
scanf("%d", &num); // 读取一个整数
printf("Enter a string: ");
scanf("%s", str); // 读取一个字符串
printf("You entered: %d and %s\n", num, str); // 输出结果
return 0;
}
变量#
变量是程序中用于存储数据的内存位置。
1. 基本数据类型#
| 数据类型 | 描述 | 示例值 |
|---|---|---|
int | 整型变量 | 25, -100, 0 |
float | 浮点型变量 | 5.9, -3.14, 0.0 |
char | 字符型变量 | 'A', 'b', '9' |
2. 定义变量#
- 变量的声明:
数据类型变量名; - 变量的初始化:
数据类型变量名=初始值;
3. 变量的命名规则#
- 变量名只能包含字母、数字和下划线。
- 变量名的第一个字符不能是数字。
- 变量名的大小写敏感。
- 变量名不能与关键字冲突。
- 变量名的长度不能超过 31 个字符。
int num; // 声明一个整型变量 num
float pi = 3.14; // 声明一个浮点型变量 pi,并初始化为 3.14
char ch = 'a'; // 声明一个字符型变量 ch,并初始化为 'a'
C 中的关键字: auto break case char const continue …
4. 示例#
#include <stdio.h>
int main()
{
// 整型变量
int a = 10;
int b = 20;
int c = a + b;
printf("The sum of %d and %d is %d\n", a, b, c);
// 浮点型变量
float d = 3.14;
printf("The value of pi is %f\n", d);
// 字符型变量
char e = 'A';
printf("The ASCII value of %c is %d\n", e, e);
return 0;
}
5. 奇怪的 %#
%d 、%f 和 %c 这三个格式化符号,它们的意义和作用都很奇怪。
%d:打印整数。%f:打印浮点数。%c:打印字符。
数据类型#
数据类型是指变量所存储的数据的类型。
1. 基本数据类型补充#
| 类型 | 存储大小 | 取值范围 |
|---|---|---|
int | 2 或 4 字节 | -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 |
float | 4 字节 | 1.2E-38 到 3.4E+38 |
char | 1 字节 | -128 到 127 或 0 到 255 |
我们可以通过 sizeof() 函数来获取数据类型的存储大小。
#include <stdio.h>
#include <limits.h>
int main()
{
printf("int 存储大小 : %lu \n", sizeof(int));
return 0;
}
除此之外还有:
整数类型
| 类型 | 存储大小 | 值范围 |
|---|---|---|
unsigned int | 2 或 4 字节 | 0 到 65,535 或 0 到 4,294,967,295 |
unsigned char | 1 字节 | 0 到 255 |
short | 2 字节 | -32,768 到 32,767 |
unsigned short | 2 字节 | 0 到 65,535 |
long | 4 字节 | -2,147,483,648 到 2,147,483,647 |
unsigned long | 4 字节 | 0 到 4,294,967,295 |
浮点类型
| 类型 | 存储大小 | 值范围 |
|---|---|---|
double | 8 字节 | 2.2E-308 到 1.8E+308 |
#include <stdio.h>
#include <float.h>
int main()
{
printf("float 存储最大字节数 : %lu \n", sizeof(float));
printf("float 最小值: %E\n", FLT_MIN );
printf("float 最大值: %E\n", FLT_MAX );
printf("精度值: %d\n", FLT_DIG );
return 0;
}
输出
float 存储最大字节数 : 4
float 最小值: 1.175494E-38
float 最大值: 3.402823E+38
精度值: 6
%E 格式化符号用于输出浮点数,其中 E 表示指数形式。
除了刚才讲的基本数据类型(整型、浮点型、字符型)外还有:
- 指针类型:
void *、int *、float *、char * - 结构体类型:
struct - 枚举类型:
enum
操作符#
操作符是用来执行特定操作的符号。
1. 算术运算符#
其中算术运算符有:+、-、*、/、%
举例:
int a = 20;
int b = 10;
int c = a + b; // 加法运算
int d = a - b; // 减法运算
int e = a * b; // 乘法运算
int f = a / b; // 除法运算
int g = a % b; // 取模运算
输出:
输入两个整数 a 和 b: 3 2
a + b = 5
a - b = 1
a * b = 6
a / b = 1
a % b = 1
2. 赋值运算符#
赋值运算符有:=、+=、-=、*=、/=、%=
举例:
int a = 20;
int b = 10;
a += b; // a = a + b
a -= b; // a = a - b
a *= b; // a = a * b
a /= b; // a = a / b
a %= b; // a = a % b
3. 关系运算符#
关系运算符有:==、!=、>、>=、<=
举例:
int a = 20;
int b = 10;
printf("%d == %d? %d\n", a, b, a == b); // 等于
printf("%d!= %d? %d\n", a, b, a!= b); // 不等于
printf("%d > %d? %d\n", a, b, a > b); // 大于
printf("%d >= %d? %d\n", a, b, a >= b); // 大于等于
printf("%d < %d? %d\n", a, b, a < b); // 小于
printf("%d <= %d? %d\n", a, b, a <= b); // 小于等于
4. 逻辑运算符#
逻辑运算符有:||、&&、!
和关系运算一样逻辑运算的结果也只有 0 或 1。
举例:
int a = 10;
int b = 20;
printf("%d", a > 0 && b < 30); // 输出 1
printf("%d", a > 0 || b > 30); // 输出 1
printf("%d",!(a > 10)); // 输出 0
控制结构#
控制结构是程序的执行流程的控制语句。
1. 条件语句:if#
条件语句是用来判断条件是否成立,并根据条件的成立与否执行不同的代码块。
条件语句的语法:
if (条件表达式)
{
// 条件表达式为 true 时执行的代码块
}
else
{
// 条件表达式都不成立时执行的代码块
}
示例:
#include <stdio.h>
int main()
{
int x = 10;
int y = 20;
if (x == y)
{
printf("%d is equal to %d\n", x, y);
}
else
{
printf("%d is not equal to %d\n", x, y);
}
return 0;
}
2. 循环语句:for#
循环语句是用来重复执行代码块的语句。
循环语句的语法:
for (初始化表达式; 条件表达式; 迭代表达式)
{
// 循环体
}

示例:
#include <stdio.h>
int main()
{
int i;
for (i = 0; i < 5; i++)
{
printf("%d\n", i);
}
return 0;
}
疑问:i++ 和 ++i 有什么区别?
答案:i++ 是后置递增,而 ++i 是前置递增。
示例:
#include <stdio.h>
int main() {
int i = 5;
int a, b;
// 后置递增 i++
a = i++; // 先将 i 的值(5)赋给 a,然后 i 增加 1(i 变为 6)
printf("After a = i++, i: %d, a: %d\n", i, a); // 输出:i: 6, a: 5
// 重置 i
i = 5;
// 前置递增 ++i
b = ++i; // 先将 i 增加 1(i 变为 6),然后将 i 的值(6)赋给 b
printf("After b = ++i, i: %d, b: %d\n", i, b); // 输出:i: 6, b: 6
return 0;
}
3. 跳转语句:goto#
跳转语句是用来改变程序执行流程的语句。
跳转语句的语法:
goto 标签名;
示例:
#include <stdio.h>
int main()
{
int i = 0;
while (i < 5)
{
i++;
if (i == 3)
{
goto end; // 跳转到 end 标签处
}
printf("%d\n", i);
}
end: // 定义 end 标签
printf("The end\n");
return 0;
}
函数#
函数是程序中用来执行特定功能的子程序。
1. 函数的声明#
返回类型 函数名 (参数类型 参数名) {函数体}
示例:
定义一个比大小的函数:
int compare(int a, int b)
{
if (a > b)
{
return 1;
}
else if (a < b)
{
return -1;
}
else
{
return 0;
}
}
2. 函数的调用#
调用方式:函数名 (参数1, 参数2, ...)
示例:
#include <stdio.h>
int compare(int a, int b)
{
if (a > b)
{
return 1;
}
else if (a < b)
{
return -1;
}
else
{
return 0;
}
}
int main()
{
int x = 10;
int y = 20;
int result = compare(x, y); // 调用函数
if (result == 1)
{
printf("%d is greater than %d\n", x, y);
}
else if (result == -1)
{
printf("%d is less than %d\n", x, y);
}
else
{
printf("%d is equal to %d\n", x, y);
}
return 0;
}
模块化#
模块化是指将一个大型程序分解为多个小模块,每个模块只完成特定的功能,然后再将这些模块组合起来组成一个完整的程序。
1. 头文件#
头文件是模块化的重要组成部分。头文件中包含了模块的接口(函数声明),模块的实现(函数定义)和全局变量声明。
头文件以 .h 结尾,包含了模块的接口。
示例: 新建 my_header_file.h 文件
// 声明一个求和的函数
int sum(int a, int b)
{
return a + b;
}
2. 示例:#
#include <stdio.h>
#include "my_header_file.h" // 包含头文件
int main()
{
int x = 10;
int y = 20;
int z = sum(x, y); // 调用函数求和
printf("The sum of %d and %d is %d\n", x, y, z);
return 0;
}
作用域#
作用域是程序中变量、函数、结构体等符号的可访问范围。
1. 简单说明#
- 定义在所有函数之外的变量,称为全局变量,可以被所有函数访问。
- 定义在函数内部的变量,称为局部变量,只能在函数内部访问。
以下是全局作用域和局部作用域的区别
| 作用域 | 变量类型 | 访问范围 |
|---|---|---|
| 全局作用域 | 全局变量 | 在程序的任何位置都可以访问 |
| 全局函数 | 在程序的任何位置都可以调用 | |
| 局部作用域 | 局部变量 | 只能在定义它的函数体内访问 |
| 局部函数 | 只能在定义它的函数体内调用 |
示例:
#include <stdio.h>
int main()
{
int x = 10; // 局部变量
int y = 20; // 局部变量
{
int x = 30; // 局部变量
int y = 40; // 局部变量
printf("x = %d, y = %d\n", x, y);
}
printf("x = %d, y = %d\n", x, y);
return 0;
}
2. 还有高手#
当局部变量被定义时,系统不会对其初始化 ,您必须自行对其初始化。定义全局变量时,系统会自动对其初始化 ,如下所示:
| 变量类型 | 初始值 |
|---|---|
| int | 0 |
| float | 0.0 |
| char | ‘\0’ |
错误处理#
错误处理是指程序在运行过程中出现错误时,如何处理错误。
错误类型:
错误类型分为:语法错误、逻辑错误、运行时错误。
错误处理机制:
- 语法错误:编译器在编译时就发现错误,并报告错误信息。
- 逻辑错误:运行时发现错误,程序会终止运行。
- 运行时错误:运行时出现错误,程序会终止运行。
示例:
#include <stdio.h> int main() { int x = 10; int y = 0; int z = x / y; // 运行时错误,程序会终止运行 printf("The result is %d\n", z); return 0; }
总结#
本文主要介绍了 C 语言的一些基本概念,包括注释、输入/输出、变量、数据类型、操作符、控制结构、函数、模块化、作用域、错误处理等。
剩下的进阶内容,如指针、结构体、枚举、数组、指针运算、字符串、文件操作等,将在后续的文章中介绍。
