以下为个人学习笔记和习题整理
课程:计算机程序设计(C++)- 西安交通大学 @ 中国大学 MOOC
https://www.icourse163.org/course/XJTU-46006
# 课堂笔记
# 数组
# 一维数组
# 定义方法
<类型> <标识符>[<大小-数组元素的个数>]; |
double length[30], width[30]; | |
const int N=100, M=20; | |
int score[N*M]; |
# 初始化方法
<类型> <数组名> [<常量表达式>] = {<表达式1>, <表达式2>, ...}; |
// 全部元素 | |
double room[]={401,402,403,404,405,606,407,408,409,411}; | |
// 部分元素 | |
double average[30]={21.0, 2012.7, 7.5+2.3, a*b}; |
# 使用方法
<数组名>[<下标>] |
下标范围是 0 ~ 元素个数 - 1
// 定义数组 | |
int length[10],width[10]; | |
// 给数组元素赋值,正确 | |
length[0]=1; | |
// 数组元素参与运算,正确 | |
sum=length[0]*2; | |
// 输入数组元素,正确 | |
cin>>length[0]; | |
// 输出数组元素,正确 | |
cout<<length[0]; | |
// 对整型数组整体输入,错误 | |
cin>>length; | |
// 数组下标超界,错误 | |
cin>>length[10]; | |
// 数组整体赋值,错误 | |
width=length; | |
// 整型数组整体输出,错误 | |
cout<<length; |
# 字符数组与字符串
sizeof(类型名) | |
// 或者 | |
sizeof(变量名) |
// 字符数组 | |
char chr[]={'H', 'e', 'l', 'l', 'o'}; | |
int a=sizeof(chr) //a=5 | |
// 定义一个字符数组,并使用字符串进行初始化 | |
char chr[]="Hello" | |
int b=sizeof(chr) //b=6 | |
// 因为除了 Hello 5 个字符外,还有一个 \0 串结束符 |
# 例子:将单词中小写字母转换成大写
编写程序,用户从键盘输入一个小写字母组成的英文单词,将其转换为大写,然后显示到屏幕上。
#include <iostream> | |
using namespace std; | |
int main() | |
{ | |
char str[10]; // 定义一维数组,用来存放单词的字母,假设字母数量不超过 9 个 | |
int i=0; // 表示数组下标的当前值 | |
cin>>str; // 整体输入一维字符型数组 | |
while(str[i]!='\0') // 如果当前不是表示结束的字符,则进入循环体 | |
{ | |
str[i]=str[i]-32; // 字母大小写转换,32 是大小写字母 ASCII 码值的差 | |
i=i+1;// 调整数组下标值 | |
} | |
cout<<str<<endl; // 整体输出一维字符数组 | |
return 0; | |
} |
# 二维数组
从逻辑结构讲,类似一个二维表格,由行标和列标指明数组元素在表格中的位置。
在内存中,按一维数组来存放,即按行顺序在内存中分配存储单元,占据一片连续的存储单元。
# 定义方法
<类型> <数组名>[<常量表达式1>][<常量表达式2>]; | |
// 常量表达式均为整型,分别表行数和列数 | |
// 数组个数 = 常量表达式 1 * 常量表达式 2 |
int score[60][3]; // 总共包含 60*3=180 个元素 | |
const int N=10,M=3; | |
double point[N][M]; |
行标列标不能是变量,必须是常量表达式,这种定义方式又称为静态定义。
# 初始化方法
<类型> <数组名>[<行数>][<列数>]={<表达式1>,<表达式2>,...}; |
// 全部元素 | |
int A[3][3]={1,2,3,4,5,6,7,8,9}; | |
// 明确说明了行数和列数 | |
int A[][3]={1,2,3,4,5,6,7,8,9}; | |
// 只说明列数,但可以根据初始化值的个数和列数,确定行数 | |
int A[3][3]={ {1,2,3},{4,5,6},{7,8,9} }; | |
// 初始化值添加了花括号,可以明确看出每行缩包含的元素 | |
// 部分元素 | |
int B[3][3]={1,2,3}; | |
// 只对第一行赋值 | |
int B[3][3]={ {1,2},{4,5},{7,8} }; | |
// 只对所有行中的前两列的元素赋值 |
# 使用方法
<数组名>[<行标>][<列标>] | |
// 行标的范围是 0 ~ 行数 - 1 | |
// 列标的范围是 0 ~ 列数 - 1 |
int A[2][2]; | |
A[0][0]=2; // 下标最小元素赋值,即第一个数组元素 | |
A[1][1]=1; // 下标最大元素赋值,即最后一个数组元素 |
下标不能越界
# 问题:一维数组与二维数组的对应关系
如果使用一维数组 a[M*N]
和二维数组 b[M][N]
表示同一矩阵,则 b[i][j]
与 a 中哪个下标的元素对应?
1 | 2 | 3 |
4 | 5 | 6 |
b[0][0] == a[0]
b[0][1] == a[1]
b[0][2] == a[2]
b[1][0] == a[3]
b[1][1] == a[4]
b[1][2] == a[5]
b[i][j]=a[i*N+j]
#include <iostream> | |
using namespace std; | |
int main() | |
{ | |
const int m=2,n=3; // 行数 m、列数 n | |
int a[m*n],b[m][n]; | |
int i,j,x=1; //i 和 j 为循环变量,与数组下标有关,x 是数组元素的值,初始化为 1 | |
for(i=0;i<6;i++,x++) | |
{ | |
a[i]=x; // 循环给数组 a 赋值 | |
} | |
x=1; | |
for(i=0;i<2;i++) | |
{ | |
for(j=0;j<3;j++,x++) | |
{ | |
b[i][j]=x; // 循环给数组 b 赋值 | |
cout<<a[i*n+j]<<"\t"<<b[i][j]<<endl; | |
} | |
} | |
return 0; | |
} |
# 例子:月份转换
编写程序,将用户输入的阿拉伯数字转换成对应月份的英文单词。
#include <iostream> | |
using namespace std; | |
int main() | |
{ | |
char month[12][10]={"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; // 二维数组定义和初始化 | |
// 行标表示英文月份的序号,共 12 个 | |
// 列标表示英文月份中字母出现的位置,因为单词最长有 9 个字母,因此长度定义为 10 | |
int m; | |
cin>>m; | |
if(m>0&&m<13) { // 输入值在 1-12 之间 | |
cout<<month[m-1]; // 二维数组使用 | |
} | |
else { | |
cout<<"The month is wrong"; | |
} | |
return 0; | |
} |
# 多维数组
int A[5][4][3]; // 类似 5 行 4 列 3 层的魔方结构 | |
double B[3][3][3]; // 类似 3 行 3 列 3 层的魔方结构 |
存储数据时,按照先层后行再列的方式:
第 0 层第 0 行第 0 列开始,完成 1 行存储后,存储第 2 行,直到该层存储完毕后,开始第 1 层的存储。
# 结构体
可以用来表示某事物的多个特征,并保证多个特征之间的关系。
# 结构体类型
# 定义方法
struct <结构体名> | |
{ | |
<类型名1> <成员名表1>;// 定义方法与普通变量相同 | |
<类型名2> <成员名表2>; | |
...... | |
<类型名n> <成员名表n>; | |
}; |
struct Date | |
{ | |
int year,month,day; | |
}; |
同一结构体的成员不能同名,但可与结构体外的其它变量同名
# 结构体变量
定义好结构体变量后,就可以声明该结构体的变量。
# 声明方法
struct <结构体名> <变量列表>; |
# 声明时机
- 定义结构体类型后声明
struct Date today; |
- 定义结构体类型同时声明,可以省略结构体名
struct Date{int year,month,day;} today; | |
// Date 为结构体名称,可以省略 |
# 初始化方法
struct <结构体名> <变量1>={<数据列表>}, <变量2>={<数据列表>}, ... ; |
struct Date today={2015,2,20}; | |
//year, month, day 将分别被初始化为 2015,2,20 |
# 使用方法
使用成员运算符 / 分量运算符 .
<结构体名>.<成员名> |
today.year=2015 // 对结构体变量 today 的成员 year 赋值 | |
today.month=2 | |
today.day=20 |
# 所占内存大小
sizeof(类型名) | |
// 或者 | |
sizeof(变量名) |
为了提高内存的访问效率,大多数计算机系统在存储数据时,都是从偶地址开始的。
字符和字符串所占的内存空间,是按机器字长对齐的,即按边界对齐存储。
struct SHAPE1 {char name; int x; int y; char classification;}; | |
sizeof(SHAPE1) //4*4=16 | |
// 两个 char 与两个 int 对齐,每个 int 占 4 个字节 | |
struct SHAPE2 {char name; double x; int y; char classification;}; | |
sizeof(SHAPE2) //8*2+4*2=24 | |
// 前一个 char 与 double 对齐,占用 8 个字节,后一个 char 与 int 对齐,占用 4 个字节 | |
struct SHAPE3 {char name; double x; double y;char classification;}; | |
sizeof(SHAPE3) //8*4=32 | |
// 两个 char 与两个 double 对齐,每个 double 占 8 个字节 |
# 结构体数组
以一维结构体数组举例
# 定义方法
struct <结构体名> <结构体数组名>[<数组大小=元素个数>]; |
struct Date manydates[30]; |
# 初始化方法
struct <结构体名> <结构体数组名>[<数组大小>]={<结构体类型值列表>}; |
struct Date manydates[30]={ {2015,1,1}, {2015,2,17}, {2015,3,8} }; | |
// 该结构体包含 30 个元素,仅给 0、1、2 号元素赋值。 | |
struct Date manydates[]={ {2015,1,1}, {2015,2,17}, {2015,3,8} }; | |
// 省略下标,该结构体仅包含 3 个元素。 |
# 使用方法
<结构体数组名>[<整型表达式>] |
如需使用结构体成员,则使用成员运算符 .
。
manydates[i].year=2015 | |
manydates[i].month=2 | |
manydates[i].day=20 |
下标范围不能越界。
# 例子:简易通讯录
编写程序,按照通讯录格式录入联系人信息并按照输入的反序显示联系人信息。
通讯录内容要求:
- 姓名
- 性别
- 联系电话
- 联系电话
#include <iostream> | |
using namespace std; | |
int main() | |
{ | |
// 定义结构体类型 telelist、结构体数组 list1 包含 3 个元素 | |
struct telelist { | |
char name[8]; | |
char sex; | |
char num1[5]; | |
char num2[5]; | |
} list1[3]; | |
int i; | |
for(i=0;i<=2;i++) { | |
// 结构体数组使用 | |
// 循环输入结构体成员值 | |
cin>>list1[i].name>>list1[i].sex>>list1[i].num1>>list1[i].num2; | |
} | |
for(i=2;i>=0;i--) { // 反序 | |
cout<<list1[i].name<<"/"<<list1[i].sex<<"/"<<list1[i].num1<<"/"<<list1[i].num2<<endl; | |
} | |
return 0; | |
} |
# 枚举
可以检查变量取值的合法性
# 枚举类型
# 定义方法
enum <枚举类型名>{枚举常量表}; |
enum Week {Sun, Mon, Tes, Wed, Thu, Fri, Sat}; | |
// 默认赋值为 Sun=0,Mon=1,Tes=2,Wed=3,Thu=4,Fri=5,Sat=6 | |
enum Coin {PENNY=1, NICKEL=5, DIME=10, QUARTER=25, HALF_DOLLAR=50, DOLLAR=100}; | |
enum Color {red, yellow, blue=1, white, black}; | |
// red=0,yellow=1,blue=1,white=2,black=3 | |
//blue 之前的元素按照默认方式赋值,blue 后的元素按逐个加 1 的方式分配整数 |
枚举常量是以标识符形式表示的整型量,而不是字符串或字面常量。
# 枚举变量
# 声明方法
enum <枚举类型名> <枚举变量列表>; |
# 声明时机
- 定义枚举类型后声明
enum COLOR background, foreground; |
- 定义枚举类型同时声明
enum Week {Sun=7, Mon=1, Tes, Wed, Thu, Fri, Sat} begin, end; |
# 使用方法
不同类型的枚举变量不能相互赋值
// 定义一个枚举类型 Week,和两个 Week 变量 begin end | |
enum Week {Sun=7,Mon=1,Tes,Wed,Thu,Fri,Sat} begin, end; | |
// 直接输入枚举变量,错误,不能直接输入枚举变量 | |
cin>>background; | |
// 整数赋给枚举变量,错误,不能将整数赋值给枚举变量 | |
begin=1; | |
// 将枚举常量赋给枚举变量,正确 | |
begin=Mon; | |
end=Sun; | |
// 相同类型的枚举变量赋值,正确 | |
begin= end; | |
// 整数强制类型转换后赋给枚举变量,正确 | |
begin=(Week)1; | |
// 将枚举变量赋给整型变量,正确 | |
int a=begin; | |
// 将枚举常量赋给整型变量,正确 | |
int b=Sun; | |
// 可以直接输出枚举变量,正确,输出的是变量的整数值,而不是枚举常量名 | |
cout<<begin; | |
// 枚举变量可以参加数学运算,正确,结果是数值型 | |
cout<<end-begin; |
# 例子:三色球组合
口袋中有红、黄、蓝 3 种颜色的小球各一个,从中取出两个,显示各种可能的组合。
#include <iostream> | |
using namespace std; | |
int main() | |
{ | |
enum color{red,yellow,blue}; // 定义枚举类型 color,包括三个枚举元素,表示红、黄、蓝 3 种颜色 | |
int temp,i,j; | |
for(i=red;i<=yellow;i++) // 枚举变量使用方法,模拟第 1 次取球 | |
{ | |
for(j=i+1;j<=blue;j++) // 循环嵌套,模拟第 2 次取球 | |
{ | |
for(int t=0;t<2;t++) // 循环嵌套,处理每次取球的颜色 | |
{ | |
switch(t) // 多路分支,确定不同取球次数所对应的小球颜色 | |
{ | |
case 0: temp=i; break; // 第 1 次由第一层的 i 决定 | |
case 1: temp=j; break; // 第 2 次由第二层的 j 决定 | |
} | |
switch((enum color)temp) // 强制转换为枚举变量 | |
{ | |
case red: cout<<"red"<<"\t"; break; | |
case yellow: cout<<"yellow"<<"\t"; break; | |
case blue: cout<<"blue"<<"\t"; break; | |
} | |
} | |
cout<<"\n"; | |
} | |
} | |
return 0; | |
} |
# 实例
# 实例 1 冒泡排序
用户从键盘输入 N,然后输入 N 个实数,使用冒泡排序方法对这 N 个元素排序,输出排序后的数据。
运行结果:
8
9 8 7 6 5 4 3 2
2 3 4 5 6 7 8 9什么是冒泡排序?
- 两两比较相邻元素
A(I)
和A(I+1)
(I=1,2,…N-1), 如果A(I)
>A(I+1)
,则交换A(I)
和A(I+1)
的位置; - 对剩下的
N-1
个元素,再两两进行比较,按同样规则交换它们的位置,经过N-2
次比较,将次最大值交换到A(N-1)
的位置; - 如法炮制,经过
N-1
趟的 “冒泡处理”,每趟进行N-i
次的比较,全部数列有序。
- 两两比较相邻元素
算法描述:
设有N个元素,用数组a[i]表示,i=0,...,N
1. 输入N;
2. 输入a[i],i=0,...,N-1;
3. 对i=0,...,n-2
4. 对j=0,...,n-2-i
5. 若a[j]>a[j+1],则交换它们的值。
6. 对i=0,...,N-1,输出a[i]。
- 源程序:
#include<iostream> // 包含输入输出头文件 | |
#include<cmath> | |
using namespace std; // 指定名字空间 | |
int main() { // 主函数 | |
double a[100]; // 定义数组,大小 100 | |
int N; // 元素的实际个数 | |
int i=0,j=0; // 循环变量,并进行初始化 | |
cin>>N; // 输入元素个数 | |
//------- 输入数据 ----------- | |
for(i=0;i<N;i++) | |
{ // 输入 N 个元素 | |
cin>>a[i]; // 循环体只有一行 | |
} | |
//------- 排序 --------------- | |
for(i=0;i<N-1;i++) | |
{ // 控制 n-1 趟冒泡 | |
for (j=0;j<N-1-i;j++) | |
{ | |
if (a[j]>a[j+1]) | |
{ // 比较相邻的两个元素 | |
int tmp; // 临时变量 | |
tmp=a[j]; // 交换 | |
a[j]=a[j+1]; | |
a[j+1]=tmp; | |
} | |
} | |
} | |
//-------- 输出 ---------- | |
for(i=0;i<N;i++) | |
{ // 使用循环,输出 N 个元素 | |
cout<<a[i]<<" "; // 输出 a [i], 后加空格,不换行 | |
} | |
cout<<endl; // 所有元素输出完之后才换行 | |
return 0; // 函数返回 | |
} |
- 程序分析
- 注意程序中带短划线的三段注释,基本体现了本程序的三块内容,输入、处理和输出。
- 排序的程序主要有两个循环,外层循环控制
N-1
趟,内层循环控制一趟的若干次比较。
第 1 趟需要N-1
次比较,第 2 次需要N-2
次比较,第N-1
次需要N-(N-1)=1
次比较。 - 元素存放在数组中,数组的大小在定义时要求是常量表达式。不能先输入
N
,再定义数组double A[N]
,这是不正确的。
# 实例 2 文字信息统计
用户输入一段文本(英文),统计其字符总个数,大写字母个数、小写字母个数、数字个数及其他字符个数。
运行结果:
C++ Programming 字符串总长度:15 大写字母:2 小写字母:10 数字个数:0 其他字符:3
问题分析:
输入字符串。
cin>> <字符数组名>
只能输入单词;
因为>>
以空格、tab 键、回车为分隔符,遇到空格认为是一项数据的结束。cin
是输入流对象,它有一个成员函数getline()
可以读取带空格的一行字符串。
基本使格式为:cin.getline(<字符数组名>,<字符长度>);
例如 char sentence[100]; // 字符数组大小 100
cin.getline(sentence,99);
// 可以输入最长 99(回车结束)的字符串,中间可以有空格。
统计计数。
输入的文本看做字符串,统计字符个数,就是从字符串的第 1 个字符开始,逐个计数,直到结束。
注意,在 C++ 中,字符串的结束是\0
。
统计各类字符个数,只要在逐个计数的过程中,再对各类字符计数。
计数就是逐个数,用一个变量表示,每遇到一个字符,该变量增加 1。
- 源程序:
#include<iostream> // 包含输入输出头文件 | |
using namespace std; // 指定名字空间 | |
int main() { // 主函数 | |
const int N=101; // 定义常量,表示问题规模 | |
char str[N]; // 定义字符数组,存放字符串,N 是常量 | |
// 定义变量并初始化为 0 | |
int len=0, // 字符串长度 | |
capital=0,// 大写字母个数 | |
smallletter=0,// 小写字母个数 | |
digit=0,// 数字个数 | |
others=0;// 其他字母个数 | |
int i; // 循环变量 | |
cin.getline(str,N); // 输入字符串 | |
// 处理 | |
i=0; // 从字符串的第 1 个字符开始 | |
while(str[i] != '\0') { // 不是结束符时,循环 | |
len++; // 长度加 1 | |
if(str[i]<='Z' && str[i]>='A') | |
{ // 大写字母 | |
capital++; | |
} | |
else if(str[i]<='z' && str[i]>='a') | |
{ // 小写字母 | |
smallletter++; | |
} | |
else if(str[i]<='9' && str[i]>='0') | |
{ // 数字 | |
digit++; | |
} | |
else | |
{ // 其他字符 | |
others++; | |
} | |
i++; // 字符下标加 1,指向下一个字符 | |
} | |
// 输出结果 | |
cout<<"字符串总长度:"<<len<<endl; | |
cout<<" 大写字母:"<<capital<<endl; | |
cout<<" 小写字母:"<<smallletter<<endl; | |
cout<<" 数字个数:"<<digit<<endl; | |
cout<<" 其他字符:"<<others<<endl; | |
return 0; // 函数返回 | |
} |
- 程序分析:
C++ 提供了一些字符串处理的库函数,方便字符串操作。
可以直接调用,常见的有:
// 包含头文件 | |
#include <cstring> | |
// 或使用头文件 | |
#include <string.h> | |
// 求字符串 s 的长度 | |
int strlen(char *s); | |
// 将字符串 source 复制到 destin 中 | |
char *strcpy(char *destin,char *source); | |
// 比较 string1 和 string2 | |
int strcmp(char *string1, char *string2); | |
// 将 source 连接到 destin 末尾 | |
char *strcat(char *destin, char *source); | |
//string 转换为小写 | |
char *strlwr(char *string); | |
//string 转换为大写 | |
char *strupr(char *string); |
在 VS2008 及以上版本中调用
strcpy
、strcat
等函数时,由于安全原因会提示警告。
可以在程序开始的位置加上#define _CRT_SECURE_NO_WARNINGS
这一变量。
# 实例 3 使用 string
字符串的操作
设有两个句子:
Heavy rains are pushing water levels beyond the limit.
Sluice gates at Three Gorges Dam opened to discharge water.
开始由两个符号表示,请将它们合并为一段文字,然后查找其中的 Heavy
替换为 Strong
,最后显示处理过的文本。
问题分析:
目的:练习使用string
类表示字符串;
先定义两个string
对象,为它们赋值,用+
号将它们连接起来;
使用find
函数查找Heavy
的位置,使用erase
函数删除该字符串,再使用insert
函数插入Strong
。源程序:
#include <iostream> // 包含需要的头文件 | |
#include <cstring> // 包含头文件 string | |
using namespace std; // 名字空间 | |
int main() { // 主函数 | |
// 定义并初始化字符串对象 text1 | |
string text1("Heavy rains are pushing water levels beyond the limit."); | |
string text2,text3; // 定义但没有初始化话对象 text2,text3 | |
int k; // 定义整型变量 k | |
// 为对象 text2 赋值 | |
text2="Sluice gates at Three Gorges Dam opened to discharge water."; | |
text3=text1+text2; | |
k=text3.find("Heavy"); | |
text3.erase(k, sizeof("Heavy")-1); // 删除 Heavy | |
text3.insert(k, "Strong"); | |
cout<<text3<<endl; | |
return 0; | |
} |
# 实例 4 矩阵乘法
用户输入 AM×N,BN×K 两个矩阵的元素,计算它们的乘积并输出。其中 M,N,K 也由用户输入,它们均不超过 20。
- 问题分析:
输入矩阵的元素只要根据行数、列数,使用循环即可。设 C 是矩阵的乘积,则矩阵乘法的运算公式是
- 源程序:
#include <iostream> | |
using namespace std; | |
int main(){ | |
const int M=20,N=20,K=20; | |
double A[M][N],B[N][K],C[M][K]; | |
int M1,N1,N2,K1; // 矩阵的实际维数 | |
int i,j,k; // 循环变量 | |
cout<<"请输入第1个矩阵的维数M N"<<endl; | |
cin>>M1>>N1; // 输入第 1 个矩阵的行数和列数 | |
cout<<"请按行输入第1个矩阵的元素"<<endl; | |
for(i=0;i<M1;i++) { // 输入第 1 个矩阵的元素,按行 | |
for(j=0;j<N1;j++) | |
{ // 第 i 行 | |
cin>>A[i][j]; | |
} | |
} | |
cout<<"请输入第2个矩阵的维数N K"<<endl; | |
cin>>N2>>K1; // 输入第 2 个矩阵的行数和列数 | |
while(N2!=N1) { | |
cout<<"第2个矩阵的行数应等于第1个矩阵的列数,请重输"<<endl; | |
cin>>N2>>K1; | |
} | |
cout<<"请按行输入第2个矩阵的元素"<<endl; | |
for(i=0;i<N1;i++) { // 输入第 2 个矩阵的元素,按行 | |
for(j=0;j<K1;j++) // 第 i 行 | |
cin>>B[i][j]; | |
} | |
for(i=0;i<M1;i++) { // 第 M1 个行 | |
for(j=0;j<K1;j++) { // 每行的 K1 列 | |
C[i][j]=0; //i 行 j 列元素赋初值 0 | |
for(k=0;k<N1;k++) | |
{ // 计算 i 行 j 列元素的值 | |
C[i][j]=C[i][j]+A[i][k]*B[k][j]; | |
} | |
} | |
} | |
// 输出乘积矩阵的元素 | |
for(i=0;i<M1;i++) { //M1 行 | |
for(j=0;j<K1;j++) { //K1 列 | |
cout<<C[i][j]<<"\t"; // 行中中间用 Tab 键分隔 | |
} | |
cout<<endl; // 行间换行 | |
} | |
return 0; | |
} |
# 实例 5 取子字符串
用户输入一个字符串,然后输入起始位置 k
和长度 l
。
显示从第 k
个字符开始,长度为 l
的子字符串。
要求字符串输入一次,子串操作可以多次,输入位置和长度均为 0
时停止。
运行结果:
请输入字符串(可以有空格格) the c++ programming 请输入子串起始位置和长度 1 5 the c
问题分析:
取字符就是从一个字符串中取出连续的部分字符串。
若字符串用字符数组表示,可以将从 k 到 k+l-1 的字符逐个复制到令一个字符数组中形成一个新的字符串。
特别注意在末尾加\0
。源程序:
#include <iostream> | |
using namespace std; | |
int main(){ | |
char str[101]; // 源字符串 | |
char sub[101]; // 子字符串 | |
int len; // 源字符串长度 | |
int k,l; // 子字符串起始位置,子字符串长度 | |
int i,j=0; // 循环变量 | |
// 输入源字符串 | |
cout<<"请输入字符串(可以有空格格)"<<endl; | |
cin.getline(str,100); // 输入带空格的字符串 | |
// 求字符串的长度 | |
len=0; | |
while(str[len] != '\0'){ | |
// 每遇到一个不是结束符的字符,长度就加 1 | |
len++; | |
} // 结束时,len 的值就是长度 | |
cout<<"请输入子串起始位置和长度"<<endl; | |
cin>>k>>l; // 子字符串起始位置,子字符串长度 | |
while(k!=0 && l!=0){ // 结束标志 | |
j=0; // 子串的字符下标 | |
for(i=k-1;i<k+l-1 && i<len;i++){ // 取子串 | |
sub[j]=str[i]; // 取一个字符,放入 sub 中 | |
j++; //sub 中的下一个空位 | |
} | |
sub[j]='\0'; // 子串末尾放置结束标志 | |
cout<<sub<<endl; // 输出子串 | |
cout<<"请输入子串起始位置和长度"<<endl; | |
cin>>k>>l; // 再次输入子字符串起始位置,子字符串长度 | |
} | |
return 0; | |
} |
取子串的 for 循环结束时,子串字符确实已经放在
sub
中,但这时只能叫字符数组,还不能叫字符串。
因为字符串是以\0
为结束标志的,sub[j]='\0';
就是在末尾放置结束标志,才成为字符串,才可以整体输出。
# 实例 5 词频统计
输入一系列英文单词 (单词之间用空格隔开),用 xyz
表示输入结束。
统计各单词出现的次数(单词不区分大小写),对单词按字典顺序进行排序后输出单词和词频。
运行结果:
请输入一系列英语单词,以xyz表示输入结束 Do you see the star , the little star ? xyz 词频统计结果如下: , 1 ? 1 Do 1
问题分析:
数据结构。本题中每个单词有两条信息要记录,一是单词本身,二是单词的出现次数,即使 1 次,所以可以用结构体。
查找。每输入一个单词,要在已有单词序列中查找,找到在次数加 1,找不到则添加一个新单词,次数置 1。
选择排序
先将待排序序列分成有序部分和无序部分,重复地从无序部分中找出最大的元素,放在有序部分的最后,直到无序部分只有一个元素。如果有 N 个元素要排序,这样的选择过程只需要 N-1 次。
源程序:
#define _CRT_SECURE_NO_WARNINGS | |
#include <iostream> // 包含基本输入输出库头文件 | |
#include <cstring> | |
using namespace std; // 使用名字空间 | |
struct WordList { // 字典结构体 | |
char word[20]; // 单词 | |
int freq; // 使用次数 | |
}; | |
int main() { // 主函数 | |
WordList list[1000]; // 结构体数组 | |
int N=0; // 实际单词数 | |
int i,j,k; // 循环变量;临时变量 | |
char tmp[20]; // 临时存放新输入的单词 | |
//------------- 输入单词 ------------------ | |
cout<<"请输入一系列英语单词,以xyz表示输入结束"<<endl; | |
cin>>tmp; | |
while(strcmp(tmp,"xyz")!=0) // 比较 tmp 和 `xyz` 是否相等 | |
{ // 不是单词的结束符时循环 | |
for(i=0;i<N;i++) | |
{ // 在当前词典中逐个查 | |
if(strcmp(list[i].word, tmp)==0){// 比较两个单词是否相同 | |
list[i].freq++; // 词频加 1 | |
break; // 不再查找 | |
} | |
} | |
if(i>=N) | |
{ // 这时是没有找到,添加该词 | |
strcpy(list[i].word,tmp); // 添加单词 | |
list[i].freq=1; // 词频置 1 | |
N++; // 单词数加 1 | |
} | |
cin>>tmp; // 继续输入单词 | |
} // 结束时,N 为词典中的单词数 | |
//--------------- 对词典进行排序 -------------- | |
for(i=0;i<N-1;i++) | |
{ // 控制 N-1 次选择 | |
k=i; // 先设 i 是当前最小元素的下标, | |
for(j=i+1;j<N;j++) | |
{ // 与后面的单词比较 | |
if(strcmp(list[j].word, list[k].word)<0) // 两个字符串比较大小 | |
{ | |
k=j; // 记下最小元素的下标 | |
} | |
} | |
if(k!=i) | |
{ // 最小的下标不是 i | |
WordList tmp; | |
// 交换下标是 k 和 i 的两个元素 | |
tmp=list[i]; | |
list[i]=list[k]; | |
list[k]=tmp; | |
} | |
} | |
//--------- 输出结果 ------------ | |
cout<<"词频统计结果如下:"<<endl; | |
for(i=0;i<N;i++) | |
{ // 输出 | |
cout<<list[i].word<<"\t"<<list[i].freq<<endl; | |
} | |
return 0; | |
} |
# 课堂讨论
# 一维数组使用
- 字符、字符数组、字符串的区别是什么?
- 字符:
一个单个的字符元素。
包括字母、数字、运算符号、标点符号和其他符号,以及一些功能性符号。
字符在计算机内存放,并规定相应的字符代表相应的二进制代码。
字符是数据结构中最小的数据存取单位。通常由 8 个二进制位(一个字节)来表示一个字符,但也有少数计算机系统采用 6 个二进制的字符表示形式。一个系统中字符集的大小,完全由该系统自己规定。
计算机可用字符一般为 128-256 个(不包括汉字时),每个字符进入计算机后,都将转换为 8 位二进制数。不同的计算机系统和不同的语言,所能使用的字符范围是不同的。- 字符数组:
当数组中的元素由多个字符组成时,称字符数组。是指用来存放字符数据的数组。其定义的一般形式为:char 数组名[数据长度]
。字符数组用于存放字符或字符串,字符数组中的一个元素存放一个字符,它在内存中占用一个字节。- 字符串:
字符串 String 是由数字、字母、下划线组成的一个连续的字符系列,用特殊字符\0
结尾。一般记为s="a1a2···an"(n>=0)
。它是编程语言中表示文本的数据类型。
- 数组能否整体输入和输出?
数字型数组 (整型、浮点型)) 不能整体输入或输出,字符型数组可以。
# 二维数组使用
- 还有什么算法可以实现月份转化程序?
switch 语句 ,if else 语句,字符串数组
# 随堂练习
引用一个数组元素时,数组元素的下标表达式的类型
定义一维数组时,可以不写数组长度。
一维数组的下标从 0 开始。
若有说明:
int a[3][4]={0};
则下面正确的叙述是下面关于二维数组的说法错误的是
在说明一个结构体变量时,系统分配给它的存储空间是
一个整数能直接赋值给枚举变量。
# 单元测试
已知枚举类型定义语句为:
enum Token { NAME, NUMBER, PLUS=5, MINUS, PRINT=10 };
则下列叙述中错误的是
以下各组选项中,均能正确定义二维实型数组a的选项是
以下选项中不能正确赋值的是
A
已知
char a[][20]={"beijing","shanghai","changsha"}
; 语句cout<<a[3];
得到的输出是:设有数组定义:
char array[]="China";
,则数组array
所占的空间为设有以下说明语句
struct STUDENT
{
int n;
char ch[8];
} PER;
下列正确的是:
对于结构体变量,下列说法正确的是
struct st1{int a, b; float x, y;} s1, s2;
struct st2{int a, b; float x, y;} s3, s4;
以下对枚举类型名的定义中正确的是
C,注意不需要
=
在
int a[][3]={ {1},{3,2},{4,5,6},{0} }
中,a[2][2]
的值是对二维数组的正确定义是
列数必须确定