以下为个人学习笔记和习题整理
课程:计算机程序设计(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 中哪个下标的元素对应?

123
456

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

  • 什么是冒泡排序?

    1. 两两比较相邻元素 A(I)A(I+1) (I=1,2,…N-1), 如果 A(I) > A(I+1) ,则交换 A(I)A(I+1) 的位置;
    2. 对剩下的 N-1 个元素,再两两进行比较,按同样规则交换它们的位置,经过 N-2 次比较,将次最大值交换到 A(N-1) 的位置;
    3. 如法炮制,经过 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; // 函数返回
}
  • 程序分析
  1. 注意程序中带短划线的三段注释,基本体现了本程序的三块内容,输入、处理和输出。
  2. 排序的程序主要有两个循环,外层循环控制 N-1 趟,内层循环控制一趟的若干次比较。
    第 1 趟需要 N-1 次比较,第 2 次需要 N-2 次比较,第 N-1 次需要 N-(N-1)=1 次比较。
  3. 元素存放在数组中,数组的大小在定义时要求是常量表达式。不能先输入 N ,再定义数组 double A[N] ,这是不正确的。

# 实例 2 文字信息统计

用户输入一段文本(英文),统计其字符总个数,大写字母个数、小写字母个数、数字个数及其他字符个数。

  • 运行结果:

    C++ Programming
    字符串总长度:15
     大写字母:2
     小写字母:10
     数字个数:0
     其他字符:3
    
  • 问题分析:

  1. 输入字符串。

    cin>> <字符数组名>

    只能输入单词;
    因为 >> 以空格、tab 键、回车为分隔符,遇到空格认为是一项数据的结束。
    cin 是输入流对象,它有一个成员函数 getline() 可以读取带空格的一行字符串。
    基本使格式为:

    cin.getline(<字符数组名>,<字符长度>);
    例如
    char sentence[100]; // 字符数组大小 100
    cin.getline(sentence,99);
    // 可以输入最长 99(回车结束)的字符串,中间可以有空格。
  2. 统计计数。
    输入的文本看做字符串,统计字符个数,就是从字符串的第 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 及以上版本中调用 strcpystrcat 等函数时,由于安全原因会提示警告。
可以在程序开始的位置加上 #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 是矩阵的乘积,则矩阵乘法的运算公式是

Cij=k=1Naik×bkj,i=1,M,j=1,KC_{ij} = \displaystyle\sum_{k=1}^N a_{ik} × b_{kj}, i=1,\mathellipsis M, j = 1,\mathellipsis K

  • 源程序:
#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 次,所以可以用结构体。

    2. 查找。每输入一个单词,要在已有单词序列中查找,找到在次数加 1,找不到则添加一个新单词,次数置 1。

    3. 选择排序
      先将待排序序列分成有序部分和无序部分,重复地从无序部分中找出最大的元素,放在有序部分的最后,直到无序部分只有一个元素。如果有 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;
}

# 课堂讨论

# 一维数组使用

  1. 字符、字符数组、字符串的区别是什么?
  • 字符:
    一个单个的字符元素。
    包括字母、数字、运算符号、标点符号和其他符号,以及一些功能性符号。
    字符在计算机内存放,并规定相应的字符代表相应的二进制代码。
    字符是数据结构中最小的数据存取单位。通常由 8 个二进制位(一个字节)来表示一个字符,但也有少数计算机系统采用 6 个二进制的字符表示形式。一个系统中字符集的大小,完全由该系统自己规定。
    计算机可用字符一般为 128-256 个(不包括汉字时),每个字符进入计算机后,都将转换为 8 位二进制数。不同的计算机系统和不同的语言,所能使用的字符范围是不同的。
  • 字符数组:
    当数组中的元素由多个字符组成时,称字符数组。是指用来存放字符数据的数组。其定义的一般形式为: char 数组名[数据长度] 。字符数组用于存放字符或字符串,字符数组中的一个元素存放一个字符,它在内存中占用一个字节。
  • 字符串:
    字符串 String 是由数字、字母、下划线组成的一个连续的字符系列,用特殊字符 \0 结尾。一般记为 s="a1a2···an"(n>=0) 。它是编程语言中表示文本的数据类型。
  1. 数组能否整体输入和输出?

数字型数组 (整型、浮点型)) 不能整体输入或输出,字符型数组可以。

# 二维数组使用

  1. 还有什么算法可以实现月份转化程序?

