1程序与程序设计
1.1 程序设计的一般步骤
- 分析问题、确定需求:明确输入是什么、要输出什么、有哪些约束。
- 设计算法:规划解题的方法和步骤(先想清楚再写代码!)。
- 编写代码(编码):用程序设计语言把算法表达出来。
- 调试程序:发现并修正语法错误和逻辑错误,让程序跑通。
- 测试与维护:用多组数据验证正确性与边界情况,后续按需修改完善。
1.2 著名的程序公式
瑞士计算机科学家沃斯(Niklaus Wirth)提出:
程序 = 数据结构 + 算法
数据结构描述数据如何组织,算法描述对数据如何操作,二者构成程序的核心。这也解释了为什么本课程后续要专门学习数据结构。
代码风格同样重要:见名知义的命名、适当的注释、缩进整齐、函数短小,程序不仅是写给机器执行的,更是写给人阅读和维护的。
2算法基础
2.1 算法的五个基本特性
- 有穷性:必须在执行有限步骤之后结束,且每步都在有限时间内完成。
- 确定性:每条指令含义明确,不能有二义性;相同输入必有相同结果。
- 可行性(有效性):每一个操作都可以由已经实现的基本运算执行有限次实现。
- 输入:有零个或多个输入(零输入时算法内部自带初始数据)。
- 输出:有一个或多个输出——没有输出的算法毫无意义。
2.2 算法的描述方法
自然语言与伪代码
用中文或类语言描述步骤,通俗易懂;伪代码介于自然语言与真实代码之间,结构紧凑、无语法负担,是设计算法时最常用的草稿形式。
流程图
用标准图形符号表示控制流:起止框(圆角矩形)、处理框(矩形)、判断框(菱形)、输入输出框(平行四边形)、流程线(箭头)。
以"求两个数的最大值"为例的伪代码:
算法 Max(a, b)
输入: 两个数 a, b
输出: 较大者
if a > b then
return a
else
return b
2.3 算法的评价
- 正确性:对合法输入能得到正确结果(最基本要求)。
- 可读性:易于理解,便于交流和维护。
- 健壮性(鲁棒性):输入非法数据时能妥善处理而不崩溃。
- 高效率与低存储:运行时间短、占用内存少,用时间复杂度与空间复杂度量化。
时间复杂度表示算法运行时间随问题规模 n 的增长趋势,用大 O 记号表示,常见量级(从优到劣):
O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ) < O(n!)。
例如单层循环是 O(n),两层嵌套循环通常是 O(n²)。
3程序的三种基本控制结构
1966 年 Bohm 和 Jacopini 证明:任何复杂的程序都可以由顺序、选择、循环三种基本结构组合嵌套而成。 它们都只有一个入口和一个出口,是结构化程序设计的基石。
3.1 顺序结构
最基本的结构:语句按书写顺序自上而下依次执行,每条语句只执行一次。
// 计算矩形面积
length = 5; // 第 1 步:输入长
width = 3; // 第 2 步:输入宽
area = length * width; // 第 3 步:计算
print(area); // 第 4 步:输出 15
3.2 选择结构(分支结构)
根据条件判断的真假选择执行不同语句。分为单分支、双分支和多分支:
if (score >= 60) {
print("及格");
} else if (score >= 0) {
print("不及格");
} else {
print("成绩无效");
}
多分支还可用 switch / case 语句处理一个表达式取多个离散值的情况。注意条件判断中的相等比较是 ==(C/Java)或 =(Python),不要与赋值号混淆。
3.3 循环结构(重复结构)
在条件满足时反复执行某段语句(循环体),常见两种形式:
- 当型循环 while:先判断条件,真则执行循环体(可能一次也不执行)。
- 直到型循环 do-while:先执行循环体再判断(至少执行一次)。
- for 循环:适合循环次数已知的场景,把初始化、条件、增量写在一起。
// 求 1+2+...+100,结果应为 5050
sum = 0;
for (i = 1; i <= 100; i++) {
sum = sum + i;
}
print(sum);
编写循环务必保证条件最终能变为假,否则会造成死循环(如忘了更新循环变量);循环体内可使用 break 提前跳出、continue 跳过本次剩余语句进入下一次判断。
4数据类型、常量与变量
4.1 数据的表示
计算机内部所有数据都以二进制存储。数值型数据有原码、反码、补码等表示(计算机中有符号整数普遍采用补码,可化减法为加法); 字符则通过编码标准映射为数字:
- ASCII 码:美国标准信息交换码,用 7 位表示 128 个英文字符;数字 '0' 是 48,大写 'A' 是 65,小写 'a' 是 97。
- 汉字编码:GB2312、GBK、GB18030;Unicode 致力于收录全球所有文字,UTF-8 是其最常用的变长编码实现。
4.2 基本数据类型
| 大类 | 常见类型 | 示例 |
|---|---|---|
| 整型 | int、short、long | 0、-7、2026 |
| 实型(浮点型) | float 单精度、double 双精度 | 3.14、-0.5(浮点数有精度误差,金额计算需谨慎) |
| 字符型 | char | 'A'、'9'、'\n'('\n' 换行等转义字符) |
| 布尔型 | bool | true、false |
此外还有构造类型:数组、字符串、结构体/记录、枚举,以及指针、列表、字典等语言扩展类型。
4.3 常量与变量
- 常量:程序运行期间值不能改变,如圆周率 π、整数 100;用 const 修饰或全大写命名。
- 变量:内存中一块命名的存储单元,其值可以改变;变量具有名字、类型和值三要素,使用前通常要"先定义后使用"。
const PI = 3.14159; // 常量
int age = 18; // 整型变量
age = age + 1; // 变量的值可以修改 → 19
强类型语言(C、Java)变量必须声明类型,编译期检查;弱类型/动态类型语言(Python、JavaScript)变量类型可随值改变,书写灵活但大型项目更依赖测试与规范。
5运算符与表达式
运算符规定对数据进行的操作,操作数通过运算符连接成表达式,表达式按优先级和结合性计算。
| 类别 | 常见运算符 | 示例 / 说明 |
|---|---|---|
| 算术运算符 | + - * / %(取余) | 7 % 3 = 1;整数相除可能取整(C 中 7/2=3) |
| 关系运算符 | > < >= <= == != | 结果为布尔值,如 5 > 3 → true |
| 逻辑运算符 | &&(与) ||(或) !(非) | 组合条件:age>=18 && hasID |
| 赋值运算符 | = += -= *= /= | x += 5 等价于 x = x + 5 |
| 自增自减 | ++ -- | i++ 使 i 增 1 |
| 条件运算符 | ? : | max = a>b ? a : b;(三目运算) |
| 位运算符 | & | ^ ~ << >> | 直接对二进制位操作,常用于底层与性能优化 |
优先级大致顺序:括号最优先 → 算术(先乘除后加减)→ 关系 → 逻辑(与高于或)→ 赋值。 实际编程中建议用括号显式表达意图,避免死记优先级。
if ( (x > 0) && (x < 100) ) {
print("x 是 1~99 之间的数");
}
6函数与模块化程序设计
// 定义:求两数之和
function add(a, b) {
return a + b;
}
// 调用
result = add(3, 5); // → 8
6.1 为什么需要函数
- 消除重复:同一段逻辑写一次、调用多次(DRY 原则:Don't Repeat Yourself)。
- 分解复杂问题:大程序拆成一个个高内聚、低耦合的小函数,便于分工与理解。
- 复用与共享:函数可放入库中供多个程序使用(如数学函数 sqrt()、print())。
- 信息隐藏:调用者只需知道"传什么、返回什么",不必关心内部实现。
6.2 参数传递与作用域
- 形参与实参:定义时括号中的是形式参数,调用时实际传入的是实际参数。
- 局部变量:函数内部定义,只在函数内有效;全局变量:函数外定义,整个文件可用。应尽量少用全局变量以减少相互影响。
- 递归:函数在定义中直接或间接调用自身。递归必须有明确的终止条件(出口)并不断向出口逼近,如阶乘 n! = n × (n-1)!。
6.3 结构化程序设计思想
20 世纪 60 年代为解决"软件危机",Dijkstra 等人提出结构化程序设计:自顶向下、逐步求精、模块化, 只用三种基本结构、限制滥用 goto 语句,使程序层次清晰、易读易验证。
7面向对象程序设计
随着软件规模增大,面向对象程序设计(OOP)成为主流范式。它把现实世界中的事物抽象为对象, 用"对象 + 消息"来组织程序,比面向过程更贴近人的思维方式。代表语言有 C++、Java、C#、Python 等。
7.1 类与对象
- 类(Class):对一类事物共同特征的抽象模板,包含属性(数据,描述状态)和方法(函数,描述行为)。
- 对象(Object):类的具体实例。类与对象的关系如同"图纸与房子""学生类与张三同学"。
class Dog {
String name; // 属性
void bark() { // 方法
print(name + ": 汪汪!");
}
}
Dog d = new Dog(); // 由类创建对象(实例化)
d.name = "旺财";
d.bark();
7.2 面向对象的三大特性
| 特性 | 含义 | 意义 |
|---|---|---|
| 封装 | 把数据和操作数据的方法捆绑在对象中,对外隐藏内部细节,只留必要接口(用 private/public 等访问控制) | 降低耦合、保护数据、提高安全性 |
| 继承 | 子类自动获得父类的属性和方法,并可扩展或重写(如"狗"继承"动物") | 代码复用、建立类的层次体系 |
| 多态 | 同一消息发给不同对象可产生不同行为(方法重写、重载、接口实现) | 程序更灵活、可扩展 |
面向过程
以"函数/过程"为中心,关注"第一步做什么、第二步做什么",把数据与处理数据的函数分开。适合小规模、流程固定的问题。代表:C。
面向对象
以"对象"为中心,关注"有哪些事物、各自有什么状态和行为、如何协作"。适合大型复杂系统。代表:Java、C++、Python。
8程序的翻译、执行与开发环境
8.1 从源代码到运行
高级语言源程序不能被 CPU 直接执行,必须经过翻译:
- 编译方式(C/C++):源代码 → 编译 → 目标文件 → 链接库 → 可执行文件(.exe),运行快、与平台相关。
- 解释方式(Python/JS):解释器读取源代码边翻译边执行,开发调试方便、跨平台,速度较慢。
- 虚拟机方式(Java/C#):先编译成字节码,再由虚拟机 JVM/CLR 在目标平台上执行。
8.2 集成开发环境(IDE)
IDE 把编辑器、编译器/解释器、调试器、项目管理等集成在一起,大幅提高开发效率。常见有: Visual Studio、IntelliJ IDEA(Java)、PyCharm(Python)、Dev-C++/CLion(C/C++)、VS Code(轻量通用、靠插件扩展)。
8.3 程序错误的类型
| 错误类型 | 表现 | 解决 |
|---|---|---|
| 语法错误 | 违反语言规则(漏分号、括号不配对),编译/解释无法通过 | 根据报错的行号提示修改 |
| 运行时错误 | 语法正确但运行中出错:除以 0、数组越界、空指针、内存不足 | 查看异常信息定位 |
| 逻辑错误 | 程序能运行,但结果不对(算法或条件写反),最隐蔽 | 调试器单步跟踪、设置断点、观察变量、用测试用例验证 |
8.4 调试方法
常用手段:设置断点 → 单步执行 → 监视变量/表达式 → 查看调用栈;也可在关键位置打印中间结果。 调试的本质是"观察程序实际执行路径与预期的偏差",先复现、再缩小范围、最后定位根因。
9主流编程语言与选择
| 语言 | 诞生/特点 | 主要应用领域 |
|---|---|---|
| C | 1972 年,贴近硬件、高效灵活 | 操作系统、嵌入式、驱动、底层库 |
| C++ | C 的超集,增加面向对象与泛型 | 游戏引擎、高性能计算、浏览器、数据库内核 |
| Java | 1995 年,"一次编写到处运行",生态庞大、安全稳定 | 企业级后端、Android 开发、金融系统 |
| Python | 1991 年,语法简洁接近自然语言、库极其丰富 | 人工智能、数据分析、自动化脚本、Web 后端 |
| C# | 微软主导,与 .NET 平台结合紧密 | Windows 应用、Unity 游戏、企业开发 |
| JavaScript | 网页交互的唯一原生脚本语言,借助 Node.js 也可做后端 | 前端、全栈 Web、浏览器插件 |
| Go / Rust / Kotlin / Swift | 较新语言:Go 并发简洁;Rust 内存安全;Kotlin(安卓)、Swift(苹果)为移动开发首选 | 云服务、系统编程、移动开发 |
初学者语言选择建议:Python 语法最简单、成就感来得最快,适合培养编程思维;学校教学常以 C 语言打基础,帮助理解内存与底层原理。语言是相通的,扎实掌握一门后再学其他语言会非常快——重要的是计算思维,而非语法本身。
本章小结:程序 = 数据结构 + 算法;算法须满足有穷、确定、可行、有输入输出;任何程序都由顺序、选择、循环三种结构构成;数据按类型组织、以变量承载、用运算符组成表达式;函数实现模块化分解,面向对象用封装、继承、多态管理复杂性;源代码经编译或解释执行,配合 IDE 与调试技术保证正确。下一章学习数据如何被更精巧地组织——数据结构。