第一章 程序设计和C语言
1.1 什么是计算机程序
程序的定义:程序是一组计算机能识别和执行的指令,每条指令对应一个特定的操作。
1.2 什么是计算机语言
1、计算机语言发展三阶段及特点对比
维度 | 机器语言(低级语言) | 汇编语言(低级语言) | 高级语言 |
---|---|---|---|
表现形式 | 二进制代码(0/1组合) | 助记符(如ADD/SUB) | 自然语言+数学表达式(如PRINT*语句) |
硬件依赖性 | 完全依赖特定机器 | 强依赖特定机器 | 弱依赖,跨平台 |
执行效率 | 最高(直接执行) | 较高(一对一转换) | 相对较低(一对多转换) |
开发效率 | 极低(需手工穿孔) | 低(需记忆符号) | 高(贴近人类思维) |
可维护性 | 最差(难以阅读修改) | 较差(需专业知识) | 良好(结构清晰) |
转换程序 | 无(直接执行) | 汇编程序 | 编译程序 |
典型应用 | 早期计算机程序 | 硬件驱动、嵌入式系统 | 通用软件开发 |
2. 语言分类标准对比
分类维度 | 类型 | 特点 | 代表语言 |
---|---|---|---|
抽象级别 | 低级语言 | 直接操作硬件,执行效率高,开发效率低 | 机器语言、汇编语言 |
高级语言 | 贴近人类语言,跨平台,开发效率高 | FORTRAN、C、Java | |
编程范式 | 结构化语言 | 基于过程,强调顺序/选择/循环结构 | C、FORTRAN 77 |
面向对象语言 | 基于对象,强调封装/继承/多态 | C++、Java、Python | |
执行方式 | 编译型语言 | 需编译为机器码后执行 | C、C++ |
解释型语言 | 逐行解释执行 | Python、JavaScript |
3. 程序设计方法演进
方法 | 核心思想 | 特点 | 代表语言 |
---|---|---|---|
非结构化编程 | 无严格流程控制 | 随意使用goto语句,程序可读性差 | 早期BASIC、FORTRAN |
结构化编程 | 三大基本结构控制流程 | 禁止goto,代码模块化,易于维护 | C、Pascal |
面向对象编程 | 以对象为基本单位 | 封装数据与操作,支持继承和多态,适合大规模程序 | C++、Java |
4. 关键转换程序对比
转换程序 | 输入语言 | 输出形式 | 转换方式 | 示例 |
---|---|---|---|---|
汇编程序 | 汇编语言 | 机器指令 | 一对一转换 | ADD A,B → 1011011000000000 |
编译程序 | 高级语言(源程序) | 目标程序(机器码) | 整体编译后执行 | C语言编译为.exe文件 |
解释程序 | 高级语言(脚本语言) | 逐行翻译并执行 | 逐行解释不生成目标程序 | Python解释执行 |
5. 程序执行流程对比
流程类型 | 步骤 | 特点 |
---|---|---|
编译执行 | 源程序 → 编译 → 目标程序 → 执行 | 执行效率高,需提前编译,跨平台性差 |
解释执行 | 源程序 → 逐行解释并执行 | 灵活调试,跨平台性强,执行效率较低 |
6. 语言特性对比
特性 | 机器语言 | 汇编语言 | 高级语言 |
---|---|---|---|
执行效率 | 最高 | 较高 | 较低 |
开发效率 | 最低 | 低 | 最高 |
可移植性 | 无 | 无 | 高 |
学习成本 | 极高 | 高 | 低 |
1.3 C语言的发展和特点
1. 简洁性与灵活性:仅37个关键字、9种控制语句;程序结构自由,多用小写字母;内核精简,无直接I/O语句,依赖库函数实现功能。
2. 运算符丰富性:34种运算符,包含位运算、赋值、类型转换等,支持复杂运算(如自增/自减、逗号表达式)。
3. 数据类型多样性:整型、浮点型、指针、结构体、共用体等;C99新增布尔型、超长整型等;指针为核心,支持链表、栈等数据结构。
4. 结构化编程:支持if-else
、switch
、while
、for
等控制语句;以函数为模块单位,实现模块化设计。
5. 语法自由度大:不强制检查数组越界;变量类型灵活(如整型与字符型通用);依赖程序员保证正确性。
6. 底层操作能力:可直接访问物理地址、进行位操作,兼具高级语言与汇编特性,适用于系统软件开发。
7. 可移植性高:编译系统简洁,标准库可跨平台移植,源代码无需修改。
8. 高效性:生成的目标代码质量高,执行效率接近汇编语言。
1.4 最简单的C语言程序
知识点 | 详细解释 | 对应示例与代码片段 |
---|---|---|
1. 主函数结构 | - 定义: C程序必须包含main 函数作为入口,其类型为int ,表示返回整型值。- 返回值: return 0 表示程序正常结束,虽不强制检查但需符合规范。- 函数体: 由 {} 包裹,语句以; 结尾。 | 例1.1int main() { printf("This is a C program.\n"); return 0; } |
2. 预处理指令 | - 作用: 在编译前插入头文件内容(如stdio.h ),提供库函数支持。- 语法: #include <文件名> ,需置于程序开头。- 依赖关系: 若未包含 stdio.h ,printf 和scanf 将无法使用。 | 例1.1~1.3#include <stdio.h> |
3. printf函数 | - 功能: 格式化输出,支持占位符(如%d )和转义符(如\n )。- 参数结构: printf("格式字符串", 变量) 。- 占位符替换: 如 %d 被变量值替换,\n 换行。 | 例1.1printf("This is a C program.\n"); 例1.2 printf("sum is %d\n", sum); |
4. scanf函数 | - 功能: 格式化输入,需用& 获取变量地址。- 语法: scanf("格式符", &变量) ,如%d 对应整型。- 注意事项: 输入格式需与代码匹配(例1.3中 %d,%d 要求逗号分隔输入)。 | 例1.3scanf("%d,%d", &a, &b); |
5. 变量与赋值 | - 声明: 需指定类型(如int a, b, sum; )。- 赋值: 通过 = 运算符(如a=123; )。- 运算: 支持算术表达式(如 sum=a+b; )。 | 例1.2int a, b, sum; a=123; b=456; sum=a+b; |
6. 函数定义与调用 | - 定义语法: 返回类型 函数名(形参列表) { ... } 。- 调用过程: 实参传递给形参(值传递),执行函数体后返回结果。 - 前置声明: 若函数定义在调用后,需提前声明(如 int max(int x, int y); )。 | 例1.3int max(int x, int y) { ... } c = max(a, b); |
7. 控制结构 | - if-else逻辑: 根据条件选择执行分支,条件为真执行if 块,否则执行else 块。- 返回值控制: 函数内通过 return 返回结果(如return z; )。 | 例1.3if (x>y) z=x; else z=y; return(z); |
8. 注释语法 | - 单行注释: // (C99标准支持)。- 多行注释: /* ... */ (C89标准兼容)。- 特殊案例: 注释中的符号(如 // 或/* */ )若在字符串内则原样输出。 | 例1.1注释//这是编译预处理指令 例1.3注释 /*若x>y成立,将x的值赋给z*/ |
9. 编译器特性 | - 编译顺序: 自顶向下编译,函数调用前需声明。 - 返回值处理: main 函数的return 0 可被操作系统捕获,非必须但推荐保留。 | 例1.3分析 因 max 定义在main 之后,需前置声明int max(int x, int y); ,否则编译报错。 |
10. 输入输出细节 | - 格式控制: printf 中%d 对应整型,scanf 中%d 严格匹配输入类型。- 换行符影响: \n 控制输出换行,无\n 则后续输出紧接当前行。 | 例1.1输出This is a C program. 后换行。例1.2输出 sum is 579 后换行。 |
知识点分类 | 详细解释 | 对应代码示例/教材引用 |
---|---|---|
1. 源程序文件组成 | 一个C程序由一个或多个源程序文件组成,每个文件包含: ① 预处理指令(如 #include )② 全局声明(函数外的变量声明) ③ 函数定义(如 main 和其他函数) | 例1.3:#include <stdio.h> (预处理)int max(int x, int y); (全局声明)int main() { ... } (函数定义) |
2. 全局与局部变量 | - 全局变量:在函数外声明,整个源文件有效。 - 局部变量:在函数内声明,仅函数内有效。 - 未使用全局变量的示例见第7章 | 例1.2局部变量:int a, b, sum; (声明在main 函数内) |
3. 函数的作用 | - C程序由函数构成,main 函数为唯一入口。- 函数分为库函数(如 printf )和用户自定义函数(如max )。- 模块化设计通过多源文件实现,便于编译调试。 | 例1.3模块化:main 调用max 函数,函数定义在同一源文件。 |
4. 函数结构 | 函数包含两部分: ① 函数首部: 返回类型 函数名(参数类型 参数名) ② 函数体: - 声明部分(变量/函数声明) - 执行部分(操作语句) | 例1.3函数定义:int max(int x, int y) { ... } 函数体内含 if-else 逻辑和return |
5. 程序执行顺序 | 程序始终从main 函数开始执行,无论其在文件中的位置(如可置于文件末尾)。 | 例1.1~1.3均以main 函数为起点。 |
6. 语句与分号规则 | - 操作由C语句实现(如赋值、I/O)。 - 分号必要性:每条语句必须以分号结尾。 - 书写自由性:允许一行多语句或一语句多行,但需保证可读性。 | 例1.2语句:sum = a + b; (分号结尾) |
7. 输入输出实现 | C语言通过库函数(如scanf /printf )实现I/O,不直接提供I/O语句,增强可移植性。 | 例1.1输出:printf("This is a C program.\n"); |
8. 注释的重要性 | 注释(// 或/* */ )用于提高代码可读性,推荐在关键逻辑处添加。 | 教材说明://这是编译预处理指令 (例1.1注释) |
1.5 运行C语言程序的步骤和方法
流程阶段 | 详细步骤与工具 | 输入/输出文件 |
---|---|---|
1. 编辑源程序 | - 操作:使用IDE(如VS2010)或文本编辑器编写代码,保存为.c 文件。- 工具:Visual Studio、Code::Blocks | 输入:键盘输入的代码 输出: f.c 源文件 |
2. 编译预处理 | - 操作:预处理器执行#include 指令,替换头文件内容。- 工具:预编译器(如 cpp ) | 输入:f.c 中间文件:预处理后的临时文件 |
3. 正式编译 | - 操作:编译器检查语法错误,生成目标文件(二进制代码)。 - 工具:GCC、Clang、MSVC | 输入:预处理后的文件 输出: f.obj (Windows)或f.o (Linux) |
4. 连接处理 | - 操作:链接器合并目标文件与库函数,生成可执行程序。 - 工具:Linker(如 ld 或VS的链接器) | 输入:f.obj + 库文件输出: f.exe (Windows)或a.out (Linux) |
5. 运行程序 | - 操作:执行可执行文件,验证输出结果。 - 工具:操作系统命令行或IDE内置终端 | 输入:f.exe 输出:控制台显示结果 |
第二章 算法——程序的灵魂
略
第三章 最简单的C程序设计——顺序程序设计
3.2 数据的表现形式以及运算
知识点分类 | 详细解释与代码示例 | 注意事项与扩展分析 |
---|---|---|
1. 变量定义与初始化 | 例3.1:float f = 64.0, c; 例3.2: float p0=1000, r1=0.0036; - 作用:声明变量类型并赋初值,明确数据存储形式。 | - 精度警告:VC++会将float 视为双精度,可能提示精度损失,但不影响结果(GCC无此警告)。- 建议:需高精度时使用 double 。 |
2. 算术表达式计算 | 例3.1公式:c = (5.0/9) * (f - 32); 例3.2公式: p3 = p0*(1 + r3/2)^2 - 关键:运算符优先级与类型转换(如 5.0/9 避免整除误差)。 | - 浮点数陷阱:若写成5/9 结果为0,需显式使用浮点常量(如5.0 )。- 幂运算需手动展开(C语言无 ^ 运算符)。 |
3. 输入输出控制 | 例3.1输出:printf("f=%f, c=%f\n", f, c); 例3.2输出: printf("p1=%f\np2=%f\np3=%f\n", p1, p2, p3); - 格式符: %f 默认保留6位小数。 | - 换行符:\n 控制输出格式,增强可读性。- 扩展: %.2f 可限制小数位(如17.78 )。 |
4. 顺序结构特性 | - 执行流程:按代码书写顺序逐行执行,无分支或循环。 - 适用场景:公式计算、数据转换等线性问题。 | - 调试要点:可通过插入printf 中间变量验证计算步骤(如检查f-32 结果)。 |
5. 程序调试与警告 | VC++警告:双精度常量赋值给float 变量时提示精度损失。GCC无警告:默认兼容性更高。 | - 解决方案:显式类型转换(如float f = 64.0f; )或改用double 。- 实践建议:关注警告信息,避免隐式错误。 |
3.2.1常量和变量
一、常量与变量
分类 | 定义与特点 | 示例与注意事项 |
---|---|---|
常量 | 程序运行中不可改变的值,分为字面常量(直接量)与符号常量(宏定义)。 | - 字面常量:5 , 3.14 , 'A' , "Hello" - 符号常量: #define PI 3.1415 (预编译替换,无类型、不占内存) |
变量 | 程序运行中可改变的值,需声明类型并分配存储空间。 | - int a = 10; - float price = 99.9; 注意:变量名需符合标识符规则,区分大小写。 |
二、常量类型详解
类型 | 表示形式与规则 | 示例与常见错误 |
---|---|---|
整型常量 | 十进制整数(正负均可),无小数点。 | 123 , -456 错误: 12,345 (逗号非法) |
实型常量 | 1. 小数形式:必须含小数点(整数部分或小数部分可为0) 2. 指数形式: aEn (a为基数,n为整数指数) | 1. 123.45 , 0.78 2. 1.23e5 (=123000)错误: e5 , 12e3.5 (指数必须为整数) |
字符常量 | 单引号括起的单个字符,存储ASCII码值。 | 'A' (ASCII 65)错误: 'AB' (多字符非法) |
字符串常量 | 双引号括起的字符序列,末尾自动添加\0 表示结束。 | "Hello" (存储为H ,e ,l ,l ,o ,\0 )注意: "A" 与'A' 占用内存不同(字符串多\0 ) |
符号常量 | 使用#define 定义,预编译时替换为字面值。 | #define MAX 100 优点:一改全改、见名知义;缺点:无类型检查,不分配存储空间。 |
三、常变量与符号常量对比
特性 | 符号常量(#define) | 常变量(const) |
---|---|---|
定义方式 | 预编译指令,无分号 | 变量声明,需指定类型和const 关键字 |
存储空间 | 不占用内存,仅文本替换 | 占用内存,具有固定地址 |
类型检查 | 无类型,替换后可能引发类型错误 | 有明确类型(如const float pi=3.1415; ) |
作用域 | 从定义处到文件结束,或使用#undef 取消 | 遵循变量作用域规则(如局部、全局) |
兼容性 | 所有C编译器支持 | C99及以上支持,旧编译器可能不兼容 |
典型用例 | 全局常量(如数学常数、配置参数) | 函数内常量(如循环边界、临时计算值) |
四、标识符命名规则
规则 | 合法示例 | 非法示例 | 注意事项 |
---|---|---|---|
1. 由字母、数字、下划线组成 | sum , _total , Student1 | 3DPrint , user-name | 首字符必须为字母或下划线 |
2. 区分大小写 | sum ≠ Sum ≠ SUM | Class 与class 为不同变量 | 建议变量名全小写,常量名全大写 |
3. 不可使用关键字 | int , float 为关键字,不可用作标识符 | char , return | C语言共有32个关键字(如if , for ) |
4. 长度限制 | 一般支持至少31个有效字符 | 超长部分可能被截断(如this_is_a_very_long_variable_name ) | 建议简洁且见名知义(如total_score 优于ts ) |
5. 避免歧义性命名 | student_count , max_value | a1 , temp | 推荐使用小写+下划线(蛇形命名法)或驼峰命名法(如studentName ) |
3.2.2 数据类型
一、C语言数据类型分类与特点
大类 | 子类与典型类型 | 核心特点 |
---|---|---|
基本类型 | - 整型:char , short , int , long , long long - 浮点型: float , double - 布尔型: _Bool (C99) | - 整型:精确计算,无误差 - 浮点型:近似值,科学计算 - 布尔型:逻辑判断( true /false ) |
枚举类型 | enum | 用户自定义整数常量集合(如enum Week {Mon, Tue...}; ),增强代码可读性。 |
空类型 | void | 表示“无类型”,用于函数无返回值或通用指针(如void* )。 |
指针类型 | * (如int* , char* ) | 存储内存地址,支持动态内存管理与复杂数据结构(如链表、树)。 |
派生类型 | - 数组:int arr[5] - 结构体: struct - 共用体: union - 函数类型: int func() | - 数组:同类型数据集合 - 结构体:异构数据封装 - 共用体:内存共享(节省空间) - 函数类型:描述函数接口 |
二、数据类型的核心作用
作用 | 具体表现 | 示例与意义 |
---|---|---|
内存分配 | 类型决定变量占用的内存大小(如char 占1字节,double 占8字节)。 | 优化内存使用(如嵌入式系统中优先选择short 而非int )。 |
运算规则 | 类型决定支持的运算(如浮点型支持小数运算,指针支持地址运算)。 | 避免非法操作(如对void* 指针直接算术运算需显式转换)。 |
数据表示精度 | 浮点型精度有限(如float 仅6~7位有效数字),整型无误差但范围受限。 | 科学计算需用double ,财务计算可能需高精度库(如GMP)。 |
类型安全检查 | 编译器检查类型匹配(如int a = "hello"; 会报错)。 | 减少运行时错误(如误将字符串赋给整型变量)。 |
三、常见类型的内存占用(以Visual C++为例)
类型 | 字节数 | 取值范围/精度 | 典型用途 |
---|---|---|---|
char | 1 | -128~127 或 0~255 | ASCII字符、小型整数存储 |
int | 4 | -2,147,483,648 ~ 2,147,483,647 | 通用整数运算(循环计数、数组索引) |
float | 4 | 约6~7位有效数字,范围±3.4e38 | 3D图形坐标、一般科学计算 |
double | 8 | 约15~16位有效数字,范围±1.8e308 | 高精度计算(如数值分析、物理仿真) |
long long | 8 | -9×10¹⁸ ~ 9×10¹⁸ | 大整数存储(如时间戳、金融计算) |
void* | 4/8 | 地址空间(32位系统4字节,64位系统8字节) | 通用指针、动态内存管理 |
3.2.3 整型数据
一、整型分类与存储方式
类型分类 | 关键字形式 | 字节数(Visual C++) | 数值范围 | 存储方式与特性 |
---|---|---|---|---|
基本整型(int) | int / signed int | 4 | 有符号:-2,147,483,648 ~ 2,147,483,647 无符号:0 ~ 4,294,967,295 | 存储补码形式(正数原码=补码;负数补码=原码取反+1),最高位为符号位(0正1负)。 |
短整型(short) | short / signed short | 2 | 有符号:-32,768 ~ 32,767 无符号:0 ~ 65,535 | 与int 存储方式相同,但空间减半,适用于内存敏感场景。 |
长整型(long) | long / signed long | 4 | 有符号:-2,147,483,648 ~ 2,147,483,647 无符号:0 ~ 4,294,967,295 | 与int 在Visual C++中字节数相同,但部分系统可能分配8字节(如Linux 64位)。 |
双长整型(C99新增) | long long / signed long long | 8 | 有符号:-9×10¹⁸ ~ 9×10¹⁸ 无符号:0 ~ 1.8×10¹⁹ | 支持超大整数运算,适用于金融、时间戳等场景。补码存储规则与其他整型一致。 |
二、补码存储机制详解
数值示例 | 存储步骤 | 二进制表示(以2字节为例) |
---|---|---|
正数(+5) | 直接存储原码的二进制形式。 | 0000 0000 0000 0101 (符号位0,数值位5) |
负数(-5) | 1. 取绝对值原码(5的二进制) 2. 按位取反 3. 加1得到补码 | 原码:0000 0000 0000 0101 取反: 1111 1111 1111 1010 加1后补码: 1111 1111 1111 1011 |
符号位作用 | 最高位为符号位(0正1负),其余位为数值位。 | 示例:1xxx xxxx xxxx xxxx 表示负数,0xxx xxxx xxxx xxxx 表示正数。 |
三、有符号与无符号整型对比
对比维度 | 有符号整型(signed) | 无符号整型(unsigned) |
---|---|---|
符号位 | 最高位为符号位(0正1负) | 无符号位,全部位表示数值 |
数值范围(2字节) | -32,768 ~ 32,767 | 0 ~ 65,535 |
溢出行为 | 正溢出变为负数,负溢出变为正数(循环) | 溢出时从0重新开始(循环) |
典型用途 | 需要表示正负数的场景(如温度、坐标) | 仅需非负数的场景(如计数器、数组索引) |
3.2.4 字符型数据
一、字符型数据基础
分类 | 描述 | 示例与说明 |
---|---|---|
字符与ASCII代码 | 字符以整数形式(ASCII代码)存储,C99将其归类为整型的一种。 | - 'A' →65(二进制1000001 )- 'a' →97(二进制1100001 )- '1' →49(二进制0110001 ) |
ASCII字符集范围 | 基本集包含127个字符,用7位二进制表示,存储时占用1字节(8位),最高位固定为0。 | 所有ASCII字符的二进制范围为00000000 ~01111111 (0~127)。 |
字符分类 | 分为字母、数字、专用符号、空格符、不可显示字符(转义字符)。 | - 专用符号:如! 、# 、& 等29个符号- 转义字符:如 \n (换行,ASCII 10)、\0 (空字符,ASCII 0)。 |
二、字符存储与编码
存储特性 | 说明 | 示例(以1字节存储) |
---|---|---|
7位编码机制 | ASCII码仅需7位,存储时占用1字节(8位),最高位补0。 | 'a' 存储为01100001 (十进制97) |
字符与整数存储差异 | 字符以ASCII码存储(1字节),整数以补码存储(2/4字节)。 | - '1' →ASCII码49(二进制00110001 )- 整数1→补码 00000000 00000001 (2字节) |
运算差异 | 字符运算基于ASCII码值,而非实际数值。 | '1' + '1' = 49 + 49 = 98 (对应字符'b' ),而非整数2。 |
三、字符变量与操作
操作类型 | 语法与规则 | 示例与输出 |
---|---|---|
变量定义 | 使用char 关键字定义,可赋值为字符或0~127的整数。 | char c = '?'; (等价于char c = 63; ) |
输入输出 | 通过格式符%c (字符)或%d (十进制ASCII码)控制输出形式。 | printf("%d %c", c, c); →输出63 ? |
符号修饰 | 可添加signed 或unsigned 修饰符,影响取值范围。 | - signed char :-128~127- unsigned char :0~255 |
四、有符号与无符号字符型对比
对比项 | signed char | unsigned char |
---|---|---|
字节数 | 1字节 | 1字节 |
取值范围 | -128 ~ 127(-2⁷ ~ 2⁷-1) | 0 ~ 255(0 ~ 2⁸-1) |
典型用途 | 需要表示正负ASCII扩展字符(如某些系统字符) | 处理非负数据(如原始字节流、图像像素值) |
溢出行为 | 正溢出变为负数,负溢出变为正数(循环) | 溢出时从0重新开始(循环) |
3.2.5 浮点型数据
一、浮点型数据基础
分类 | 核心定义与特性 | 示例与说明 |
---|---|---|
浮点数名称由来 | 实数以指数形式存储,小数点位置可浮动(通过调整指数值保持数值不变)。 | 如 3.14159 可表示为 3.14159×10⁰ 或 0.0314159×10² ,通过指数调整实现小数点“浮动”。 |
浮点型分类 | 包括 float (单精度)、double (双精度)、long double (长双精度)。 | - float :4字节,6位有效数字- double :8字节,15位有效数字- long double :16字节(VC++中8字节),19位有效数字。 |
二、存储结构与精度
存储特性 | 说明 | 二进制存储示意图(替代原图3.11) |
---|---|---|
规范化二进制指数形式 | 浮点数分为小数部分(尾数)和指数部分: - 尾数:二进制小数,首位隐含为1(规范化) - 指数:2的幂次偏移表示(如IEEE754标准)。 | float 存储结构(32位):1位符号位 + 8位指数(偏移量127) + 23位尾数 示例: 3.14159 存储为符号位0 ,指数偏移10000000 ,尾数10010010000111111001111 。 |
精度与范围关系 | - 尾数位数决定有效数字(精度) - 指数位数决定数值范围。 | float :23位尾数→6位十进制精度double :52位尾数→15位十进制精度。 |
三、浮点型分类对比(Visual C++环境)
类型 | 字节数 | 有效数字 | 数值范围(绝对值) | 典型应用场景 |
---|---|---|---|---|
float | 4 | 6 | 1.2×10⁻³⁸ ~ 3.4×10³⁸ | 图形处理、低精度传感器数据 |
double | 8 | 15 | 2.3×10⁻³⁰⁸ ~ 1.7×10³⁰⁸ | 科学计算、金融建模、高精度算法 |
long double | 8(VC++) 16(Turbo C) | 19 | 3.4×10⁻⁴⁹³² ~ 1.1×10⁴⁹³² | 数学分析、高精度工程仿真(需注意编译器兼容性) |
四、关键特性与注意事项
特性 | 规则与风险 | 解决方案 |
---|---|---|
运算类型提升 | float 参与运算时自动提升为 double (精度更高)。 | 避免混合类型运算,如 float a = 0.1; double b = a; (隐式转换可能导致误差累积)。 |
精度丢失风险 | 二进制无法精确表示某些十进制小数(如 0.1 ),导致舍入误差。 | 使用高精度类型(double 或 long double ),或引入误差容忍机制(如 if(abs(a-b) < 1e-6) )。 |
极小值存储限制 | float 无法存储绝对值小于 1.2×10⁻³⁸ 的数(如 1e-40 )。 | 改用 double 或科学计数法表示(如 1e-40 存为 1.0×10⁻40 )。 |
编译器差异 | long double 字节数因编译器而异(VC++ 8字节,Turbo C 16字节)。 | 跨平台代码需显式声明类型长度(如使用 stdint.h 中的固定宽度类型)。 |
3.2.6 怎样确定常量的类型
略。
3.3 运算符和表达式
3.3.1 C运算符
一、运算符基础分类与功能概览
运算符类别 | 包含运算符 | 核心功能与运算逻辑 | 典型应用场景 |
---|---|---|---|
算术运算符 | + - * / % ++ -- | 数值计算(加减乘除、取余)、单目自增/自减操作 | int a = (b + c) * 2; i++; (循环计数器) |
关系运算符 | > < == >= <= != | 比较操作数大小或相等性,返回布尔值(0或1) | if (score >= 60) (条件判断) |
逻辑运算符 | `! && | ` | |
位运算符 | `<< >> ~ | ^ &` | 直接操作二进制位:移位、取反、位或、位异或、位与 |
赋值运算符 | `= += -= *= /= %= &= | = ^= <<= >>=` | 赋值与复合运算(先计算后赋值) |
二、特殊用途运算符深度解析
运算符类型 | 符号与语法 | 核心特性与规则 | 代码示例与说明 |
---|---|---|---|
条件运算符 | ?: | 三目运算:条件 ? 表达式1 : 表达式2 (条件为真取表达式1,否则取表达式2) | max = (a > b) ? a : b; (替代简单if-else) |
逗号运算符 | , | 从左到右依次执行表达式,最终结果为最右侧表达式的值 | int x = (a=3, b=5, a+b); → x=8(多操作串联) |
指针运算符 | * & | - * :解引用指针- & :取变量地址 | int *p = &a; *p = 10; (通过指针修改变量) |
成员访问运算符 | . -> | - . :结构体/联合体成员访问-> :通过指针访问结构体成员 | student.name <brptr->age (链表操作) |
下标运算符 | [] | 数组元素访问(等价于指针算术运算:arr[i] ≡ *(arr + i) ) | matrix[2][3] = 10; (二维数组操作) |
三、编译时与类型操作关键运算符
运算符类型 | 语法形式 | 功能与底层实现 | 应用场景与注意事项 |
---|---|---|---|
求字节数运算符 | sizeof | 编译时计算操作数所占内存字节数(非函数,返回值类型为size_t ) | malloc(10 * sizeof(int)); (动态内存分配)sizeof arr (数组总字节数) |
强制类型转换 | (type) | 显式转换数据类型(可能引发精度丢失或溢出) | double d = 3.14; int i = (int)d; → i=3(截断小数) |
函数调用运算符 | () | 执行函数调用(参数传递与返回值处理) | printf("Hello"); <brint result = func(a, b); (函数式编程基础) |
3.3.2 基本的算术运算符
一、运算符分类与基础特性
运算符 | 类型 | 功能描述 | 示例与运算规则 | 注意事项 |
---|---|---|---|---|
+ | 单目/双目运算符 | - 单目:取正值 - 双目:加法运算 | +5 → 53 + 2 → 5 | 单目运算符优先级高于双目运算符。 |
- | 单目/双目运算符 | - 单目:取负值 - 双目:减法运算 | -5 → -55 - 3 → 2 | 单目负号可用于常量或变量前(如 -a )。 |
* | 双目运算符 | 乘法运算 | 3 * 2 → 6 | 替代数学中的 × ,支持整数、浮点数混合运算。 |
/ | 双目运算符 | 除法运算(结果类型由操作数决定) | 5 / 2 → 2(整数除法)5.0 / 2 → 2.5(浮点除法) | - 整数除法向零取整(如 -5/3 → -1 )- 操作数含浮点数则结果为双精度。 |
% | 双目运算符 | 求余运算(仅限整数操作数) | 8 % 3 → 2-5 % 3 → -2(结果符号与被除数一致) | - 操作数必须为整数 - 结果满足 a = (a / b) * b + (a % b) 。 |
二、运算规则与底层实现
规则类型 | 详细说明 | 代码示例 | 底层机制 |
---|---|---|---|
整数除法截断规则 | 向零取整(Truncate Toward Zero) | 5 / 3 → 1 -5 / 3 → -1 (VC++、GCC等主流编译器) | 二进制除法指令直接截断小数部分(如 x86 IDIV 指令)。 |
浮点数除法精度规则 | 操作数含浮点数时自动提升为双精度计算 | 5.0 / 2 → 2.5 5 / 2.0 → 2.5 | 编译器隐式转换整数为浮点数(如 int→double ),使用浮点寄存器(如 x87 FPU 或 SSE)运算。 |
求余运算公式 | a % b = a - (a / b) * b (结果符号与 a 一致) | -7 % 3 → -1 (因 -7 = (-3)*3 + (-1) ) | 通过乘法和减法实现余数计算,依赖整数除法规则。 |
运算溢出处理 | 整数溢出行为未定义(Undefined Behavior) | int a = 2147483647 + 1; → 结果不可预测(32位 int 最大值溢出) | 编译器不检查溢出,可能产生环绕(如补码表示)或错误值。 |
类型隐式转换规则 | 运算前自动进行算术提升(Arithmetic Promotion): - char /short →int - float →double | char c = 10; short s = 20; c + s → int 类型结果 float f = 3.14; f + 2 → double 类型结果 | 编译器在生成中间代码时统一操作数类型,避免精度损失。 |
三、特殊场景与易错点分析
场景分类 | 典型错误示例 | 正确实现方式 | 原理与规避建议 |
---|---|---|---|
整数除法精度丢失 | int avg = (a + b) / 2; (若 a+b 为奇数,结果舍去小数) | 使用浮点运算:double avg = (a + b) / 2.0; | 显式引入浮点操作数触发类型提升,保留小数部分。 |
负数求余方向混淆 | -5 % 3 结果可能被误判为 1 (实际为 -2 或 -1 ,依赖编译器) | 明确规则:余数符号与被除数一致(-5 % 3 → -2 ,因 -5 = (-2)*3 + 1 ) | 避免依赖负数求余结果实现逻辑,优先使用非负操作数。 |
浮点比较误差 | if (0.1 + 0.2 == 0.3) → 条件为假 | 使用误差范围判断:if (fabs(0.1 + 0.2 - 0.3) < 1e-6) | 二进制浮点数无法精确表示某些十进制小数(如 0.1),累计误差导致比较失败。 |
混合类型运算陷阱 | int a = 5; double b = 2; int c = a / b; → 隐式截断为 2 (非四舍五入) | 显式四舍五入:int c = (int)(a / b + 0.5); | 浮点到整数的转换直接截断小数部分,需手动处理舍入。 |
表达式优先级误判 | a + b * c 被误读为 (a + b) * c | 明确优先级:乘法高于加法,等价于 a + (b * c) | 参考运算符优先级表,必要时使用括号强制顺序。 |
四、运算符优先级与结合性速查表
优先级 | 运算符 | 结合性 | 示例解析 |
---|---|---|---|
1 | + (单目) - (单目) | 右结合 | -3 + 4 → (-3) + 4 = 1 |
2 | * / % | 左结合 | 5 * 3 % 2 → (5 * 3) % 2 = 1 |
3 | + (双目) - (双目) | 左结合 | 5 - 3 + 2 → (5 - 3) + 2 = 4 |
规则说明 | 单目运算符优先级高于双目,乘除余高于加减。 | 复杂表达式建议使用括号明确顺序(如 (a + b) * (c - d) )。 |
3.3.3 自增(++)、自减(--)运算
一、运算符基本规则与语法
分类 | 语法形式 | 功能描述 | 示例与运算逻辑 |
---|---|---|---|
前置自增(++i) | ++变量名 | 先对变量值加1,再使用新值 |
|
后置自增(i++) | 变量名++ | 先使用变量原值,再对变量值加1 |
|
前置自减(--i) | --变量名 | 先对变量值减1,再使用新值 |
|
后置自减(i--) | 变量名-- | 先使用变量原值,再对变量值减1 |
|
3.3.4 运算符优先级与结合性
规则类型 | 详细说明 | 示例 |
---|---|---|
优先级 | 单目运算符,优先级高于双目运算符(如 * 、/ ),低于括号。 | int a = 3 * ++i; // 等价于 3 * (++i) |
结合性 | 右结合(从右向左计算) |
|
操作数限制 | 仅能作用于变量(左值),不可用于常量或表达式。 |
|
3.3.5 不同数据类型间的混合运算
一、混合运算类型转换规则
运算场景 | 转换规则 | 运算结果类型 | 示例与说明 |
---|---|---|---|
整型与浮点型混合运算 | 所有 int 和 float 自动提升为 double ,再进行运算。 | double |
|
整型/浮点型与字符型运算 | 字符型(char )自动转换为对应ASCII码的 int ,再根据另一操作数类型提升为 float 或 double 。 | double |
|
同类型运算但精度不同 | 低精度类型自动提升为高精度类型(int → float → double )。 | 高精度类型 |
|
赋值时的隐式类型转换 | 右侧表达式结果类型转换为左侧变量类型,可能丢失精度。 | 左侧变量类型 | int x = 3.8; // x=3(截断小数) |
二、表达式求值顺序与优先级
表达式示例 | 运算步骤分解 | 中间结果与类型 | 最终结果 |
---|---|---|---|
10 + 'a' + i * f - d / 3 | 1. 'a' 转为ASCII码97(int )→ 10 + 97 = 107(int) 2. i * f 转为double → 3*2.5=7.5 3. 107 + 7.5 = 114.5(double) 4. d/3 → 7.5/3=2.5(double) 5. 114.5 - 2.5 = 112.0(double) | 107(int)→7.5(double)→114.5→2.5→112.0 | 112.0(double) |
三、常见问题与规避建议
问题类型 | 错误示例 | 修正方案 | 规避建议 |
---|---|---|---|
精度丢失 | int x = 3.14; // x=3 | 显式强制类型转换:int x = (int)3.14; | 避免隐式赋值转换,使用强制类型转换明确意图。 |
表达式歧义 | a = c + 10 * 0.5; | 添加括号明确优先级:a = (c + 10) * 0.5; | 复杂表达式使用括号分隔操作,避免依赖默认优先级。 |
字符运算溢出 | char c = 'Z' + 5; // 非ASCII字符 | 使用模运算限制范围:c = ('Z' - 'A' + 5) % 26 + 'A'; | 对字符运算结果进行范围检查(如ASCII表0~127)。 |
未初始化的类型转换 |
| 初始化变量:float f = 0.0; | 禁止使用未初始化变量参与运算,编译器可能返回随机值。 |
3.4 C语句
3.4.1 C语句的作用和分类
一、C语句总览
类别 | 定义 | 作用 | 示例 |
---|---|---|---|
控制语句 | 用于控制程序执行流程的语句(共9种) | 实现条件判断、循环、跳转等功能 | if (x>y) max=x; else max=y; |
函数调用语句 | 由函数调用加; 组成的语句 | 执行特定函数功能(如输入输出、数学运算) | printf("Hello"); |
表达式语句 | 由表达式加; 组成的语句 | 完成赋值、运算等操作(任何表达式均可构成语句) | a=3; 、x+y; |
空语句 | 仅包含; 的语句 | 占位符作用(用于循环体空操作或流程跳转目标) | ; |
复合语句 | 由{} 包裹的多条语句和声明组成的代码块 | 将多条语句合并为单条逻辑单元,常用于条件/循环体 | { int a=1; printf("%d",a); } |
二、控制语句分类详解
控制语句类型 | 语法形式 | 功能描述 | 典型应用场景 |
---|---|---|---|
条件分支(if-else) | if (条件) {语句块1} else {语句块2} | 根据条件真假选择执行路径 | 数值比较、状态判断 |
循环(for) | for (初始化;条件;增量) {语句块} | 固定次数的循环执行 | 遍历数组、计数操作 |
循环(while) | while (条件) {语句块} | 条件满足时持续循环 | 文件读取、动态条件循环 |
循环(do-while) | do {语句块} while (条件); | 至少执行一次语句块,再进行条件判断 | 用户输入验证、菜单交互 |
流程控制(break) | break; | 跳出当前switch 或循环语句 | 提前终止循环、避免死循环 |
流程控制(continue) | continue; | 跳过本次循环剩余代码,直接进入下一轮循环 | 过滤特定数据、加速循环处理 |
多分支(switch) | switch (表达式) {case 值: ...} | 根据表达式值选择不同分支执行 | 状态机、菜单选择 |
函数返回(return) | return 表达式; | 终止函数执行并返回结果 | 函数结果传递、提前退出函数 |
跳转(goto) | goto 标签; | 跳转到指定标签位置(不推荐使用) | 错误处理集中退出(需谨慎) |
3.4.2 最基本的语句——赋值语句
一、赋值运算符与复合赋值运算符
类别 | 定义与规则 | 示例 | 注意事项 |
---|---|---|---|
基本赋值运算符 | 使用 = 将右侧表达式的值赋给左侧变量。 | a = 5; (将整数5存入变量a ) | 左侧必须为可修改的变量(左值),如 int x; x=3; 合法,5=x 非法。 |
复合赋值运算符 | 在 = 前加其他运算符(如+ 、* )构成复合运算符,简化表达式。 | a += 3; → a = a + 3; x *= y+8; → x = x*(y+8); | 复合运算符右侧表达式自动加括号,如 x += y+1 等价于 x = x + (y+1) ,而非 x = x + y +1 。 |
二、赋值表达式与左值/右值
类别 | 定义与规则 | 示例 | 注意事项 |
---|---|---|---|
赋值表达式 | 由变量、赋值运算符和表达式组成,具有计算和赋值的双重功能,值为左侧变量最终值。 | a = 3*5; (表达式值为15,变量a 值也为15) | 表达式可嵌套,如 a = (b=5); → a=5, b=5 。 |
链式赋值 | 自右向左结合,多个变量连续赋值。 | a = b = c = 5; (a 、b 、c 均为5) | 避免在链式赋值中混合不同数据类型,可能导致隐式类型转换错误。 |
左值 (lvalue) | 可出现在赋值左侧的实体,需有可寻址的存储空间(如变量)。 | int x; x=10; (x 是左值) | 常量(如5 )和表达式(如a+b )不能作为左值。 |
右值 (rvalue) | 只能出现在赋值右侧的实体,可以是常量、变量或表达式。 | y = x + 5; (x+5 为右值) | 左值也可作为右值,如 z = y; 中的 y 。 |
三、赋值类型转换规则
转换方向 | 规则与示例 | 底层原理 | 潜在风险 |
---|---|---|---|
浮点型 → 整型 | 舍弃小数部分,直接截断整数。float f=3.9; int i=f; → i=3 | 浮点数的二进制表示直接截断为整数格式。 | 数值超出整型范围时结果未定义(如 float f=1e10; int i=f; )。 |
整型 → 浮点型 | 整数值转为浮点数,可能损失精度。int i=123456789; float f=i; → 精度丢失 | 整数转换为浮点格式,超出精度时舍入。 | 大整数转单精度浮点型时精度丢失(如int i=16777217; float f=i; → f=16777216.0 )。 |
字符型 ↔ 整型 | 字符ASCII码与整数值直接转换。char c=65; → c='A' int i='B'; → i=66 | 字符存储为1字节整数,赋值时直接复制内存值。 | 字符范围(0-255)超出目标整型变量范围时可能截断。 |
长整型 → 短整型 | 截断高位,保留低字节。int i=289; char c=i; → c=33 (ASCII为'!') | 长整型低字节直接复制到短整型变量中。 | 数值超出短整型范围时结果不可预测(如 int i=32768; short s=i; → s=-32768 )。 |
double → float | 双精度舍入为单精度,保留6-7位有效数字。double d=3.1415926535; float f=d; → 精度降低 | 64位双精度截断为32位单精度,尾数部分舍入。 | 数值超出单精度范围时溢出(如 double d=1e40; float f=d; → 未定义)。 |
四、赋值语句与表达式区别
类别 | 定义与语法 | 应用场景 | 注意事项 |
---|---|---|---|
赋值表达式 | 末尾无分号,可嵌入其他表达式或语句中。 | if ((a=b)>0) { ... } (先赋值再判断) | 不可在条件语句中使用赋值语句(如 if (a=b;) 错误)。 |
赋值语句 | 赋值表达式加分号,独立成句。 | a = 3; printf("%d", a=5); (输出5并赋值) | 分号不可省略,否则编译错误。 |
五、变量初始化规则
初始化方式 | 语法与示例 | 等价操作 | 常见错误 |
---|---|---|---|
声明时初始化 | 定义变量时直接赋初值。int a=3; float f=3.56; | int a; a=3; (分两步执行) | 不可链式初始化:int a=b=c=3; (需改为 int a=3, b=3, c=3; )。 |
部分初始化 | 仅对部分变量赋初值。int a, b, c=5; (仅c 初始化为5) | int a, b, c; c=5; | 未初始化的变量值为随机数(局部变量),需显式赋值后再使用。 |
静态变量初始化 | 静态变量(如全局变量)在编译时初始化。static int x=10; | 存储在静态数据区,程序启动时自动赋值。 | 非静态局部变量初始化在运行时执行,每次函数调用重新赋值。 |
3.5 数据的输入输出
3.5.2 输入输出的基本概念
一、输入输出基本概念
类别 | 定义与规则 | 示例/说明 |
---|---|---|
输入输出本质 | 以计算机主机为主体: - 输出:主机向外部设备(如显示器、打印机)发送数据。 - 输入:输入设备(如键盘、扫描仪)向主机传递数据。 | 程序通过函数调用实现与设备的交互,如 printf 输出到屏幕,scanf 从键盘读取数据。 |
C语言输入输出实现 | C语言通过标准库函数(非语句)实现输入输出,如 printf 、scanf 、getchar 、putchar 等。 | 函数库与语言分离,提升可移植性。例如,不同编译系统可自定义输入输出函数,但通用函数(如 printf )保持一致。 |
二、输入函数 scanf
关键知识点
类别 | 规则与语法 | 示例 | 注意事项 |
---|---|---|---|
基本用法 | scanf("格式控制字符串", 变量地址列表); | scanf("%lf%lf%lf", &a, &b, &c); (输入三个双精度数赋给 a 、b 、c ) | - 必须使用地址符 & (如 &a )。- 输入数据分隔符需与格式字符串匹配(如空格分隔)。 |
格式控制符 | 常用格式符: - %lf :双精度浮点数- %d :整数- %c :字符- %s :字符串 | scanf("%d,%f", &x, &y); (输入 5,3.14 ,逗号分隔) | - 格式符类型需与变量类型一致。 - 输入数据自动转换为目标类型(如输入整数 3 用 %lf 接收会转为 3.0 )。 |
输入数据分隔 | 输入时数据分隔符需与格式字符串中的分隔符一致: - 空格分隔: "%lf %lf %lf" → 输入 1 3 2 - 逗号分隔: "%lf,%lf" → 输入 1.5,2.5 | scanf("%d:%f", &h, &m); (输入 12:30 ,冒号分隔) | 错误示例:scanf("%d%d", &a, &b); 但输入 5,6 → 导致 a=5 、b 未赋值。 |
三、输出函数 printf
关键知识点
类别 | 规则与语法 | 示例 | 注意事项 |
---|---|---|---|
基本用法 | printf("格式控制字符串", 输出列表); | printf("x1=%.2f\nx2=%.2f\n", x1, x2); (输出保留两位小数的根) | - 格式符与输出项类型需匹配(如 %f 对应浮点数)。 |
格式修饰符 | - 宽度控制:%7.2f → 输出占7列,小数2位。- 对齐:默认右对齐, %-7.2f 左对齐。 | printf("%-7.2f", 3.1415); → 输出 3.14 (左对齐,末尾补空格) | - 宽度不足时按实际位数输出(如 %5.2f 输出 123.45 会突破5列)。 |
特殊字符 | - \n :换行- \t :水平制表符(对齐输出)- \\ :输出反斜杠- %% :输出百分号 | printf("Name:\tAlice\nAge:\t20"); → 输出对齐的两列数据 | \t 通常占4或8个空格,具体取决于终端设置。 |
四、头文件与预处理指令
类别 | 规则与语法 | 示例/说明 | 注意事项 |
---|---|---|---|
头文件作用 | 包含标准库函数的声明和宏定义,如 stdio.h 提供 printf 、scanf 等函数的声明。 | #include <stdio.h> (包含标准输入输出函数) | 未包含头文件会导致编译错误(如 warning: implicit declaration of function 'printf' )。 |
#include 指令形式 | - 尖括号 < > :从系统目录查找头文件(标准库)。- 双引号 " " :优先从用户目录查找,再系统目录。 | #include "myheader.h" (包含用户自定义头文件)#include <math.h> (包含数学库) | 自定义头文件建议用双引号,标准库用尖括号。 |
预处理过程 | 编译前将头文件内容插入源代码中,替换 #include 行。 | 若 stdio.h 包含 printf 函数声明,预处理后程序可直接调用 printf 。 | 头文件内容应为函数声明、宏定义等,避免包含可执行代码。 |
五、扩展输入输出函数
函数 | 功能与用法 | 示例 |
---|---|---|
getchar() | 从标准输入读取单个字符。 | char c = getchar(); (读取用户输入的字符) |
putchar() | 向标准输出写入单个字符。 | putchar('A'); (输出字符'A') |
gets() 与 puts() | gets(str) 读取字符串(已弃用,因缓冲区溢出风险),puts(str) 输出字符串并自动换行。 | char str[20]; gets(str); puts(str); (不安全,建议用 fgets 替代) |
(续)