函数的通用性
对于上节课程序中的top函数处理较为复杂的问题,比如计算语数英各个科目的最高分、在分为选修必修、每科人数又不尽相同时,就无法满足上述条件了。
从函数的通用性考虑,至少应该满足下面两个条件:
可以处理任何数组
不仅可以处理数组tensu,而且也可以处理其他任意数组。
可以处理不同元素个数的数组
数组的元素个数不仅只有NUMBER个,还要可以指定数组的元素个数。
让我们用上面的知识来实现接下来程序的运行
数组的传递
#include<stdio.h>
#define NUMBER 5//学生人数
/*返回元素个数为n的数组v中的最大值*/
int max_of(int v[], int n)
{
int i;
int max = v[0];
for(i = 0; i < n; i++)
if(v[i] > max)
max = v[i];
return max;
}
int main()
{
int i;
int eng[NUMBER];//英语分数
int mat[NUMBER];//数学分数
int max_e, max_m;//最高分
printf("请输入%d名学生的分数。\n", NUMBER);
for(i = 0; i < NUMBER; i++)
{
printf("[%d]英语:", i + 1); scanf("%d", &eng[NUMBER]);
printf(" 数学:"); scanf("%d", &mat[NUMBER]);
}
max_e = max_of(eng, NUMBER);//英语最高分
max_m = max_of(mat, NUMBER);//数学最高分
printf("英语最高分=%d\n", max_e);
printf("数学最高分=%d\n", max_m);
return 0;
}
函数max_of的作用:找出包含任意个元素的int型数组中元素的最大值,然后返回该值,无需使用tensu或者NUMBER等特定的名称就可以进行说明了。
int max_of(int v[], int n)
进行函数设计时,应尽量提高通用性,比如:
使用上面的函数,它可以是储存英语分数的数组eng,是储存数学分数的数组mat,也可以是储存英语与数学最高分的数组max_e和max_m。
接受数组的形参的声明为“类型名 参数名 [ ]”, 使用别的形参来接受元素个数(这里是int类型的变量n)。
调用函数时使用的实参只要写声明的数组名称就可以
函数的传递和const类型的修饰符
在学习前面的内容时,我们可以知道被调用函数中作为形参接受到数组,就是函数调用时被作为实参的数组(不能再理解为前面函数学习到的形参是实参拷贝的副本,对形参进行改动而实参不受任何影响),因此对接受到的数组元素进行修改,也会反映到调用时传入的数组中。
我们用程序来演示一下:
/*将数组所有元素的值设置为0*/
#include<stdio.h>
/*把0赋值给n个元素的数组的所要元素*/
void set_zero(int v[], int n)
{
int i;
for(i = 0; i < n; i++)
v[i] = 0;
}
/*显示n个元素的数组v的所有元素并换行*/
void print_array(const int v[], int n)//声明不改变所接受的数组元素的值
{
int i;
printf("{ ");
for(i = 0; i < n; i++)
printf("%d ", v[i]);
printf("} ");
}
int main()
{
int arr1[] = {1, 2, 3, 4, 5};
int arr2[] = {3, 2, 1};
printf("arr1= "); print_array(arr1, 5); putchar('\n');
printf("arr2= "); print_array(arr2, 3); putchar('\n');
set_zero(arr1, 5);//把0赋值给arr1中的所有元素
set_zero(arr2, 3);//把0赋值给arr2中的所有元素
printf("把0赋值给两个数组的所有元素。\n");
printf("arr1= "); print_array(arr1, 5); putchar('\n');
printf("arr2= "); print_array(arr2, 3); putchar('\n');
}
由于print_array函数的形参v在声明时加上了从const类型修饰符,因此在函数中就不能改写数组v的值了。
v[1] = 5;//错误,不能为const声明的数组元素赋值
如果只是接收数组的元素值而不进行改写的话,在声明接收数组的形参时应该加上const,这样就可以放心调用函数了。
线性查找(顺序查找)
#include<stdio.h>
#define NUMBER 5//元素个数
#define FAILED -1//查找失败
/*查找元素数为n的数组v中与key一致的元素*/
int search(const int v[], int key, int n)
{
int i = 0;
while(1)
{
if(i == n)
return FAILED;
if(v[i] == key)
return i;
i++;
}
}
int main()
{
int i, ky, idx;
int vx[NUMBER];
for(i = 0; i < NUMBER; i++)
{
printf("vx[%d]:", i);
scanf("%d", &vx[i]);
}
printf("要查找的值:");
scanf("%d", &ky);
idx = search(vx, ky, NUMBER);//从元素个数为NUMBER的数组vx中查找ky
if(idx == FAILED)
puts("\a查找失败");
else
printf("%d是数组的第%d号元素。\n", ky, idx + 1);
return 0;
}
函数search从元素为n的int型数组v的开头,顺次查找是否存在于key相同的元素,如果有,则返回元素下标,反之返回FAILED。
函数search中的while语句控制的表达式是1,因此只有在执行return语句时才能跳出循环,否则循环将会一直进行下去。
if(i == n)
return FAILED;
if(v[i] == key)
return i;
大家可以看下下面的图:只需要找到相同的元素然后返回下标,如果遍历完数组(i = n)仍未找到就返回FAILED
在满足下面任一条件时,就可以跳出循环:
A.未能找到想要查找的值,最初跳出循环(当i == n成立的时候)
B.找到了想要查找的值(当v[i] == key成立时 )
像这样从数组开头的元素按照顺序搜索,找出与目标相同元素的一系列操作,称为顺序查找或线性查找。
哨兵查找法
进行循环操作的时候,需要不断判断是否满足两个结束循环的条件,虽然操作简单但是当次数积累到一定量时,也是一个不小的负担。
如果数组的大小(元素个数)还有一定的富裕时,我们就可以把想要查找的数值存储到数组末尾的元素v[n]中,这样即使数组中没有想要查找的数值也会满足条件B,条件A也就省略了。
在数组的末尾假的数据称为哨兵,使用哨兵查找的方法称为哨兵查找法,使用此方法可以简化对循环条件的判断。
#include<stdio.h>
#define NUMBER 5//元素个数
#define FAILED -1//查找失败
/*查找元素数为n的数组v中与key一致的元素*/
int search(int v[], int key, int n)
{
int i = 0;
while(1)
{
if(v[i] == key)
break;
i++;
}
return (i < n) ? i:FAILED;
}
int main()
{
int i, ky, idx;
int vx[NUMBER + 1];//多准备一个元素
for(i = 0; i < NUMBER; i++)
{
printf("vx[%d]:", i);
scanf("%d", &vx[i]);
}
printf("要查找的值:");
scanf("%d", &ky);
if((idx = search(vx, ky, NUMBER)) == FAILED)//从元素个数为NUMBER的数组vx中查找ky
puts("\a查找失败");
else
printf("%d是数组的第%d号元素。\n", ky, idx + 1);
return 0;
}
由于函数search需要改变数组v的内容,因此在声明时不能使用const类型的修饰符。
return (i < n) ? i:FAILED;
这个语句是基于条件运算符的判定,具体说就是判断发现元素的值(与key相等的值)是原本就存在数组的元素还是作为哨兵追加的元素。
if((idx = search(vx, ky, NUMBER)) == FAILED)
我们拆开来看,首先是if(),括号里面的表达式也可分为a和b两部分
a部分看做idx = search(vx,ky,NUMBER),使用赋值运算符把函数search的返回值赋值给idx
b部分看做FAILED,使用相等运算符,判断idx = search(vx,ky,NUMBER)与FAILED是否相等
到这里我们大概学习了函数的基本知识,到后面我们会学习基本数据类型和数等更加精细的内容。