第5章数组 前面章节中使用的数据都属于简单数据类型,使用单一的存储单元来存储一个变量,如整型、浮点型、字符型。但是在实际应用中,常常需要处理大量同类型的相关数据,如1000个学生的成绩等,若定义1000个简单类型的变量肯定是不合适的。类似这样的数据在C语言中可以通过数组来处理。 本章主要介绍一维数组和二维数组的定义及使用,其中着重介绍一维数组,因为一维数组的使用更加频繁。同时,考虑到字符型数组与数值型数组在某些使用方式上会有区别,因此,对字符数组和字符串将单独进行介绍。 5.1一 维 数 组 一维数组的 定义与引用 数组是含有多个数据项的数据结构,这些数据项称为数组元素,同一数组中的每个元素都属于同一个数据类型,可以根据元素在数组中所处的位置把它们一个个区分开来。 一个数组是具有相同类型的数据项的集合。 5.1.1一维数组定义 最简单的数组类型就是一维数组。假设有10个学生的成绩需要处理,可以定义一个长度为10的一维数组: int score[10]; 编译器将10个存储单元与数组名score相关联,这些存储单元在内存中是相邻的,每一个单元都可以存储一个int型的数据,见图51。 图51有10个元素的一维整型数组示意图 与其他变量一样,数组也需要“先定义后使用”。为了定义数组,需要指明数组元素的类型和个数,数组元素的类型可以是C语言允许的任何类型,数组元素的个数可以用任何整数类型的常量表达式来指定。 一维数组的定义形式见表51。 表51一维数组的定义形式 语法 类型标识符 数组名[数组长度]; 示例 int score[10];//定义一个长度为10的整型数组 说明(1) 数组名的命名规则和变量名相同,遵循标识符的命名规则。 (2) 方括号内的数组长度表示元素个数。注意不是圆括号,如int a(10)是错误的。 (3) 数组长度是整型的,可以是常量(表达式)或符号常量,不能是变量 C语言程序设计·在线实践·微课视频 第 5 章 数组 C语言不允许对数组的大小做动态定义,如以下语句是错误的。 int len; scanf("%d",&len); //企图临时输入数值作为数组大小 int a[len]; //用变量len作为数组长度,为错误的应用 C99中,数组的长度可以用不是常量的表达式指定。前提是数组不具有静态存储期限且数组定义时未进行初始化。 一个数组中包含多个数组元素,为了存取特定的某个数组元素,可以在数组名的后面加一个用方括号括起来的整型数值来表示具体的数组元素,这种引用数组元素的方法称为“下标法”,即: 数组名[下标] 假设a数组的长度为10,则其各个元素用下标法表示如图52所示,分别为a[0]、a[1]、a[2]、…、一直到a[9]。 图52下标法表示一维数组中各个元素 需要特别注意的是,C语言中,对数组元素引用时的下标是从0开始,而不是从1开始。即长度为N的数组,其第1个元素用a[0]表示,第2个元素用a[1]表示,……,第N个元素用a[N-1]表示。 数组元素的作用相当于一个同类型的简单变量,因此,该类型的简单变量能进行的运算,数组元素也能进行。例如: int score[10]; score[0]=80; //给下标为0的元素赋整数值 score[1]=score[0]; //将score[0]元素的值赋给score[1]元素 需要注意,定义数组时用到的“数组名[整型常量表达式]”和引用数组元素时用到的“数组名[下标]”从形式上看是相同的,但其实含义不同,例如: int score[10]; //这里的score[10]表示一个叫score的数组,其包含10个元素 temp=score[9]; //这里的score[9]表示引用score数组中序号为9的那个元素,即第10个元素 数组下标: 在数组名后的方括号内的数值或表达式,用于区分数组中的不同元素。 长度为N的数组,其各个元素的下标应该是从0到N-1,而不是从1到N。 5.1.2一维数组初始化 在定义数组的同时,可以给各数组元素赋值,这称为数组的初始化。数组初始化的形式有多种,下面通过几个简单的例子进行示范。 【例51】给全部数组元素赋初值。某学生期中考试4门课程的成绩分别为88,91,80,79,求其本次考试的平均成绩。 #include int main() { float a[4]={88,91,80,79}; //给全部数组元素赋初值 float sum=0,ave=0; sum=a[0]+a[1]+a[2]+a[3]; //求总分 ave=sum/4; //求平均成绩 printf("Average=%.1f\n",ave); return 0; } 运行结果: Average=84.5 图53全部元素初始化后 一维数组中各元素的取值 本例采用在定义数组时“给全部元素赋初值”的形式初始化数组,语句: float a[4]={88,91,80,79}; 将数组元素的值按顺序放在一对大括号内,经过这样的初始化后,a数组各元素的取值如图53所示。 对于数值型数组而言,只能给数组元素逐个赋值,而不能给数组整体赋值。比如要对长度为4的数组中所有元素都赋值3,则应写成: int a[4]={3,3,3,3};//给每个元素都赋值3 而不能贪图方便写成: int a[4]={3};//本例仅表示给a[0]元素赋值3,而其他三个元素赋值0 也不能写成: int a[4]=3;//这是错误的数组初始化形式 【例52】给部分数组元素赋初值。求某学生4门课程的平均成绩,并将平均成绩放置在数组的最后一个元素中。 #include int main() { float a[5]={88,91,80,79}; //给前4个元素赋初值 float sum=0; sum=a[0]+a[1]+a[2]+a[3]; //计算总成绩 a[4]=sum/4; //计算平均成绩并存储到数组的最后一个元素a[4]中 printf("Average=%.1f\n",a[4]); return 0; } 本例的运行情况与例51相同。本例的数组定义及初始化语句如下。 float a[5]={88,91,80,79}; 此处将学生4门课程成绩和平均分都放在同一个数组中,因此数组的长度应为5。其中,4门课程的成绩是已知的,而平均分暂时未知, 图54部分元素初始化后 一维数组中各元素的取值 可见数组前4个元素初值是可以确定的,而最后一个元素的初值不能确定,因此对数组中的“部分元素赋初值”,初始化后各元素取值情况如图54所示。 这时候,未初始化的元素a[4]的值为0,当程序后面部分的语句计算出平均值,并将平均值赋给a[4]元素后,a[4]元素才获得更新后的值。 在定义数值型数组时,指定了数组长度并对其初始化,将按照大括号内的次序依次对数组中的各元素初始化,而未被赋初值的元素系统自动将它们初始化为0。 若要对所有数组元素赋初值为0,可以采用如下方式。 int a[10]={0,0,0,0,0,0,0,0,0,0}; 或 int a[10]={0}; 除了前面提到的“给全部元素赋初值”和“给部分元素赋初值”以外,还有一种初始化的方式,即在对全部数组元素初始化时,由于数据个数已确定,因此可以不指定数组长度。例如,在例51中的语句: float a[4]={88,91,80,79};//给全部数组元素赋初值 可以写成: float a[]={88,91,80,79};//给全部数组元素赋初值 在第二种写法中,大括号内有4个数,此时,虽然没有在前面的方括号中指定数组长度,但系统会根据大括号中数据的个数确定a数组有4个元素。但是,如果数组实际长度与提供初值的个数不相同,则方括号内的数组长度不能省略。例如,想定义一个长度为10的数组,其中有5个数值是已知的,则写成如下的语句是错误的。 float a[]={80,90,80,70,60};//该数组实际长度只有5 必须写成: float a[10]={80,90,80,70,60};//定义一个长度为10的数组,只初始化前5个元素,后5个 //元素为0 在定义数组的同时进行初始化时,尽量在方括号内给出数组长度,而不要省略长度的描述。 如果定义数组时没有给数组元素初始化,数组元素的值是一个不确定的数据。 5.1.3用循环结构存取数组 5.1.2节通过初始化的方式使数组元素获得值。但是,在实际应用中,数组元素的值往往需要通过交互方式获得,因此,在定义数组时进行初始化的方式并不是很实用,更多的是通过输入语句对数组中的元素赋值。 考虑到数组中的元素都是依次存放的,下标也是有规律可循的,因此可以结合循环语句来存取数组元素。 【例53】一维数组基本练习。(nbuoj1149) 已知某学生期中考试4门课程的成绩,请将这4个成绩存放到数组中,然后计算其本次考试的平均成绩并输出,输出保留1位小数。 #include int main() { float score[4]; //数组长度为4,可存放4个成绩 int i; float sum=0; for(i=0;i<4;i++) //用循环处理,控制数组元素下标从 0到3进行变化 { scanf("%f",&score[i]); //输入一个成绩,保存到数组元素score[i]中 sum+=score[i]; //每次输入的成绩及时累加到sum变量 } printf("%.1f\n",sum/4); return 0; } 运行结果: 80 80 90 90 85.0 变量i是元素下标的计数器,它决定在每次循环过程中要操作数组的哪个元素。通常使用循环控制变量来担任这一角色,因为循环控制变量自增1更新以后,下一个数组元素就被自动选中了。 C语言不要求检查下标的范围,当下标超出范围时,编译器不会给出错误信息,程序也能正常运行,但会读取一些非法的内存空间从而得到错误的结果。 下标超出范围的主要原因是忽略了长度为N的数组其元素的下标是从0到N-1,而不是从1~N,因此在使用过程中常会出现以下两种错误情况。 (1) 将数组元素a[1]作为数组的起点,而忽略了元素a[0]。 (2) 将数组的最后一个元素的下标误认为是N。 错误形式见例54。 【例54】一维数组逆序显示。(nbuoj1155) 输入10个整数保存到数组中,再逆序显示这10个数据。以下代码是错误的。 #include int main() { int a[10],i; for(i=0;i<10;i++) //输入10个元素,下标从0到9 scanf("%d",&a[i]); for(i=10;i>=0;i--) //下标出错,误引用a[10]元素了 printf("%d ",a[i]); printf("\n"); return 0; } (错误的) 运行结果: 1 2 3 4 5 5 4 3 2 1 1703792 1 2 3 4 5 5 4 3 2 1 本例在输出语句中误用了元素下标,对长度为10的数组出现了下标为10的元素,即a[10],这种情况下编译不会出错,程序也能运行,但a[10]元素的值不是我们期望的内容,如果将该元素列入计算范围的话,就会对结果造成影响。正确的做法是将输出语句改成如下形式。 for(i=9;i>=0;i--) //长度为10的数组,元素下标只能是从0到9 printf("%d ",a[i]); 下面再看一个用数组解决斐波那契问题的例子。 【例55】斐波那契数列。(nbuoj1125)。 输入一个整数n(1≤n≤12),输出斐波那契数列的前n项。 #include #define N 12 int main() { int f[N]={1,1}; //对最前面两个元素f[0]和f[1]初始化为0 int n,i; scanf("%d",&n); for(i=2;i int main() { int key,num[10],i; int flag=0; //flag标记查找是否成功,初值0 for(i=0;i<10;i++) //输入10个数据存入数组 scanf("%d",&num[i]); scanf("%d",&key); //输入一个待查找的值存入key变量 for(i=0;i<10;i++) //顺序查找 { if(key==num[i]) //如找到相同值 { printf("%d\n",i); //输出该数组元素下标 flag=1; //将标记flag置1,说明查找成功 break; //可提前退出循环 } } if(flag==0) //若查找失败,则输出对应提示信息 printf("not found\n"); return 0; } 运行结果: 6 70 -9 80 83 54 3 88 10 2 80 3 【例57】最大数和最小数。(nbuoj1874) 输入任意10个整数,从中找出最大数值和最小数值并输出。 本题要求在10个数据间进行比较,分别求出一个最大数值和一个最小数值并输出。首先假设数组中的第一个元素(下标为0)为最大值(最小值),然后从第二个元素(下标为1)开始逐一比较,看后面是否有更大(更小)的元素值。具体代码如下。 #include int main() { int s[10],i,min,max; //max变量存放最大数值,min变量存放最小数值 for(i=0;i<10;i++) scanf("%d",&s[i]); max=s[0]; //假设数组首元素为当前最大值,存入max变量 min=s[0]; //假设数组首元素为当前最小值,存入min变量 for(i=1;i<10;i++) //从下标为1的元素开始逐一比较 { if(maxs[i]) min=s[i]; //寻找最小值 } printf("%d\n%d\n",max,min); return 0; } 运行结果: 1 2 5 4 7 8 3 54 13 20 54 1 顺序查找对于数据没有要求,可以是未排序的数列,也可以是已排序的数列。 2. 二分查找 二分法查找法是在已经排好序的数中查找,并不需要将每个数据都和待查数比较,而是采用以下的方法(假设待查数据为key,并且数据已经按从小到大顺序排列),即每次用key与位于查找区间中央位置的元素进行比较,比较结果将会有以下三种情况。 (1) 如果相等,说明查找成功。 (2) 如果key<中央位置元素,则如果有解的话,解应该位于查找区间的左半部分。此时将查找区间缩小为原来的一半(即左半部分),并在这一半的区间中继续用相同的方法查找。 (3) 如果key>中央位置元素,则如果有解的话,解应该位于查找区间的右半部分。此时将查找区间缩小为原来的一半(即右半部分),并在这一半的区间中继续用相同的方法查找。 可以看出,用key与当前查找区间的中央位置元素比较后,要么找到数据key,要么将查找区间缩小一半。如果查找区间不存在了依然没找到key,则说明该数据序列中不存在key。 【例58】有序数组的查找。(nbuoj1158) 输入10个升序排列的整数(假设没有重复数值)存入一个数组中,然后再输入一个待查找的数据key,查找数组中是否存在值为key的数组元素。如果有,则输出相应的下标,否则输出not found。 #include #define N 10 int main() { int sucess=0; //用sucess来标记是否查找成功 int location; //标识找到的数值的下标 int a[N],i; int low,high,mid,key; for(i=0;i<10;i++) //输入10个数 scanf("%d",&a[i]); scanf("%d",&key); //输入待查找数 //二分查找法 low=0; high=N-1; while(low<=high) { mid=(low+high)/2; if(key==a[mid]) {sucess=1;location=mid;break;} if(keya[mid],因此执行“low=mid+1;”,将查找区域缩小到右半区,并重新计算mid的值,见图55(b)。 此时key==a[mid],查找成功,该数值在数组中的下标为7。 图55二分查找示意图 对于大小随机排列的数列,适合用顺序法查找,对于已经按大小排序的数列,用二分查找法效率更高。 5.1.5一维数组的删除 要执行删除操作,首先要查找待删除的元素是否存在,若存在,则执行删除操作; 否则报出错信息。 数组确定以后,其各个元素的空间也是确定的,不能“抹”去某一个元素空间,同时,元素空间中的数据也不能“撤销”,只能更新。因此,对数组元素的删除一般采用以下方法: 将待删除数据后面的所有数据元素依次向前移,覆盖被删除的那个元素,这就相当于删除操作了。见例59。 【例59】一维数组的删除。(nbuoj1154) 有5个整型数据存储在数组中,再输入一个数值key,删除数组中第1个等于key的元素。如果key不是该数组中的元素,则显示not found。 #include #define N 5 int main() { int a[N]; int i,j,key; for(i=0;i #define N 5 int main() { int a[N+1]={0}; //至少多留一个数组空间,以便插入新的元素 int i,j,key; for(i=0;i=i;j--) //元素依次后移,腾出空位 a[j+1]=a[j]; a[i]=key; //在下标为i的位置插入元素 } for(i=0;i #define N 10 int main() { int a[N]; int i,j,maxloc,temp; for(i=0;ia[maxloc]) maxloc=j; if(maxloc!=i) {//若最后选出的最大值不是原来假设的待排序区域的第一个数,则对这两个数进行交换 temp=a[i];a[i]=a[maxloc];a[maxloc]=temp; } } for(i=0;i #define N 10 int main() { int a[N]; int i,j,temp; for(i=0;i #define M 3 //表示有三位学生 #define N 3 //表示有三门课程 int main() { int i,j; float score[M][N]={{95,68,78},{65,77,88},{94,82,73}}; //按行分段初始化 float sum[M]={0},ave[M]={0}; //表示每位同学的总分和平均分 for(i=0;i #define M 3 //表示有三个学生 #define N 4 //列数增加到4,最后一列表示平均分 int main() { int i,j; float score[M][N]={{95,68,78},{65,77,88},{94,82,73}}; //只对每行前3列元素初始化,其余自动为0 float sum[M]={0}; //每位同学的总分 for(i=0;i #define max 100 //最大维数 int main() { int i,j; int a[max][max]; int n; //n表示实际维数 scanf("%d",&n); //输入一个整数表示当前需要的实际维数 for(i=0;i #define N 15 //最大行数 int main() { int a[N][N],n; int i,j; scanf("%d",&n); //实际行数 for(i=0;i int main() { char str[5]={'F','r','a','n','k'};//将字符常量依次赋给字符数组中的各元素 int i; for(i=0;i<5;i++) printf("%c",str[i]); //逐个输出字符数组的元素 printf("\n"); return 0; } 运行结果: Frank 单个字符的输入输出,用格式控制符%c。 字符常量外面的单引号只是在书写代码时起到界定符的作用,在存储或输出时都不会显示。 图518逐个字符赋给数组中的各元素 本例的字符数组str有5个数组元素,大括号内也有5个字符常量。初始化后,str数组的存储情况如图518所示,5个字符常量依次赋给了str[0]到str[4]的5个数组元素。 用初始化列表时要注意以下三种情况。 (1) 若大括号内的初值个数(即字符个数)大于数组长度,编译时将出现语法错误。 (2) 若大括号内的初值个数小于数组长度,则将这些字符依次赋给数组中前面的那些元素,其他元素自动赋空字符'\0'(空字符在字符数组中有特殊含义,作为字符串结束标志)。例如,将上例中的初始化语句改为: char str[8]={'F','r','a','n','k'}; 则str存储情况如图519所示,共有8个数组元素,前5个数组元素存储了有效的字符内容,后3个数组元素自动为空字符。 图519字符个数小于数组长度的初始化 (3) 若大括号内的初值个数与预定的数组长度相同,则在定义时可以省略数组长度,系统会自动根据初值字符的个数来决定数组大小,例如: char str[ ]= {'F','r','a','n','k'}; 此时系统将数组str的长度自动定义为5。 可以定义和初始化一个二维字符数组,见例518。 【例518】用初始化列表对二维字符数组逐个元素初始化。 #include int main() { char tri[3][5]={{' ',' ','A'}, {' ','A','A','A'}, {'A','A','A','A','A'}}; int i,j; for(i=0;i<3;i++) { for(j=0;j<5;j++) printf("%c",tri[i][j]); printf("\n"); } return 0; } 运行结果: A AAA AAAAA 本例通过对二维字符数组的初始化,最后输出一个由大写字母A组成的三角形。 2. 用字符串常量初始化 用逐个字符赋给数组中的各元素初始化字符数组的形式,虽然比较清晰,但实际使用时很不方便,每个字符常量书写时都要加单引号,是一件比较烦琐的事情。C语言中对于字符数组的初始化,除了逐个元素赋值以外,还允许用字符串常量进行初始化。 字符串常量是由双引号括起来的字符序列,如"Hello"或空字符串""。无论双引号内是否包含字符,包含多少个字符,都代表一个字符串常量。 为了便于确定字符串长度,C编译器会自动在字符串末尾添加一个“字符串结束标志”,即一个ASCII码值为0的空字符('\0'),空字符是一个不可显示的字符,只作为字符串结束的标志。 由于字符串常量具有以'\0'结尾的特性,因此当用字符串常量初始化字符数组时,就会出现特别的地方,见例519。 【例519】用字符串常量对字符数组初始化。 #include int main() { char str[6]={"Frank"};//用字符串常量初始化字符数组 int i; for(i=0;i<5;i++) printf("%c",str[i]); printf("\n"); return 0; } 运行结果: Frank 本例用字符串常量"Frank"初始化字符数组,书写上简便了许多,而且赋值的内容也显得更加直观。 细心的读者可能会发现这里字符数组str的长度变成了6,而不是5。请注意,这决不是一个可有可无的改变,如果用字符串常量来初始化字符数组的话,字符数组的长度一定要至少比字符串的有效长度加1。例如,"Frank"字符串常量的有效长度为5,则存储它的字符数组的长度至少要为6。 原因还是前面提到的字符串常量以'\0'结尾的这一特性。当以字符串常量对字符数组初始化时,系统会自动在最后面添加'\0'作为字符串的结束标志,此时str数组的存储情况将如图520所示。 图520用字符串常量初始化数组 其中,str[0]到str[4]存储的是有效字符,另外还需要str[5]来存储字符串结束标志,因此,虽然字符串"Frank"的有效字符只有5个,但是存储它的字符数组的长度至少需要6个。 如果用以下形式来初始化: char str[5]={"Frank"};//错误 这是错误的,因为字符串"Frank"至少需要6B的存储单元,而数组的长度为5,无法存储字符串结束标志,从而导致系统无法处理该字符串。 用字符串常量初始化字符数组的话,还有更简洁的书写形式,即可以省略大括号,直接写成如下形式。 char str[6]= "Frank"; C语言中,字符串以空字符'\0'作为结束标志,空字符在输出时不会被显示出来。 C语言并不要求所有的字符数组的最后一个字符都必须是'\0',如“char str[5]={'F','r','a','n','k'};”也是正确的。但如果用字符串常量的形式初始化字符数组,系统必将在字符串后面自动加'\0'。 对于有效字符个数为n的字符串,其占用内存为n+1个字符所占空间大小。 【例520】计算字符串的有效长度,并输出该字符串。 #include int main() { int i,len=0; char str[20]="Programming C"; //用字符串常量初始化字符数组 for(i=0;str[i]!='\0';i++) //若str[i]不等于'\0'则继续循环 len++; //计算字符串有效长度的计数器增1 printf("String is:"); for(i=0;i int main() { char s[5]; int i; for(i=0;i<5;i++) //输入5个字符到s数组 scanf("%c",&s[i]); for(i=0;i<5;i++) //输出s数组中的5个字符 printf("%c",s[i]); printf("\n"); return 0; } 运行结果: hello hello 用getchar()和putchar()来处理单个字符也是可以的,如本例的输入/输出部分可改写为: for(i=0;i<5;i++) //输入5个字符到s数组 s[i]=getchar(); for(i=0;i<5;i++) //输出s数组中的5个字符 putchar(s[i]); 通过控制个数来输入/输出单个字符的方法在实际使用中很不方便,容易出错,一般不建议采用这种方式。 2. 整个字符串输入/输出 可以用scanf()/printf()函数按%s格式控制整个字符串的输入/输出,但是要注意,scanf()函数接收的字符串中不可以包含空格。 【例522】从键盘输入一个字符串(不带空格),统计其中的数字字符有多少个。 #include int main() { char str[20]; int i=0,count=0; scanf("%s",str); //输入一行字符,不带空格,以回车结束 while(str[i]!='\0') { if(str[i]>='0'&&str[i]<='9') count++; //统计数字字符个数的计数器 i++; } printf("String is:"); printf("%s",str); //整个字符串输出,到结束标志'\0'则认为字符串结束 printf("\nDigit=%d\n",count); return 0; } 运行结果: Hello007 String is:Hello007 Digit=3 在语句“scanf("%s",str);”中,地址表部分是字符数组名,不需要再加取地址符号“&”,因为在C语言中数组名代表该数组的起始地址。 在语句“printf("%s",str);”中,输出项也是字符数组名,而不是某个数组元素的名字。 scanf()函数按%s格式输入字符串时,如果以空格、回车、制表符(Tab)作为间隔符,则不能得到完整的输入内容。见例523。 【例523】字符串输入/输出。(nbuoj1088) 输入任意长度的字符串(小于100个字符),以换行结束,并输出。 #include int main() { char str[100]; scanf("%s",str); printf("%s",str); putchar('\n'); return 0; } 运行结果: Hello Boy Hello 程序运行时从键盘输入“Hello Boy”,但实际上只接收空格前的"Hello"存入str数组,并在后面增加一个'\0',而把空格后面的内容“Boy”丢弃了,见图521。 图521str数组的存储情况 用scanf()函数按%s格式输入字符串时,遇到空格、回车、制表符,系统认为字符串输入结束。 本题用scanf()按%s格式来输入字符串,只能接收不带空格的字符串,如果要接收带空格的字符串则需要用到下面介绍的gets()函数。 3. 用字符串处理函数gets()和puts()进行输入/输出 C语言提供的字符串处理函数gets()可以接收带空格的字符串。gets()函数的作用是从终端输入一个字符串到字符数组,其调用形式见表56。 表56gets()函数的调用形式 语法示例说明 gets(字符数组); char str[100]; gets(str); (1) 从终端输入一个字符串到字符数组,输入正确时,返回值为字符数组的起始地址; 输入失败时,返回NULL指针。 (2) 输入的字符串以换行符'\n'为结束标记。在向字符数组赋值时,自动将'\n'转换成'\0',作为字符串的结束标记 gets()函数一次只能输入一个字符串,不能写成gets(str1,str2)。 gets()函数接收的字符串中可以包含空格。 因此,例523的代码改写为: char str[100]; gets(str); //用gets()函数可读取带空格的字符串 printf("%s",str); putchar('\n'); 就可以接收带空格的字符串。 puts()函数的作用是将一个字符串(以'\0'结束的字符序列)输出到终端,其调用形式见表57。 表57puts()函数的调用形式 语法示例说明 puts(字符数组); char str[100]; … puts(str); (1) 将一个字符串(以'\0'结束的字符序列)输出到终端。 (2) 在输出时将字符串结束标记'\0'转换成'\n',即输出字符串后自动换行 用puts()函数输出的字符串中可以包含转义字符。例如: char str[30]= "Hello\nNice to meet you"; 输出为: Hello Nice to meet you 【例524】字符变换。(nbuoj1057) 输入任意一个字符串(长度小于等于1000),将字符串中的大写英文字母转换成对应的小写英文字母,而将小写英文字母转换成对应的大写英文字母,其余字符不变。输出转换后的字符串。 #include #include int main() { char str[1001]; int i=0; gets(str); //输入一个字符串,以换行结束 while(str[i]!='\0') { if(str[i]>='a'&&str[i]<='z') str[i]=str[i]-32; else if(str[i]>='A'&&str[i]<='Z') str[i]=str[i]+32; i++; } puts(str); //输出字符串 return 0; } 运行结果: hELLO bOY! Hello Boy! puts()函数的作用等同于printf("%s\n",字符数组名)。不过puts()函数一次只能输出一个字符串,而printf()函数可以一次输出多个字符串,如printf("%s %s\n",字符数组名1,字符数组名2)。 gets()函数在读取字符串时将删除结尾的换行符'\n',而puts()函数在写字符串时将在结尾添加一个换行符。 5.4.4字符数组输入/输出的异常情况 与数值型数组一样,如果在定义字符数组后没有给数组元素赋值,数组元素的值是一个不确定的数据。 【例525】数组元素没有正确赋值时的输出异常。 #include int main() { char s[10]; //定义数组时没有初始化 int i; for(i=0;i<5;i++) //仅对前5个数组元素赋值 scanf("%c",&s[i]); puts(s); //或printf("%s",s); //试图输出整个字符串 return 0; } 运行结果: Frank Frank烫烫烫蘰 □ 本例在定义s数组时没有对其初始化,因此10个数组元素的值都是不确定的。在循环语句中只对前5个数组元素进行了读入,数组元素的存储示意图见图522,由于后5个数组元素没有读入任何数据,其值是不确定的。 图522对字符数组不正确赋值时的存储示意图 如果本例的输入/输出采用如下形式: for(i=0;i<5;i++) //仅对前5个数组元素赋值 scanf("%c",&s[i]); for(i=0;i<5;i++) //仅输出前5个数组元素 printf("%c",s[i]); 那么在输入Frank后能正确输出Frank,即仅对前5个数组元素操作。但是本例在输出时用了put(s)(或printf("%s",s)),由于puts()函数(或printf按%s格式)在输出时以'\0'作为字符串结束标志,而通过图522可知,后5个数组元素的值不确定,没有字符串结束标志'\0',因此输出时会出现乱码。 字符串往往在整体出现时才有意义,如人的名字“Frank”,打招呼的语句“Hello Boy”,课程名字“Programming C”等,因此建议在输入/输出时采用整体输入/输出的方式,而不要采用%c单个字符处理的方式。以下两种方式都可对字符串进行整体操作。 char s[10]; scanf("%s",s);//整体输入字符串,不可接收带空格字符串 printf("%s",s);//整体输出字符串 或者 char s[10]; gets(s);//整体输入字符串,可接收带空格字符串 puts(s);//整体输出字符串 用scanf("%s",s)输入字符串,按空格键或回车键后,系统会将空格或回车之前的内容赋给数组元素,然后自动在末尾加字符串结束标志'\0'。同样,用gets(s)输入字符串,按回车键后,系统会将回车之前的内容赋给数组元素,然后自动在末尾加字符串结束标志'\0'。因此在有效字符的后面出现了字符串结束标志'\0',见图523,此时用puts或%s整体输出时就不会出现乱码。 图523对字符数组正确赋值后的存储示意图 5.4.5字符串处理函数 常用字 符串处 理函数 C语言的库函数中提供了丰富的字符串处理函数,除了上面已介绍的gets()和puts()外,还有一些其他的字符串处理函数,这些函数在使用时需要加上头文件string.h,即: #include 1. 字符串长度函数strlen() 前面的例子中实现了测试字符串的有效长度,C语言库函数也提供了测字符串长度的函数,即strlen()函数,其调用形式见表58。 表58strlen()函数的调用形式 语法示例说明 strlen(字符数组名)char st[20]="hello boy"; int len; len=strlen(st); printf("%d\n",len); strlen()函数的返回值是字符串的实际长度,不包括'\0'。如示例中len获得的值为9,不是10,也不是20 2. 字符串连接函数strcat() strcat()函数的作用是将两个字符数组中的字符串连接起来,组成一个新的字符串。其调用形式见表59。 表59strcat()函数的调用形式 语法示例说明 strcat(字符数组1,字符数组2); strcat(str1,str2); (假设str1,str2是数组名) (1) 将字符串2连接到字符串1的后面,结果放到字符数组1中,函数调用后返回字符数组1的地址。 (2) 字符数组1必须足够大,以便容纳连接后的新字符串 【例526】输入两个字符串,然后把它们连接起来。 #include #include int main() { char str1[80],str2[30]; int i=0; printf("Enter the first string:"); gets(str1); printf("Enter the second string:"); gets(str2); strcat(str1,str2); //用strcat函数实现两个字符串连接,结果放在第一个字符数组 printf("New string:"); puts(str1); //输出第一个字符数组内容,即连接后的新字符串 return 0; } 运行结果: Enter the first string:Hello Enter the second string:Boy New string:HelloBoy 连接前两个字符串的后面都有一个'\0',连接后原来的第一个字符串末尾的'\0'被覆盖,只在新形成的字符串末尾加一个'\0'。 连接两个字符串时不会自动添加其他字符,比如输入第一个串Hello后面没有空格,则连接后内容HelloBoy在Hello后面也不会有空格。 可以自己编码来实现strcat()函数的功能。见例527。 【例527】编程实现strcat()函数的功能。 #include int main() { char s[15],t[10]; int i=0,j=0; printf("Enter the first string:"); gets(s); //输入第一个字符串存入s数组 printf("Enter the second string:"); gets(t); //输入第二个字符串存入t数组 while(s[i]!='\0') i++; //搜索到字符串1的末尾 while(t[j]!='\0') //若字符串2未遇到'\0',则执行连接操作 { s[i]=t[j]; //将字符串2的内容逐一复制到字符数组1 i++; j++; } s[i]='\0'; //在新的字符串末尾添加'\0'作为结束标记 printf("New String is:"); puts(s); //输出连接后形成的新的字符串 return 0; } 运行结果: Enter the First String:Good. Enter the second string:Thanks! New String: Good.Thanks! 连接前,s数组中存放“Good.”,t数组中存放“Thanks!”,见图524(a)。代码中第一个while循环对s数组搜索,遇到'\0'停下,此时已到字符串“Good.”的末尾,i的值为5。第二个while循环实施连接操作,不断执行“s[i]=t[j]”,直到第二个字符串遇到'\0'停下,见图524(b)。 图524连接前后两个字符数组的存储示意图 程序最后的“s[i]='\0';”不能省略,否则s数组中的字符串没有结束标志,会造成输出错误。 连接的是两个字符串,而不是两个字符数组。 3. 字符串复制函数strcpy() C语言中,不能像基本变量一样直接用赋值号进行字符串之间的复制,需要使用循环逐个对字符进行复制,并在最后加上字符串结束标志'\0'。C语言提供的strcpy()函数可以实现这个功能,将字符串2的内容复制到字符数组1中,其调用形式见表510。 表510strcpy()函数的调用形式 语法示例说明 strcpy(字符数组1,字符串2); strcpy(str1,str2); 或 strcpy(str1,字符串常量) (假设str1,str2是数组名) (1) 字符数组1必须足够大,以便容纳被复制的内容,至少不能小于字符串2的长度。 (2) 字符数组1必须写成数组名形式,字符串2可以是字符数组名,也可以是字符串常量。 (3) 字符串2后面的'\0'也一同被复制到字符数组1 例如: char str1[80],str2[20]= "Good luck! " strcpy(str1,str2); 则str1数组中存放的内容也将是"Good luck! "。 注意: C语言中不能用赋值号将一个字符串或字符数组直接复制给另一个字符数组,例如,下面的赋值语句是错误的。 char str1[20],str2[10]= "Bye Bye! " str1="Bye Bye! "//错误 str1=str2;//错误 因为数组名是数组的首地址,是一个常量,不可以给常量赋值。 可以自己编码来实现strcpy()函数的功能,见例528。 【例528】编程实现strcpy()函数的功能。 #include int main() { char s[10],t[10]; int i=0,j=0; printf("T String is:"); gets(t); //输入第二个字符串 while(t[i]!='\0') //若字符串2未遇到'\0',则执行复制操作 { s[i]=t[i]; //将t数组的内容逐一复制到s数组 i++; } s[i]='\0'; //在新的字符串末尾添加'\0'作为结束标记 printf("S String is:"); puts(s); //输出s数组的内容 return 0; } 运行结果: T String is:Hello Boy S String is:Hello Boy 4. 字符串比较函数strcmp() strcmp()函数的作用是对两个字符串进行比较,其用法见表511。 表511strcmp()函数的调用形式 语法示例说明 strcmp(字符串1,字符串2); strcmp(str1,str2); (假设str1,str2是数组名) 对两个字符串从左到右逐个字符比较(按ASCII码值大小比较),直到出现不同字符或遇到'\0'。比较结果返回一个函数值。 (1) 若全部字符相同,则认为两个字符串相等,函数返回0。 (2) 若出现不相同字符,则以第1对不相同的字符的比较结果为准,若字符串1大于字符串2,函数返回一个正整数; 若字符串1小于字符串2,函数返回一个负整数 例如: 图525字符串比大小的示意图 int k; k=strcmp("Hi","Hello"); 本例中的k是一个正整数,说明字符串"Hi"大于字符串"Hello",因为"Hi"中的第2个字符是'i',"Hello"中的第2个字符是'e',显然字母'i'的ASCII码值大于字母'e'的ASCII码值,见图525。 字符串比大小,比的是对应字符ASCII码值的大小,而不是比两个字符串的长度。 字符串比大小的功能在类似字符串排序的过程中非常有用,见例529。 【例529】输入两个人的姓名,按字典顺序进行输出。 #include #include int main() { char s1[20],s2[20]; printf("Enter first name:"); gets(s1); printf("Enter second Name:"); gets(s2); if(strcmp(s1,s2)<0||strcmp(s1,s2)==0) //比较两个字符串大小 printf("%s %s\n",s1,s2); //按字典序输出 else printf("%s %s\n",s2,s1); return 0; } 运行结果: Input first name:Susan Input second name:Anney Anney Susan 不能用关系运算符比较两个字符串,假设str1和str2是两个字符串,则以下写法是错误的。 if(str1==str2) printf("Equal\n");//写法错误 应该写成: if(strcmp(str1,str2)==0) printf("Equal\n");//写法正确 5.5高精度加法 高精度计算,是指参与运算的数大大超出标准数据类型所能表示的范围的运算。例如,两个1000位的数相加,或者求斐波那契数列的前1000位等,用已知的数据类型无法正确实现这些运算。 在C语言中,可以用数组模拟大数的运算,数组的每个元素代表大数的某一位,然后按位处理进位、借位问题,处理完以后再将该数组用规定的格式输出即可。基本步骤如下。 (1) 读入数据: 建议用字符串方式读入,优点是字符串输入较方便。 (2) 存储数据: 建议转换为整型数组存储,这样计算的时候比较方便。 (3) 实施运算: 模拟数学计算,从个位开始向高位逐位计算。 (4) 输出结果: 用规定格式输出计算结果。 【例530】大数的输入/输出。(nbuoj1909) 输入一个不超过200位的正整数,并输出该数。 #include #include int main() { int i,len,a[201]; char s[201]; gets(s); //用字符串形式读入大数 len=strlen(s)-1; for (i=0; i<=len; i++) a[i]=s[i]-'0'; //将字符数组s的每一位转换成整数 printf("The Number:"); for (i=0; i<=len; i++) printf("%d",a[i]); //用整数形式输出数组a的每一位 printf("\n"); return 0; } 运行结果: 111222333444555666777888999 The Number: 111222333444555666777888999 本例如果用int型处理会溢出,用double型处理则会失真。 用数组可以实现大数的输入和输出,那么如何实现两个大整数的加法呢?首先以9768+523=10291为例,来看一个加法运算的实现过程。将这个过程写成竖式,见图526。 图526加法的竖式 加法需要尾对齐,从个位开始模拟。 个位: 8+3=11,个位取1,向高位进位1。 十位: 6+2+1(低位进位)=9,十位取9,进位为0。 百位: 7+5=12,百位取2,向高位进位1。 千位: 9+1(低位进位)=10,千位取0,向高位进位1。 万位: 万位为1,来自低位的进位。 在加法的每个步骤都要考虑是否有来自低位的进位,以及是否需要向高位进位。 由于加法需要个位对齐,如果两个数直接从左到右存入数组的话,会错位,给计算带来麻烦,因此考虑将数据逆序存放到数组中,即从左到右依次存放个位、十位、百位、……,见图527。计算时,将两个数组中的元素逐位相加即可。输出时,要注意再次实施逆序操作,按数据的正常格式输出计算结果。 图527大数相加时数据在数组中的存放形式 假设两个大数已分别存放在数组a和b中,相加的结果存回数组a,则两个大数加法的核心部分的参考程序如下。 c=0; //进位初值0 for(i=0;i=10) //若大于10则进位 { a[i]=a[i]%10; //保留小于10的数值 c=1; //进位1 } else c=0; //若小于10则无进位 } if(c>0) {a[len]=c;len++;} //最后一次计算还有进位 【例531】大数相加。(nbuoj2830) 输入任意长度(不超过1000位)的两个正整数,计算两数相加的结果并输出。 #include #include int main() { char sa[1002],sb[1002]; int a[1002]={0},b[1002]={0}; int i,j,lensa,lensb,len,c; gets(sa); gets(sb); //用字符串形式读入两个大数 lensa=strlen(sa);lensb=strlen(sb); //测出每个字符串的长度 j=0; for(i=lensa-1;i>=0;i--) //将第一个数逆序转换为整型数组形式,即低位在前 a[j++]=sa[i]-'0'; j=0; for(i=lensb-1;i>=0;i--) //将第二个数逆序转换为整型数组形式,即低位在前 b[j++]=sb[i]-'0'; if(lensa>=lensb) len=lensa; //len取两数中最大的位数 else len=lensb; //进行两个大数的加法 c=0; //进位初值0 for(i=0;i=10) //大于10则进位 {a[i]=a[i]%10;c=1;} else c=0; } if(c>0) {a[len]=c;len++;} //最后一次计算还有进位 //逆序输出 for(i=len-1;i>=0;i--) printf("%d",a[i]); printf("\n"); return 0; } 运行结果: 111222333444555666777888999 1 111222333444555666777889000 两个数都是以字符串形式输入的,分别存入字符数组sa和sb。接着测出每个字符串的长度(也就是该数的位数),因为字符串不能直接进行运算,所以将字符串形式的数据逆序转换为整型数组的形式(例如输入两个数为1234和5678,则转换为整型数组形式存储为4321和8765,让低位在前,使运算更方便),然后进行运算。加法计算完成后,在数组a中低位在前,高位在后,因此在输出时,需要再做一次逆序操作,使高位在前,低位在后。 5.6实 例 研 究 5.6.1统计单词数 【例532】统计单词数。(nbuoj1176) 输入一行由英文字母、数字和空格组成的字符串,遇到换行符时表示输入结束。单词间以空格分隔,可能有多个空格。文章最多由1000个字符组成。试统计其中的单词个数并输出。 本题的关键是如何判断出现了新的单词。判断是否有新单词,可以由是否有空格来决定(一行开头的空格不算,连续出现的多个空格记为1次)。如果当前字符为空格,说明没有新单词出现。如果当前字符非空格,而它前面的字符为空格,说明“新的单词开始了”。如果当前字符非空格,而它前面的字符也是非空格,说明仍然是前面那个单词的延续,未出现新单词。一个基本的流程图见图528。 图528“统计单词数”的流程图 假设: 用变量is_word=0表示前一字符为空格,is_word初值为0,is_word=1表示前一字符不是空格。用变量num记录单词数,初值为0。 则具体操作如下: 如果当前字符是空格,说明未出现新单词,此时使is_word为0,num保持原值不变。如果当前字符不是空格,而前一个字符是空格(is_word=0),说明出现新单词,此时使is_word=1,num加1; 如果当前字符不是空格,前一字符也不是空格(is_word=1),说明未出现新单词,维持is_word=1不变,并且num保持不变。 #include int main() { char str[1001]; int i,num=0,is_word=0; char ch; gets(str); //读入一行字符 for(i=0;str[i]!='\0';i++) { if(str[i]==' ') //若当前字符是空格,说明未出现新单词 is_word=0; //使is_word=0,num不变 else //若当前字符不是空格 if(is_word==0) //若前一字符是空格 { //说明出现新单词 is_word=1; num++; //num加1,说明出现新单词 } //若当前字符不是空格,前一字符也不是空格,说明未出现新词,is_word和num都不变 } printf("%d\n",num); return 0; } 运行结果: He comes from Thailand 4 5.6.2成绩管理 【例533】输入n名学生的学号、姓名以及c门课的成绩,并输出这些信息。其中,n和c也是从键盘输入的。 本题定义三个二维数组: 二维字符数组id存储学生的学号,二维字符数组name存储学生的姓名,二维浮点型数组score存储每位同学的若干门课的成绩。 #include #define S_NUM 100 //假设学生最大数100 #define C_NUM 10 //假设课程最大数10 int main() { int i,j; char id[S_NUM][10]; //学号 char name[S_NUM][20]; //姓名 double score[S_NUM][C_NUM]; //课程成绩 int n,c; //实际学生数及课程门数 printf("Enter student number:\n"); scanf("%d",&n); //输入学生实际人数 printf("Enter course number:\n"); scanf("%d",&c); //输入课程实际数量 for(i=0;i #include int main() { int n,i,j,min; char city[101][101],temp[101]; //temp数组可临时保存一个字符串 scanf("%d",&n);getchar(); for(i=0;i0) min=j; if(min!=i) {strcpy(temp,city[i]);strcpy(city[i],city[min]);strcpy(city[min],temp);} } for(i=0;i #include #include #define SUITS 4 //牌的花色有4种 #define RANKS 13 //每种花色有13张牌 int main() { int in_hand[SUITS][RANKS]={0}; //记录每张纸牌是否被发过,初值为0 char rank_code[]={'2','3','4','5','6','7','8','9','T','J','Q','K','A'}; //13张牌的牌点 char suit_code[]={'c','d','h','s'}; //牌的4种花色 int num; //玩家手头牌的张数 int suit,rank; //每次抽取的牌的花色和牌点 int count=0; //控制每行输出几组牌 srand((unsigned)time(NULL)); printf("Enter number of cards:\n"); scanf("%d",&num); //输入需要发牌的张数 printf("----------------Your Cards------------------\n"); while(num>0) { suit=rand()%SUITS; //随机产生一种花色 rank=rand()%RANKS; //随机产生一个牌点 if(!in_hand[suit][rank]) //判断该牌是否被发过,没被发过的牌本次可以发出 { in_hand[suit][rank]=1; num--; printf("(%c,%c) ",suit_code[suit],rank_code[rank]); count++; if(count%5==0) putchar('\n'); } } putchar('\n'); return 0; } 运行结果: Enter number of cards: 12 ----------------Your Cards------------------ (s,2) (d,Q) (h,J) (c,5) (d,7) (h,2) (h,4) (d,5) (c,8) (d,J) (c,7) (s,K) 为了避免两次发同样的牌,需要记录已经发过的牌。本例定义了二维数组in_hand来执行这个操作,该数组有4行13列,每行表示一种花色,每列表示一种牌点。在程序开始时,该数组的所有元素都置0,表示都没有被发出去过。每次随机抽取一张纸牌后,对应的元素值就变为1,则下次就不会被再抽取了。 5.7习题 5.7.1选择题 1. C语言中,以下关于数组的描述正确的是()。 A. 数组大小固定,但是可以有不同类型的数组元素 B. 数组大小可变,但是所有数组元素的类型必须相同 C. 数组大小固定,所有元素的类型必须相同 D. 数组大小可变,可以有不同类型的数组元素 2. 若有定义“int score[10];”,则对score数组中的元素的正确引用是()。 A. score(1)B. score[10]C. score[6.0]D. score[0] 3. 若有定义“int a[]={5,4,3,2,1},i=4;”,则以下对a数组元素的引用错误的是()。 A. a[i]B. a[a[0]]C. a[a[1*2]]D. a[a[i]] 4. 以下能正确定义一维数组的选项是()。 A. intnum [ ]; B. #define N 100 int num [N]; C. int num[0..100]; D. int N=100,num[N]; 5. C语言中,下面能正确定义一维数组并初始化的选项是()。 A. int a[5]={0,1,2,3,4,5};B. int a[5]={3}; C. int a[5 ]=5;D. int a[N]={1,2,3}; 6. 设有定义“int a[3][4];”,则对数组元素引用正确的是()。 A. a[2][4]B. a[2][0]C. a[3][3]D. a[0][4] 7. 若有说明: int a[][3]={{1,2,3},{4,5},{6,7}}; 则数组a的第一维的大小为 ()。 A. 2B. 3C. 4D. 无确定值 8. 若有初始化语句“int a[3][4]={0};”,则下面正确的叙述是()。 A. 只有元素a[0][0]可得到初值0 B. 此初始化语句不正确 C. 数组a中每个元素都可得到初值0 D. 数组a中各元素都可得到初值,a[0][0]初值0,其他元素初值为随机数 9. 下述对C语言字符数组的描述错误的是()。 A. 字符数组中的内容不一定是字符串 B. 可以用输入语句把字符串整体输入给字符数组 C. 可以在赋值语句中通过赋值运算符"="对字符数组整体赋值 D. 字符数组中没有'\0'也可以正确输出其中的内容 10. 下面是对数组s的初始化,其中错误的语句是()。 A. char s[5]={"boy"};B. char s[5]={ 'b','o','y'}; C. char s[5]=" ";D. char s[5]="Frank"; 11. 已有定义“char a[ ]="boy",b[ ]={'b','o','y'};”,则以下叙述中正确的是()。 A. 数组a和数组b长度相同B. 数组a的长度小于数组b的长度 C. 数组a的长度大于数组b的长度D. 上述说法都不对 12. 函数调用strcat(strcpy(s1,s2),s3)的功能是()。 A. 将串s1复制到串s2中,然后再连接到s3之后 B. 将串s1连接到串s2之后,再复制到s2之中 C. 将串s2复制到s1中,再将串s3连接到串s1之后 D. 将串s2复制到s1中,再将s1连接到串s3之后 13. 下列叙述错误的()。 A. 空字符串也占用内存,其内存空间大小是1B B. 两个连续的单引号可表示合法的字符常量 C. 两个连续的双引号可表示合法的字符串常量 D. 不能用关系运算符比较字符串的大小 14. 设有定义“int a[2][3];”,则以下的叙述错误的是()。 A. a[0]可看作是由3个整型元素组成的一维数组 B. a[0]和a[1]数组名,分别代表不同的地址常量 C. 可以用语句“a[0]=0;”为数组所有元素赋初值 D. 数组a包含6个元素 15. 以下程序运行后的输出结果为()。 #include int main() { char str[3][5]={"AAAA","BBB","CC"}; printf("%s\n",str[1]); return 0; } A. AAAAB. BBBC. CCD. BBBCC 16. 有以下程序,在运行时输入how do you do<回车>,则输出结果为()。 #include int main() { char a[20],b[20],c[20]; scanf("%s%s",a,b); gets(c); printf("%s%s%s\n",a,b,c); return 0; } A. HowdoyoudoB. Howdo you do C. How do youD. Howdoyou do 17. 以下程序运行后的输出结果为()。 #include int main() {int A[10]={9,3,2,15,12}; int i,t,n=5; A[n]=10; i=n-1; while((i>=0)&&(A[i]>A[i+1])) { t=A[i];A[i]=A[i+1];A[i+1]=t; i--; } n++; for(i=0;i int main() { int a[3][4]= {5,4,6,7,3,9,0,2,8,1,3,5 }; int t[3],i,j; for (i=0; i<3; i++) { t[i]=a[i][0]; for(j=1; j<4; j++) if(a[i][j]>t[i]) t[i]=a[i][j]; } for (i=0; i<3; i++) printf("%d ", t[i]); return 0; } A. 401B. 749C. 798D. 538 19. 以下程序运行后的输出结果为()。 #include #include int main() { char str[100]="How do you do"; strcpy(str+strlen(str)/2,"es she"); printf("%s\n",str); return 0; } A. How do you do B. es she C. How are you D. How does she 20. 以下程序运行后的输出结果是()。 #include int main() { int a[6] = {2,4,6,8,10,12}; int b[6] = {6,8,10,7,5,1}; int i,j; for (i = 0; i < 6; i++) { for (j = 0; j < 6; j++) if (a[i] == b[j]) break; if (j < 6) printf("%d ", a[i]); } printf("\n"); return 0; } A. 2 4 6 8 10 12B. 10 7 5C. 2 4 12D. 6 8 10 5.7.2在线编程题 1. 简单评委打分。(nbuoj1147) 学生参加项目结题汇报,假设有8位老师作为评委。计算学生最终得分的方法如下: 去掉一个最高分和一个最低分,计算剩余6个分数的平均值,所得结果就是该学生的最后得分。本题先输入8个分数,去掉一个最高分和一个最低分后计算平均得分。输出保留两位小数。 2. 求年月日。(nbuoj1075) 输入两个整数分别代表某一年和这一年的第几天(假设数据都在有效范围内),要求输出具体的年、月、日的信息。如输入2011 20,则输出211120。 3. 最高分和最低分。(nbuoj1157) 已知有10个同学的成绩,求最高分和最低分以及相应分数所在的位置。本题输入10个整数,假设这10个数互不相同,且无序排列。请找出其中最大数及它在数组中的下标,以及最小数和下标。 4. 十进制转换成八进制。(nbuoj1172) 输入一个十进制整数,把这个数转换为八进制的数输出。 5. 百灯判熄。(nbuoj1122) 有M盏灯,编号为1~M,分别由相应的M个开关控制。开始时全部开关朝上(朝上为开,灯亮),然后进行以下操作: 编号凡是1的倍数的灯反方向拨一次开关; 是2的倍数的灯再反方向拨一次开关; 是3的倍数的灯又反方向拨一次开关,……,直到是M的倍数的灯又反方向拨一次开关。本题输入一个整数m(1≤m≤100)代表灯的数量,要求输出最后为熄灭状态的灯(不亮)的数量以及编号。 6. Susan的货币兑换。(nbuoj1167) Susan到中国观光旅游,她不太熟悉人民币,因此分别将1角,2角,5角,1元,2元,5元,10元,20元,50元,100元的人民币依次排序号(从1开始排序号),她每天将自己手中不同面值人民币的张数输入iPAD,以计算手头的人民币数额。请帮她编写一个程序,可以根据她手中的不同面值人民币的张数,计算出对应的人民币数额。本题输入人民币序号及张数,每种面值占据一行,如5 20表示序号为5的人民币有20张,当输入序号或张数为负数时结束,要求输出对应的人民币数值。输出保留两位小数。 7. 对角线元素和。(nbuoj1164) 输入一个整数n,然后输入n×n个数据建立一个方阵,计算并输出方阵主对角线元素的和。 8. 上三角置零。(nbuoj1298) 输入一个5行5列的二维矩阵,输出上三角置零后的二维矩阵。 9. 二维数组最大值。(nbuoj1161) 输入12个整数构成一个3×4的二维数组,求出该数组的最大元素并输出。 10. 二维数组每行最大值。(nbuoj1191) 输入12个整数构成一个3×4的二维数组,求出每行的最大元素并输出。 11. 内部和。(nbuoj1299) 输入两个整数m和n(范围为1~9),接着输入m×n个数据建立一个二维矩阵,计算并输出矩阵内部元素(不包括最上下两行及最左右两列)的和。 12. 特定字符出现次数。(nbuoj1056) 输入一个字符串(长度小于等于1000),再输入一个待查找的特定字符key, 统计key在字符串中的出现次数 13. 相邻字符判相等。(nbuoj1054) 输入一行字符(长度小于等于1000),判断其中是否存在相邻两个字符相同的情形,若有,则输出该相同的字符并结束程序(只需输出第一种相等的字符即可),否则输出No。 14. 单词译码。(nbuoj1139) 输入一个单词,长度不超过9(假设输入内容全部都是英文字母,不存在其他字符)。对该单词进行译码并输出结果。译码规律是: 用原来字母后面的第4个字母代替原来的字母,并能循环译码。例如,字母A后面第4个字母是E,用E代替A; 同理,字母y用c代替。如单词China应译为Glmre,单词Today应译为Xshec。 15. 回文数字。(nbuoj1144) 给定一个数字字符串,长度不超过100,判断它是否是回文数字。例如,121, 1221是回文数字, 123不是回文数字。若是回文输出 Yes, 否则输出 No。 16. 回文字符串。(nbuoj1145) 给定一个字符串,长度不超过100,判断它是否是回文串。例如,aba, abcba是回文, abc, xyy 不是回文。若是回文输出 Yes, 否则输出 No。 17. 数组字符数出现频率。(nbuoj1148) 输入一行文本,统计其中数字字符0~9出现的频率并输出。没有出现的数字字符不要显示。 18. 字母出现频率。(nbuoj1159) 输入一行文本(小于1000字符),统计其中每个英文字母出现的频率,输出出现过的英文字母及其次数,未出现过的字母不需要显示。为了简化问题的复杂度,假设在统计过程中不区分字母的大小写,即'A'与'a'被认为是一种字母。 19. C语言合法标识符。(nbuoj1190) 输入一个长度不超过50的字符串,判断其是否为C语言合法的标识符。 20. 输出最短字符串。(nbuoj1201) 输入五个字符串,输出其中最短的字符串,若长度相同则输出出现较早的那一个。每个字符串长度不大于1000。 21. 判断字符串类型。(nbuoj1199) 输入一个字符串(长度不超过1000),其中只包括数字或字母。对应输入的字符串,输出它的类型。如果仅由数字构成的则输出digit,如果仅由字母构成的则输出character,如果是由数字和字母一起构成的则输出mixed。 22. 你能找出多少个整数?(nbuoj1315) 输入一个字符串,由空格、英文字母、数字组成,长度小于1000。输出字符串中的整数的个数(不是数字字符)。 23. 查找最大字符串。(nbuoj1175) 输入一行长度不超过100的字符串,字符串仅由大小写字母构成,查找其中最大字母(按ASCII码大小排),在该字母后面插入字符串"(max)",不包括引号。如果存在多个最大的字母,就在每一个最大字母后面都插入"(max)"。 24. 去过的城市。(nbuoj1352) CoCo喜欢旅游,每次都会去一个地方,并且每去过一个地方都会记录一下地名,当然有些地方去过多次也都会一一记录下来的。现在列出了CoCo去过的n个城市的名称(会有重复的),然后再输入一个城市的名称,请你帮忙计算一下这个城市CoCo去过几次了。本题先输入一个正整数n(n≤1000),接下来n行依次输入n个字符串表示CoCo去过的城市名,每个字符串的长度小于等于100字符,并且字符串中无空格。然后再输入一个城市名表示待查找字符串,要求输出该城市CoCo已经去过几次了。 25. 加法的进位。(nbuoj1451) 多位数的加法通常会有进位,如555+555有三次进位。从键盘输入两个正整数,计算它们在进行加法运算时有几次进位。(每个数不超过20位。)