360 likes | 518 Views
第四章 数组和字符串. 教学目标. (1) 掌握数组的定义、初始化和引用方法; (2) 了解数组在数值计算、数据统计、排序和数据检索方面的应用; (3) 了解字符串与字符数组的概念,会对于字符串进行整体输入输出。. 基本内容. 4.1 数组 4.1.1 一维数组 4.1.2 二维数组 4.2 字符数组 4.2.1 字符数组的定义、初始化和输入与输出 4.2.2 字符串处理函数 . 构造数据类型. 问题的引出: 实际应用的程序设计中,只用几个变量的情况是极少的;更多的情况是处理大批量的相同类型或不同类型的数据。
E N D
教学目标 • (1) 掌握数组的定义、初始化和引用方法; • (2) 了解数组在数值计算、数据统计、排序和数据检索方面的应用; • (3) 了解字符串与字符数组的概念,会对于字符串进行整体输入输出。
基本内容 • 4.1 数组 • 4.1.1 一维数组 • 4.1.2 二维数组 • 4.2 字符数组 • 4.2.1 字符数组的定义、初始化和输入与输出 • 4.2.2 字符串处理函数
构造数据类型 • 问题的引出: • 实际应用的程序设计中,只用几个变量的情况是极少的;更多的情况是处理大批量的相同类型或不同类型的数据。 • 相同类型数据举例:统计全校15,000学生英语4级统考成绩; • 不同类型数据举例:管理全校15,000学生学籍信息记录,每条记录信息包括:姓名、学号、出生日期、班级、各科成绩等。 • 用什么样的数据结构来描述这类应用更简洁?
4.1 数组 • 数组: 具有相同类型的数据的集合。 • 数组用数组名来标识。 • 其中的每个变量(数组元素)通过该变量在数组中的相对位置(下标)来引用。 • 特点: • 常用于处理大批量数据; • 数据特点:存在内在联系; • 数组——具有相同数据类型的变量集合; • 这些变量都有相同名字,但下标不同; • 称这些变量为数组元素; • 只有一个下标——一维数组; • 有两个下标——二维数组。
数组说明 • 与变量相同。 数据类型 数组名[整型常数表达式][……][,…..] • 例: • int array[10]; // 说明了一个有10个元素的整型数组 • double matrix[20][20]; // 说明了一个20行20列的浮点型矩阵 • 特点: • 数组名定名规则和变量名相同,遵循标识符定名规则。 • 数组名后是用方括号起来的常量表达式,不能用圆括号 • 错误用法:int a(10); • 方括号中的下标表示元素在数组中的位置。下标从0开始,必须是整型常量。
(1)一维数组存储结构 • 逻辑结构: • 由一串数据构成的向量表,每个元素的下标值确定了各元素在此数据表中的位置。 • A[5]排序次序为: A[0],A[1],A[2],A[3],A[4] • 存贮结构: • 数组元素存放为一片连续的存储单元,其元素在内存中的存放顺序与逻辑结构是一致的;即所谓“逻辑上相邻,物理上也相邻”。 • A[5]的存储结构为: A[0] A[1] A[2] A[3] A[4]
一维数组的初始化 • 在定义数组时对数组元素赋以初值。 • 例如: int a[10]={0,1,2,3,4,5,6,7,8,9}; • 如果想使一个数组中全部元素值为0,可以写成 int a[10]={0,0,0,0,0,0,0,0,0,0}; • 不能写成 int a[10]={0*10}; 即不能给数组整体赋初值(C++不允许对一个数组进行聚集操作 )。 • 在对全部数组元素赋初值时,可以不指定数组长度。 • 例如: int a[5]={1,2,3,4,5}; • 可以写成 int a[]={1,2,3,4,5}; • 在第二种写法中,花括号中有5个数,系统会据此自动定义a数组的长度为5。
例:找出一维数组中的最大数。 • 算法分析: • 假设数组中第1个元素最大,令big =array[0] • 将array [i](0<= i < n)与big进行比较, • 若array [i] < big ,i=i+1,再执行2 • 否则,令big =array [i],i=i+1,再执行2 • 循环结束,求出最大元素并输出big。
// 找出一维数组中的最大数 #include <iostream> using namespace std; int main() { int array[7]; cout<<"Please input an array with seven elements: "<<endl; for(int i=0; i<7; i++) cin>>array[i]; int big = array[0]; for(int j=0; j<7; j=j+1) if(array[j]>big) big = array[j]; cout<<"max="<<big<<endl; return 0; }
(2)二维数组存储结构 • 逻辑结构: • 二维数组恰似一张表格(或矩阵)。 • 数组元素[行号][列号] • M[3][3]具有如下逻辑结构: M[0][0] M[0][1] M[0][2] M[1][0] M[1][1] M[1][2] M[2][0] M[2][1] M[2][2] • 存贮结构: • “按自然顺序”在内存中分配存贮单元 • M数组在内存中排列如下: M00M01 M02M10 M11 M12 M20 M21 M22
二维数组初始化 • 按照二维数组元素的物理存储次序给所有或部分数组元素提供数据值 int score[3][4]={ 85,87,93,88,86,90,95,89,78,91,82,95}; • 以行结构方式提供各数据值 int score[3][4]={ {85,87,93,88}, {86,90,95,89}, {78,91,82,95} }; • C++语言允许在为二维数组初始化时省略行下标值,但列下标值不能省略。 • int matrix [ ][4]={ 85, 87, 93, 88, 86, 90, 95, 89, 78, 91, 82, 95};
max=a[0][0] for i=0 to 2 for j=0 to 3 真 假 max=a[i][j] row=i colum=j 输出:max和row,colum 例:有一个3×4的矩阵,要求编程序求出其中值最大的那个元素的值,以及其所在的行号和列号。 a[i][j]>max
#include <iostream> using namespace std; int main() { int i, j, row=0, colum=0, max; int a[3][4]={{1,2,3,4}, {9,8,7,6}, {-10,10,-5,2}}; max=a[0][0]; for (i=0;i<=2;i++) for (j=0;j<=3;j++) if (a[i][j]>max) { max=a[i][j]; row=i; colum=j;} cout<<"max="<<max<<", row="<<row<<", colum="<<colum<<endl; return 0; } 运行结果: max=10,row=2,colum=1
(3)三维数组存储结构 • 逻辑结构: • 三维数组可以看成是若干张表格(或矩阵)。 • 数组元素[页号][行号][列号] • 存贮结构: • “按自然顺序”在内存中分配存贮单元 C000、C001、C002、C010、C011、C012、C020、C021、C022、C100、C101、C102、C110、C111、C112、C120、C121、C122 C000 C001 C002 C010 C010 C012 C020 C021 C022 C100 C101 C102 C110 C110 C112 C120 C121 C122
4.2 字符数组 • 字符 • 是用单引号括起来的单个字符,它在存储器中占1个字节。例如,‘A’ • 字符串 • 是用双引号括起来的一串字符,它在存储器中占n+1个字节,即字符串的结束符‘\0‘也占1个字节的位置。例如,”abc”; • 有效字符的个数称为字符串长度。 例如, ”abc”的长度为3,但占4个字节 (‘\0‘占一位)。 • 字符串是用字符型数组存放的。
4.2.1 字符数组的定义、初始化和输入输出 • 字符数组的定义格式: char 字符数组名 [最大字符数+1] = "字符串"; • 例: char c [7]= "MONDAY"; 或 char c [7]={ "MONDAY"}; 或 char c[7]; c[0] = 'M'; c[1] = 'O'; c[2] = 'N'; c[3] = 'D'; c[4] = 'A'; c[5] = 'Y'; c[6] = '\0'; // c[6] = 0; • 例: char d [7]= "MON"; • 字符数组的元素是单个字符数据。
字符串的输入与输出 • 而和普通数组不同的是,字符串数组允许聚集操作 • 字符串的输入 char name[20]; cin>>name; • 字符串的输出 cout<<name;
4.2.2 字符串处理库函数 • C/C++提供了许多字符串处理函数。使用这些函数,可以提高字符处理的效率。 • 字符串处理函数的说明都包含在“cstring”头文件中,所以使用时需要添加#include <cstring> • stpcpy(): 字符串拷贝; • strcat(): 字符串连接; • strchr(): 在字符串中查找字符; • strcmp(): 字符串比较; • strlen(): 求字符串长度; • strlwr(): 将字符串中的大写字母转换为小写字母; • strrev(): 反转字符串; • strstr(): 在字符串中查找另一个字符串; • strupr(): 将字符串中的小写字母转换为大写字母; • … …
字符串处理函数 • (1)求字符串的长度: int strlen(char s[ ]); 例如: len = strlen("This is a sample."); 执行后,变量len会被赋值17。 • (2)字符串连接函数 strcat(char destin[ ], char source[ ]); • (3) 复制字符串 strcpy(char destin[ ], char source[ ]); 例如: char weekday[11]; strcpy(weekday, "MONDAY"); • (4) 字符串比较 int strcmp(char string1[ ], char string2[ ]);
例4-2:字符串复制函数和连接函数的用法 #include <iostream> #include <cstring> using namespace std; int main() { char s[80]; strcpy( s, "Hello world from " ); strcat( s, "strcpy " ); strcat( s, "and " ); strcat( s, "strcat!" ); cout<<s<<endl; return 0; }
例4-3:简单的口令验证系统 #include <iostream> #include <cstring> using namespace std; int main() { char pwd[50]; while(1) { cout<<"请输入口令:"; cin>>pwd; if(strcmp(pwd, "welcome")) cout<<"口令不正确,请重新输入!"<<endl; else break; } cout<<"口令正确,欢迎使用,再见。"<<endl; return 0; }
扩展阅读 • 4.3 字符串类 • 字符串类隶属于标准C++语言类库,若要在程序中使用字符串类,必须在源程序最前面包含标准C++语言类库的头文件<string>。 • 使用与基本数据类型int、char等类似,定义格式为: string 对象1, 对象2, …; • 例如: string s1, s2; //定义对象s1和s2 string s3("Monday"); //定义s3同时初始化
// 例 4-4 字符串类的运算符操作 #include <iostream> #include <string> using namespace std; int main() { string str1("Zhang3"); string str2("Li4"); string str3="Wang5"; string str4; // 字符串赋值 str4 = str1; cout << str1 << endl << str4 << endl; // 字符串连接 str4 = str2 + str3; cout << str4 << endl; str4 = str2 + " and " + str3; cout << str4 << endl; // 字符串比较 if(str3 >= str1) cout << "str3 >= str1"<< endl; else cout << "str3 < str1"<< endl; // 输入字符串 cout << "请输入一个字符串: "; string str5; cin >> str5; cout << str5 << endl; return 0; } 运行结果: Zhang3 Zhang3 Li4Wang5 Li4 and Wang5 str3 < str1 请输入一个字符串: Monday Monday
上机指导 • 4.4 基本调试方法 • 标准数据检验:用若干组已知结果的标准数据对程序进行检验 • 标准数据:要有代表性, 接近实际数据;比较简洁, 容易对其结果的正确性进行分析;对重要的临界数据也必须进行检验。 • 程序跟踪:让程序逐句执行, 通过观察和分析执行过程中数据和流程变化来查找错误 • 采用跟踪调试工具 • 传统的方法:在程序中直接设置断点、打印重要变量内容等来掌握程序的运行情况。 • 边界检查:重点检查边界和特殊情况 • 如果程序中有由if-else语句、switch语句等组成的分支结构, 应该设计相应的数据,使得分支中的每一条路径都要通过检验 例如, while(count<1000) {…} 就应该检验count等于999、1000、0 或负数等情况。 • 简化:在调试时, 有时可以通过对程序进行某种简化来加快调试速度。 • 例如减少循环次数、缩小数组规模、屏蔽次要程序段等。 • 要注意简化不能太过分, 以致于无法代表原程序的真实情况。
应用举例 • 例 4-5:使用数组方法计算斐波那挈数列问题 #include <iostream> using namespace std; int main() { int i, f[24]; f[0]=1; f[1]=1; // 构造斐波那契数列 for(i=2; i<24; i++) f[i]=f[i-1]+f[i-2]; // 输出斐波那契数列 for(i=0; i<24; i++) cout<<f[i]<<"\t"; cout<<endl; return 0; }
例4-6:使用冒泡排序法编写程序,可以对任意输入的10个整数进行从小到大的排序例4-6:使用冒泡排序法编写程序,可以对任意输入的10个整数进行从小到大的排序 • 算法分析: • 两两比较相邻元素A[i]和A[i-1] (i=N-1, N-2, … 2, 1, 0), 如果A[i]<A[i-1], 则交换它们的位置 A[i]A[i-1]; 经过N-1次比较,将最小值交换到A[0]的位置; • 对剩下的N-1个元素,再两两进行比较,按同样规则交换它们的位置,经过N-2次比较,将次最小值交换到A[1]的位置; • 如法炮制,经过N-1趟的“冒泡处理”,每趟进行N-i次的比较,全部数列有序。
#include <iostream> using namespace std; int main() { int i, j, tmp; int list[10]; cout << "请输入待排序的整数数列:"; for(i=0; i<10; i++) cin>>list[i]; for(i=0; i<10; i++) for(j=9; j>i; j--) // 每次都从最后一个元素开始比较 if(list[j-1]>list[j]) // 如果前后顺序不符,则交换位置 { tmp = list[j-1]; list[j-1] = list[j]; list[j] = tmp; } // 输出排序后的数组 cout << "排序后的整数数列为:"; for(i=0; i<10; i++) cout << list[i] << " "; cout<<endl; return 0; }
应用举例 • 例 4-7:编写程序,计算两个矩阵差。
#include <iostream> using namespace std; int main() { const int M=3; const int N=4; double a[M][N]= { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,12 }; double b[M][N]= { 1, 4, 7,10, 2, 5, 8, 11, 3, 6, 9,12 }; double c[M][N]; // 结果矩阵 cout << "矩阵a和矩阵b的差的矩阵c为:" << endl; // 两个矩阵的加减是对应矩阵元素的加减 for(int i=0; i<M; i=i+1) { for(int j=0; j<N; j=j+1) { c[i][j]=a[i][j]-b[i][j]; cout << c[i][j] << "\t"; } cout << endl; // 每输出完成一行中所有元素后换行 } return 0; }
应用举例 • 例4-8字符串连接。 // 方法0,直接调用字符串连接函数 #include <iostream> #include <cstring> using namespace std; int main() { char destination[81] = "abcdefghijklmnopqrstuvwxyz"; char source[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; strcat(destination, source); // 直接调用字符串连接函数 cout<<"连接后的字符串为:"<<destination<<endl; return 0; }
应用举例 • 例4-8字符串连接。 // 方法1,利用字符数组的结构特点 #include <iostream> #include <cstring> using namespace std; int main() { char destination[81] = "abcdefghijklmnopqrstuvwxyz"; char source[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; int i = strlen(destination); // 求出目标字符串长度,以确定复制位置 int j = 0; while(source[j]!='\0') // 在未达到源字符串尾部前逐个字符复制 destination[i++] = source[j++]; destination[i] = '\0'; // 在目标字符串的结尾处添加结束标志 cout<<"连接后的字符串为:"<<destination<<endl; return 0; }
应用举例 • 例4-9 恺撒(替换)加密法 • 加密规则: • 将每个字母用字母表中排在其后面的第3个字母的大写形式来替换(如字母d或D就用G来替换),对于字母表中最后的三个字母,可将字母表看成是首尾衔接的(如字母y或Y用B来替换);字符串中其他非字母符号不做改变。 • 使用该方法编写一个字符串加密程序。
// 例4-9:恺撒(替换)加密 #include <iostream> #include <cstring> using namespace std; int main() { char str[50] = "I love you"; cout<<"加密前的字符串是:"<<str<<endl; int i=0; while(str[i]!='\0') { if(str[i] >= 'a' && str[i] <= 'z') str[i] = str[i]-'a'+'A'; // 小写转换为大写 if(str[i] >= 'A' && str[i] <= 'Z') str[i] = (str[i]+3-'A')%26+'A'; // 按凯撒加密法替换字母 i++; } cout<<"加密后的字符串是:"<<str<<endl; return 0; }
结 束 语 学好程序设计语言的唯一途径是 你的编程能力与你在计算机上投入的时间成 上机练习 正比