switch 语句 ,if else 语句,字符串数组

# 随堂练习

  1. 引用一个数组元素时,数组元素的下标表达式的类型

    • 必须是整型常量
    • 必须是整型表达式
    • 必须是整型常量或整型表达式
    • 可以是任何类型的表达式
  2. 定义一维数组时,可以不写数组长度。

  3. 一维数组的下标从 0 开始。

  4. 若有说明: int a[3][4]={0}; 则下面正确的叙述是

    • 只有元素 a[0][0] 可得到初值 0
    • 此说明语句不正确
    • 数组 a 中各元素都可得到初值,但其值不一定为 0
    • 数组 a 中每个元素均可得到初值 0
  5. 下面关于二维数组的说法错误的是

    • 同行的数据在内存中连续存放。
    • 对全部数组元素都赋初值时,可以不指定数组第二维的长度。
    • 不能将二维数组作为一个整体输入与输出。
    • 将二维数组看作是一维数组时,该一维数组的每个元素本身又是一个一维数组。
  6. 在说明一个结构体变量时,系统分配给它的存储空间是

    • 该结构体中第一个成员所需的存储空间
    • 该结构体中最后一个成员所需的存储空间
    • 该结构体中占用最大空间的成员所需的存储空间
    • 该结构体中所有成员所需存储空间的总和
  7. 一个整数能直接赋值给枚举变量。

# 单元测试

  1. 已知枚举类型定义语句为:

    enum Token { NAME, NUMBER, PLUS=5, MINUS, PRINT=10 };

    则下列叙述中错误的是

    • 枚举常量 PRINT 的值为 10
    • 枚举常量 NUMBER 的值为 1
    • 枚举常量 MINUS 的值为 6
    • 枚举常量 NAME 的值为 1
  2. 以下各组选项中,均能正确定义二维实型数组a的选项是

    • float a(3,4); float a[3][4]; float a[][]={ {0},{0} };
    • float a[3][4]; float a[][4]={ {0},{0} }; float a[][4]={ {0},{0},{0} };
    • float a[3][4]; float a[][4]; float a[3][]={ {1},{0} };
    • float a[3][4]; float a[3][]; float a[][4];
  3. 以下选项中不能正确赋值的是

    • char s4[7] = {"cbest\n"};
    • char s3[10] ="cbest";
    • char s1[10];s1="cbest";
    • char s2[]={'c', 'b', 'e', 's', 't'};

    A

  4. 已知 char a[][20]={"beijing","shanghai","changsha"} ; 语句 cout<<a[3]; 得到的输出是:

    • 输出结果不确定
    • changsha
    • j
    • 数组定义有错
  5. 设有数组定义: char array[]="China"; ,则数组 array 所占的空间为

    • 6 个字节
    • 4 个字节
    • 5 个字节
    • 7 个字节
  6. 设有以下说明语句

    struct STUDENT 
    {
    	int n;
    	char ch[8];
    } PER;

    下列正确的是:

    • struct STUDENT 是结构体类型
    • PER 是结构体类型名
    • struct 是结构体类型名
    • PER 是结构体变量名
  7. 对于结构体变量,下列说法正确的是

    struct st1{int  a, b;  float  x, y;} s1, s2;
    struct st2{int  a, b;  float  x, y;} s3, s4;
    • 结构体变量不可以整体赋值
    • s1、s2、s3、s4 之间均不可以相互赋值
    • s1、s2、s3、s4 可以相互赋值
    • 只有 s1 和 s2、s3 和 s4 之间可以相互赋值
  8. 以下对枚举类型名的定义中正确的是

    • enum a {"one", "two", "three"};
    • enum a {one=9,two=-1,three};
    • enum a={one, two, three};
    • enum a={"one", "two", "three"};

    C,注意不需要 =

  9. int a[][3]={ {1},{3,2},{4,5,6},{0} } 中, a[2][2] 的值是

    • 4
    • 6
    • 2
    • 3
  10. 对二维数组的正确定义是

    • int a[2,3]={1,2,3,4,5,6};
    • int a[][]={1,2,3,4,5,6};
    • int a[2][]={1,2,3,4,5,6};
    • int a[][3]={1,2,3,4,5,6};

    列数必须确定

阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Ruri Shimotsuki 微信支付

微信支付

Ruri Shimotsuki 支付宝

支付宝

Ruri Shimotsuki 贝宝

贝宝