Chap1 C语言程序设计概述
本章要点:
1. 程序概念? 程序设计语言发展的几个阶段?
完成某项任务的一系列具体步骤; 机器语言--汇编语言--高级语言;
2. 算法概念? 算法描述方法? 流程图和伪代码?
对特定问题求解步骤的一种描述; 自然语言, 流程图, 伪代码;
3. C语言特点?
面向算法过程的程序设计语言,先分析问题求解过程, 再描述问题求解算法, 最后编程实现;
4. C语言程序的基本框架?
main函数, 若干自定义函数;
5. C语言的主要语法单位?
常量, 变量, 运算符, 分隔符, 表达式, 变量定义, 语句, 函数, 输入输出;
6. C语言程序上机步骤?
源文件(.c)编译成目标程序(.c), 通过连接编程可执行程序(.exe);
7. 实现问题求解的过程?
问题分析与算法设计--编辑程序--编译连接--运行与调试;
1.1 程序与程序语言
程序: 完成某项任务的一系列的具体步骤;
例如: 1. 新生开学典礼程序;
2. 中央电视台春节联欢晚会程序;
计算机程序: 用计算机语言描述的解决某一问题的一系列加工步骤, 符合一定语法规则的符号序列, 告诉计算机:A. 处理什么数据--处理数据 ;
B. 如何处理数据--处理步骤;
程序设计语言: 计算机语言, 提供编程手段, 提供数据表达和数据处理的两大功能;
发展阶段: 机器语言--汇编语言--高级语言
高级语言: 1. 面向算法过程的程序设计语言(C语言)
编程过程: 分析问题求解过程--描述问题求解算法--编程实现;
2. 面向任务的程序设计语言;(SQL语言)
无需知道问题如何求解, 只需描述求解什么问题;
3. 面向对象的程序设计语言(C++, Java)
面向客观事物的设计程序;
1.2 算法及其描述
程序 = 数据结构 + 算法;
算法: 对特定问题求解步骤的一种描述;
例如: 新生开学典礼算法;
算法描述通常包括: 1. 自然语言;
2. 传统流程图;
3. 结构化流程图;(N-S流程图)
4. 伪代码;(介于自然语言与计算机语言之间的文字)
1.3 C语言的发展与特点
发展历史: 1972发明C语言--1978旧标准C语言--1983新标准C语言;
特点: 结构化, 简洁灵活, 易移植, 强大处理能力, 代码运行效率高等;
注意: C语言中大小写字母代表不同含义;
1.4 简单C语言程序
要点: 1. 只由main函数构成的简单程序;
2. 由main函数调用另一个函数构成的简单程序;
3. C语言程序的基本结构;
例1: 输出"Hello World!";
运行: 程序从main函数开始运行, 当main函数运行结束, 程序运行结束;
组成: 程序由函数组成, 有且必须有一个主函数main函数;
#include <stdio.h>
int main()
{
printf("Hello World!");
return 0;
}
例2: n!阶乘问题;
组成: main函数带若干自定义函数组成, 有且必须有一个主函数main函数;
#include <stdio.h>
int fact(int n)
{
int i, res = 1;
for (i = 1; i < n; i ++)
{
res *= i;
}
return res;
}
int main()
{
int n, res;
printf("Input n: ");
scanf("%d", &n);
res = fact(n);
printf("%d! = %d\n", n, res);
return 0;
}
C语言程序构成: 一个完整的C语言程序由一个main函数带若干个用户自定义函数所构成;
1.5 C语言简介
C语言的功能: 1. 数据表达: 表达所要处理的数据;
2. 流程控制: 表达数据处理的流程;
1. 数据表达
通过数据类型来实现;
数据类型: 共同特点的数据集合的总称, 涉及数据定义和数据运算两个方面;
例如: 整数类型--int
数据定义: {..., -2, -1, 0, 1, 2, ...}, 有限范围;
数据运算: + - * / %等运算操作;
=================(此处贴数据类型)
基本类型: 程序设计语言事先已经定义好,
包括: 1. 整型--int;
2. 实型--float, double;
3. 字符型--char;
构造类型: 程序员通过使用基本类型来构造复杂类型,
包括: 数组, 结构, 共用体, 枚举,指针, 链表, 文件;
2. 流程控制
程序通常可以通过3种基本控制结构组合而成;
语句级控制: 三种基本控制结构;
顺序结构: 顺序执行模块;
分支结构: 也叫选择结构, 根据条件选择执行模块;
循环结构: 也叫重复结构, 根据条件重复执行模块;
单位级控制: 函数结构;
复杂问题: 将程序分为若干相对独立的子程序(函数);
标题3: C语言的语法
C语言字符集: C语言编程能使用的字符集是固定的, 有限的,
包括: 1. 英文字母, 大小写字母共52个;
2. 阿拉伯数字, 共10个;
3. 下划线,_;
C语言标识符: 标识符: 由字母, 数字及下划线组成的字符序列, 且首字符必须是字母或下划线;
两类标识符: 1. 保留字: C语言规定的, 赋予特定含义, 有专门用途的标识符,不能作其它用途, 如printf, if, for;
2. 自定义标识符: 程序中自定义的变量名, 类型名, 函数名, 常量名, 常用英文单词或缩写。
C语言的语法: (1)常量;
(2)变量;
(3)运算符;
(4)分隔符;
(5)表达式;
(6)变量定义;
(7)语句:1. 表达式语句;
2. 分支语句;
3. 循环语句;
4. 复合语句: 大括号内的语句;
(8)函数;
(9)输入输出;
C语言上机步骤: 源文件(.c)编译成目标程序(.c), 通过连接编程可执行程序(.exe)
1.6 实现问题求解的过程
1. 问题分析与算法设计
例如: 求1-100范围内, 满足3的倍数的若干整数的个数;
思路: 设置计数器count, 初值为0, 让变量i在1-100的整数范围内变化寻找3的倍数并计数到count中;
2. 编辑程序
将算法翻译成C源程序;
3. 编译链接
编译程序: 对源程序进行编译, 生成二进制代码, 指出语法错误(编译错误);
连接程序: 将目标程序与编程环境提供的库函数连接, 指出连接错误;
4. 运行与测试(经验)
逻辑错误: 也称语义错误, 指运行程序导致运行结果不正确;
调试程序: 在程序中查找错误并改正错误的过程,主要方法:
1. 设置断点;
2. 单步跟踪;
调试工作: 需要耐心和经验, 是程序设计最基础的技能之一;
总结: C语言程序调试, 运行步骤;
=========此处贴一个流程图=========
Chap2 C语言简单程序设计
本章要点:
1. 编写程序在屏幕上显示信息;
2. 编写程序实现简单的数据处理;
3. 使用if-else语句编程计算二分段函数;
4. 使用for语句实现指定次数的简单循环结构程序;
2.1 在屏幕上显示信息
例1: 在屏幕上显示一个短句: Hello World!;
1. 任何程序都有主函数main;
2. 程序由若干语句组成;
3. 语句由;结束;
main函数头格式定义: 函数类型 main(函数参数);
void用法: 函数类型表示无返回值, 参数表示无参数;
=========此处贴代码========
2.2 求三角形面积(顺序结构)
例2: 输入三角形的底a和高h, 求三角形的面积;
函数体构成: IPO结构
I: 主要由变量, 函数声明或数据输入;
P: 数据处理, 主体;
O: 结果输出或返回结果;
=========此处贴代码=========
2.3 计算分段函数(分支结构)
2.4 输出华氏--摄氏转换表(循环结构)
Chap8 指针
本章要点:
1. 变量, 内存单元和地址之间是什么关系?
2. 如何定义指针变量, 怎样使用指针变量?
3. 什么是指针变量的初始化?
4. 指针基本运算? 如何通过指针操作所值变量?
5. 指针作为函数参数的原理与作用?
6. 如何使用指针实现函数调用返回多个值?
7. 指针数组: 指针数组与二级指针的关系? 如何使用指针数组处理多个字符串?
关于指针: 1. 重要数据类型; 2. C语言精华; 3. 复杂数据结构;
用途: 1. 实现动态内存分配;
2. 使用数组和字符串;
3. 实现函数间各类数据的传递;
8.1 指针与指针变量
1. 引例: 如何获取银行卡密码问题
问题描述: 密码888在编号1000key抽屉, key抽屉编号1000在编号2000addr抽屉, 如何获取密码?
方法一: 直接获取: 通过key抽屉的名字, 直接取出key抽屉的内容(888);
printf("%d", key);
方法二: 间接获取: addr抽屉指向key抽屉, 通过addr抽屉, 间接取出key抽屉的内容(888);
printf("%d", *addr);
例1: 利用指针模拟寻找银行卡密码的过程;
#include <stdio.h>
int main()
{
int key = 888; // 密码
int *addr = &key; // 地址, 这个指针指向key
printf("The key is %d\n", key); // 直接访问
printf("The key is %d\n", *addr); // 间接访问
return 0;
}
2. 地址与指针 -- 指针的概念
指针: 变量的内存单元地址, 即内存单元编号;
变量的值与地址不同;
变量x: 地址1000, 值20;
变量y: 地址1002, 值1;
变量p: 地址2000, 值1000(x的地址);
3. 指针变量的定义: 类型名 * 指针变量名
类型名: 指针变量所指变量的类型; *是指针声明符;
例如: int * p; 指针变量名是p, 不是*p;
注意: 定义多个指针变量, 每个变量前都必须加上*, 不能简写;
指针初始化: 使指针变量指向普通的值变量, 例如int *p = &a;
4. 指针的基本运算
1. 取地址运算&(给出变量的地址)
注意: 指针变量类型和所指变量类型必须相同;
2. 间接访问运算*(访问指针所指向的变量)
*p: 指针变量p所指向的变量, 即变量a;
注意: 指针变量类型和所指变量类型必须相同;
例2: 指针取地址运算和间接访问运算
#include <stdio.h>
int main()
{
int a = 3, *p;
p = &a;
printf("a = %d, *p = %d\n", a, *p);
*p = 10;
printf("a = %d, *p = %d\n",a, *p);
scanf("%d", &a);
printf("a = %d, *p = %d\n", a, *p);
(*p)++;
printf("a = %d, *p = %d\n", a, *p);
return 0;
}
程序运行结果:
相关说明:
3. 赋值运算
#include <stdio.h>
int main()
{
int a = 3, *p1, *p2;
p1 = &a; // 把a的地址赋给p1, 即p1指向a
p2 = p1; // p2也指向a
return 0;
}
注意: 只有同类型指针才能相互赋值;
8.2 指针与函数
1. 引例
例3: 交换两个变量值的问题;
问题描述: 两个变量a和b, 如何实现a, b互换?
三个函数: swap1(), swap2(), swap3();
分析: swap1(), swap2(), swap3()三个函数, 谁能实现交换功能?
#include <stdio.h>
int main()
{
void swap1(int x, int y), swap2(int *px, int *py), swap3(int *px, int *py);
int a = 1, b = 2;
swap1(a, b);
printf("After calling swap1: a = %d b = %d\n", a, b);
a = 1; b = 2;
swap2(a, b);
printf("After calling swap2: a = %d b = %d\n", a, b);
a = 1; b = 2;
swap3(a, b);
printf("After calling swap3: a = %d b = %d\n", a, b);
return 0;
}
2. 指针作为函数参数
swap1()函数:
swap2()函数:
swap3()函数:
结论: 被调函数如何改变主调函数中变量的值? 必须满足:
1. 传址方式: 形参是指针变量, 实参是变量地址, 传地址;
2. 间接访问: 被调函数间接访问, 改变形参所指的实参变量;
例4: 通过指针实现函数调用返回多个值;
问题描述: 输入年和天数, 输出对应的日期, 如输入2000和61, 输出2000-3-1;
要求: 定义函数month_day(), 根据年和天数, 计算并返回对应的日期(两个值: 月, 日);
#include <stdio.h>
void month_day(int year, int yearday, int *pmonth, int *pday)
{
int k, leap;
int tab[2][13] = { {0, 31, 28, 31, 30, 31, 30, 31, 30, 31, 30, 31}, // 是闰年
{0, 31, 29, 31, 30, 31, 30, 31, 30, 31, 30, 31} }; // 不是闰年
// 计算闰年标志leap
leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
for (k = 1; yearday > tab[leap][k]; k++) // 计算对应的月和日
yearday = yearday - tab[leap][k];
*pmonth = k; // 间接访问, 返回月
*pday = yearday; // 间接访问, 返回日
}
int main()
{
int day, month, year, yearday;
printf("Input year and yearday:");
scanf("%d%d", &year, &yearday);
month_day(year, yearday, &month, &day);
printf("%d-%d-%d\n", year, month, day);
return 0;
}
3. 指针作为函数返回值
指针函数: 指针作为函数的返回值, 返回各种类型的指针数据;
定义格式: 类型标识符 * 函数名 (形式参数表)
int * fun(int a, int b)
{
函数体语句;
return (指针);
}
例5: 设计一个指针函数min, 返回指向两个整数中最小值的地址;
#include <stdio.h>
int * min(int x, int y)
{
int *p;
if (x < y) p = &x;
else p = &y;
return (p); // 返回最小值变量的地址
}
int main()
{
int x, y, *p;
printf("Input x and y:");
scanf("%d%d", &x, &y);
p = min(x, y); // 函数调用
printf("The min is %d\n", *p); // 输出结果
return 0;
}
思考: *p访问正确吗?
答: 并不安全, 因为x和y是局部变量, 它们的地址在函数min结束后可能变得无效, 在这种情况下, *p的值是未定义的;
修改后的代码
#include <stdio.h>
int * min(int *x, int *y)
{
int *p;
if (*x < *y) p = x;
else p = y;
return (p); // 返回最小值变量的地址
}
int main()
{
int x, y, *p;
printf("Input x and y:");
scanf("%d%d", &x, &y);
p = min(&x, &y); // 函数调用
printf("The min is %d\n", *p); // 输出结果
return 0;
}
4. 指向函数的指针
函数入口地址: 函数包括一组语句, 运行时存储在程序区的某内存段, 该段内存的起始地址为函数入口地址;
函数名: 函数入口地址;
函数指针: 指针变量, 专用指向函数;
定义形式: 类型标识符 (*指针变量名) (形参表);
例如: int (*p) (int, int);
作用: 定义函数指针变量p, 指向int类型函数;
函数指针赋值: 将函数入口地址赋给函数指针, 格式: 指针变量名 = 函数名;
函数调用: 两种形式
1. 函数名形式: 函数名(实参表) 例如: a = max(x, y);
2. 函数指针形式: (*函数指针变量名)(实参表) 例如: a = (*p)(x, y);
例6: 函数指针的应用
问题描述: 设计两个函数max()和min(), 求两个整数的最大值和最小值; 在主函数中定义一个函数指针变量p, 通过指针p间接调用max()和min()函数, 并输出调用结果;
#include <stdio.h>
int max(int *x, int *y)
{
if (*x > *y) return *x;
else return *y;
}
int min(int *x, int *y)
{
int (x < y) return *x;
else return *y;
}
int main()
{
int a, b, res1, res2;
int (*p)(int, int);
p = max;
res1 = (*p)(a, b);
p = min;
res2 = (*p)(a, b);
printf("%d, %d\n", res1, res2);
return 0;
}
8.3 指针与数组
1. 指向一维数组的指针
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int *p;
p = a; // p指向数组a
p = &a[0]; // p指向a[0]
return 0;
}
注意: 数组名a为指针常量, 不变; p为指针变量, 可变;