第十章 数组和指针


编程练习

1.修改程序清单10.7中的程序rain,使它不使用数组下标,而是使用指针进行计算(程序中仍然需要声明并初始化数组)。

# include <stdio.h>
# define MONTHS 12
# define YEARS 5

int main(void)
{
	//把数组初始化为2000年到2004年的降水量数据
	const float rain[YEARS][MONTHS] =
	{
		{ 4.3, 4.3, 4.3, 3.0, 2.0, 1.2, 0.2, 0.2, 0.4, 2.4, 3.5, 6.6 },
		{ 8.5, 8.2, 1.2, 1.6, 2.4, 0.0, 5.2, 0.9, 0.3, 0.9, 1.4, 7.3 },
		{ 9.1, 8.5, 6.7, 4.3, 2.1, 0.8, 0.2, 0.2, 1.1, 2.3, 6.1, 8.4 },
		{ 7.2, 9.9, 8.4, 3.3, 1.2, 0.8, 0.4, 0.0, 0.6, 1.7, 4.3, 6.2 },
		{ 7.6, 5.6, 3.8, 2.8, 3.8, 0.2, 0.0, 0.0, 0.0, 1.3, 2.6, 5.2 }
	};
	int year, month;
	float subtot, total;

	//降水总量
	for (year = 0, total = 0; year < YEARS; year++)
	{
		for (month = 0, subtot = 0; month < MONTHS; month++)
		{
			subtot += *(*(rain + year) + month);
			//(rain+year)表示行地址增加,移动范围是一个数组大小。
			//*rain + year表示列地址增加,移动范围是一个int类型大小。
		}
		printf("%d年的总降水量:%.2f\n", 2010+year, subtot);
		total += subtot;	
	}
	printf("%d年的平均降水量:%.2lf\n", YEARS, total / YEARS);
	printf("5年来每个月份的平均降水量:\n"  );
	printf(" 1月  2月  3月  4月  5月  6月  7月  8月  9月 10月 11月 12月\n");
	for (month = 0; month < MONTHS; month++)
	{
		for (year = 0, total = 0; year < YEARS; year++)
			total += *(*(rain + year) + month);
		printf("%4.1f ", total / YEARS);
	}
	putchar('\n');

	return 0;
}


2.编写一个程序,初始化一个double数组,然后把数组内容复制到另外两个数组(3个数组都需要在主程序中声明)。

制作第一份拷贝的函数使用数组符号。制作第二份拷贝的函数使用指针符号,并使用指针的增量操作。

把目标数组名和要复制的元素数目做为参数传递给函数。也就是说,如果给定了下列声明,函数调用应该如下面所示:

    double  source [5]={1.1,  2.2,  3.3,  4.4,  5.5};

    double  targetl[5];

    double  target2 [5];

    copy_arr (source, target1, 5);

    copy_ptr (source, target1,5);

#include <stdio.h>

void copy_ptr(double * source, double * target, int n);
void copy_arr(double source[], double target[], int n);
void show(double *, int);

int main(void)
{
	double source[5] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
	double target1[5] = { 0 };
	double target2[5] = { 0 };

	copy_arr(source, target1, 2);
	copy_ptr(source, target2, 4);

	printf("source: "); show(source, 5);
	printf("target1: "); show(target1, 5);
	printf("target2: "); show(target2, 5);
	
	return 0;
}

void copy_ptr(double * source, double * target, int n)
{
	int i;
	for (i = 0; i < n; i++)
		*target++ = *source++;
}
void copy_arr(double source[], double target[], int n)
{
	int i;
	for (i = 0; i < n; i++)
		target[i] = source[i];
}

void show(double * a, int n)
{
	int i;
	for (i = 0; i < n; i++)
		printf("%.2lf ", a[i]);
	printf("\n");
}


3.编写一个函数,返回一个int数组中存储的最大数值,并在一个简单的程序中测试这个函数。

#include <stdio.h>

int max(int *, int);

int main(void)
{
	int a[5] = { 2, 4, 1, 0, -3 };

	printf("数组中最大的值:%d\n", max(a, 5));
	
	return 0;
}

int max(int * a, int n)
{
	int i, temp = a[0];
	for (i = 1; i < n; i++)
	{
		if (temp < a[i])
			temp = a[i];
	}	

	return temp;
}


4.编写一个函数,返回一个double数组中存储的最大数值的索引,并在一个简单程序中测试这个函数。

#include <stdio.h>

int max(double *, int);

int main(void)
{
	double a[5] = { 25.6, 41.2, 13.0, 0.5, -0.03 };

	printf("数组中最大值的索引:%d\n", max(a, 5));
	
	return 0;
}

int max(double * a, int n)
{
	int i, temp = 0;
	for (i = 1; i < n; i++)
	{
		if (a[temp] < a[i])
		{
			temp = i;
		}		
	}	

	return temp;
}


5.编写一个函数,返回一个double数组中最大的和最小的数之间的差值,并在一个简单的程序中测试这个函数。

#include <stdio.h>

double sub(const double *, int);

int main(void)
{
	double a[5] = { 25.6, 41.2, 13.0, 0.5, 0.03 };

	printf("数组中最大值和最小值的差值:%.2lf\n", sub(a, 5));
	
	return 0;
}

double sub(const double * a, int n)
{
	int i, max = 0, min = 0;
	for (i = 1; i < n; i++)
	{
		if (a[max] < a[i])
			max = i;	
		if (a[min] > a[i])
			min = i;
	}	

	return a[max] - a[min];
}


6.编写一个程序,初始化一个二维double数组,并利用练习2中的任一函数来把这个数组复制到另一个二维数组(因为二维数组是数组的数组,所以可以使用处理一维数组的函数来复制数组的每个子数组)。

#include <stdio.h>

void copy_ptr(double * source, double * target, int n);

int main(void)
{
	double source[2][5] = {
		{ 25.6, 41.2, 13.0, 0.5, 0.03 },
		{ 12.0, 23.0, -12.0, 3.0, 0.9 }
	};
	double target[2][5] = { 0 };

	copy_ptr(source[0], target[0], 8);

	printf("target: ");
	for (int i = 0; i < 2; i++)
	{
		for (int j = 0; j < 5; j++)
			printf("%.2lf ", target[i][j]);
	}
	printf("\n");
	
	return 0;
}

void copy_ptr(double * source, double * target, int n)
{
	int i;
	for (i = 0; i < n; i++)
		*target++ = *source++;
}


7.利用练习2中的复制函数,把一个包含7个元素的数组内第3到第5元素复制到一个包含3个元素的数组中,函数本身不需要修改,只需要选择合适的实际参数(实际参数不需要是数组名和数组大小,而只需是数组元素的地址和需要复制的元素数目)

#include <stdio.h>

void copy_ptr(double * source, double * target, int n);

int main(void)
{
	double source[7] = 
		{ 25.6, 41.2, 13.0, 0.5, 0.03, 3.0, 0.9 };
	double target[3] = { 0 };

	copy_ptr(&source[2], target, 3);

	printf("target: ");
	for (int i = 0; i < 3; i++)
		printf("%.2lf ", target[i]);

	printf("\n");
	
	return 0;
}

void copy_ptr(double * source, double * target, int n)
{
	int i;
	for (i = 0; i < n; i++)
		*target++ = *source++;
}


8.编写一个程序,初始化一个3*5的二维double数组,并利用一个基于变长数组的函数把该数组复制到别一个二维数组。还要编写一个基于变长数组的函数来显示两个数组的内容。这两个函数应该能够处理任意的N*M数组(如果没有可支持变长数组的编译器,就使用传统C中处理N*5数组的函数方法)。

#include <stdio.h>

void copy_ptr(int x, int y, double source[x][y], double target[x][y]);
void show(int x, int y, double source[x][y], double target[x][y]);
int main(void)
{
	double source[3][5] = {
		{ 25.6, 41.2, 13.0, 0.5, 0.03},
		{ 21.0, 32.0, 10.9, 5.6, 4.2 },
		{ 41.2, 13.0, 0.5, 2.0, 32.1 }
	};
	double target[3][5] = { 0 };

	copy_ptr(3, 5, source, target);

	printf("source: \n"); 
	show(3, 5, source, target);
	printf("target: \n");
	show(3, 5, source, target);
	
	return 0;
}

/* VC2013不支持变长数组,用gcc编译可通过 */
void copy_ptr(int x, int y, double source[x][y], double target[x][y])
{
	int i, j;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
			target[i][j] = source[i][j];
	}
		
}

void show(int x, int y, double source[x][y], double target[x][y])
{
	int i, j;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
			printf("%.2lf ", target[i][j]);
		printf("\n");
	}
}


9.编写一个函数,把两个数组内的相应元素相加,结果存储到第3个数组内。也就是说,如果数组l具有值2、4、5、8,数组2具有值1、0、4、6,则函数对数组3赋值为3、4、9、140函数的参数包括3个数组名和数组大小。并在一个简单的程序中测试这个函数。

#include <stdio.h>
#define LEN 5
void add(const int *, const int *, int *, int);
void show(int *, int);
int main(void)
{
	int array1[LEN] = { 1, 3, 6, 8, 2 };
	int array2[LEN] = { 2, 4, 7, 4, 9 };
	int array3[LEN] = { 0 };

	add(array1,array2,array3,LEN);

	printf("array1: \n"); show(array1, LEN);
	printf("array2: \n"); show(array2, LEN);
	printf("array3: \n"); show(array3, LEN);
	
	return 0;
}

void add(const int *a1, const int *a2, int *a3, int n)
{
	for (int i = 0; i < n; i++)
		a3[i] = a1[i] + a2[i];
		
}

void show(int *a, int n)
{
	for (int i = 0; i < n; i++)
		printf("%5d ", a[i]);
	printf("\n");
}


10.编写一个程序,声明一个3x5的数组并初始化,具体数值可以随意。程序打印出数值,然后数值翻1番,接着再次打印出新值。编写一个函数来显示数组的内容,再编写另一个函数执行翻倍功能。数组名和数组行数作为参数由程序传递给函数

#include <stdio.h>
void add(int array[][5], int c);
void show(int (*a)[5], int);
int main(void)
{
	int array[3][5] ={
		{ 1, 3, 6, 8, 2 },
		{ 2, 4, 7, 4, 9 },
		{ 3, 2, 1, 2, 3 }
	};

	printf("array: \n"); show(array, 3);
	add(array, 3);
	printf("array: \n"); show(array, 3);
	
	return 0;
}

void add(int array[][5], int c)
{
	for (int i = 0; i < c; i++)
	for (int j = 0; j < 5; j++)
		array[i][j] *= 2;		
}

void show(int (*a)[5], int n)
{
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < 5; j++)
			printf("%5d ", a[i][j]);
		printf("\n");
	}
}


11.重写程序清单10.7的程序rain,main()中的主要功能改为由函数来执行。(针对若干年的降水量数据,计算年降水总量、年降水平均量,以及月降水平均量)

# include <stdio.h>
# define MONTHS 12
# define YEARS 5
float year_total(float a[][MONTHS], int n);		//五年总降水量
float month_sub(float a[][MONTHS], int n);		//每个月的平均降水量
int main(void)
{
	const float rain[YEARS][MONTHS] =
	{
		{ 4.3, 4.3, 4.3, 3.0, 2.0, 1.2, 0.2, 0.2, 0.4, 2.4, 3.5, 6.6 },
		{ 8.5, 8.2, 1.2, 1.6, 2.4, 0.0, 5.2, 0.9, 0.3, 0.9, 1.4, 7.3 },
		{ 9.1, 8.5, 6.7, 4.3, 2.1, 0.8, 0.2, 0.2, 1.1, 2.3, 6.1, 8.4 },
		{ 7.2, 9.9, 8.4, 3.3, 1.2, 0.8, 0.4, 0.0, 0.6, 1.7, 4.3, 6.2 },
		{ 7.6, 5.6, 3.8, 2.8, 3.8, 0.2, 0.0, 0.0, 0.0, 1.3, 2.6, 5.2 }
	};
	
	year_total(rain, YEARS);
	month_sub(rain, YEARS);
	
	return 0;
}
float year_total(float a[][MONTHS], int n)
{
	int i, j;
	float total_y, total;
	for (i = 0, total = 0; i < n; i++)
	{
		for (j = 0, total_y = 0; j < MONTHS; j++)
			total_y += a[i][j];
		printf("%d年总降水量:%.1f\n", 2010 + i, total_y);
		total += total_y;
	}
	printf("%d年-%d年平均降水量:%.1f\n", 2010, 2010 + i-1, total / YEARS);
		
	return total;
}

float month_sub(float a[][MONTHS], int n)
{
	int i, j;
	float total;
	for (i = 0; i < MONTHS; i++)
	{
		for (j = 0, total = 0; j < n; j++)
			total += a[j][i];
		printf("%d年-%d年 %d月的平均降水量%.1f\n", 2010, 2010+j-1, i+1, total/YEARS);
	}
	
	return total;
}


12.编写…个程序,提示用户输入3个数集,每个数集包括5个double值。程序应当实现下列所有功能: 

    a.把输入信息存储到一个3x5的数组中

    b.计算出每个数集(包含5个数值)的平均值

    c.计算所有数值的平均数

    d.找出这15个数中的最大值.

    e.打印出结果

    每个任务需要用一个单独的函数来实现(使用传统C处理数组的方法)。对于任务b,需要编写计算并返回一维数组平均值的函数,循环3次调用该函数来实现任务b。

对于其他任务,函数应当把整个数组做为参数,并且完成任务c和d的函数应该向它的调用函数返回答案。

#include <stdio.h>

void input(double(*a)[5], int n);
double sub(const double *a, int n);
double sub_total(const double a[][5], int n);
double max(const double a[][5], int n);
void show(const double a[][5], int n);

int main(void)
{
	double array[3][5];
	int i;

	input(array, 3);
	for (i = 0; i < 3; i++)
		printf("第%d个数集的平均值:%.2lf\n",i+1, sub(array[i], 5));
	printf("所有数的平均值:%.2lf\n", sub_total(array, 3));
	printf("所有数中最大的数:%.2lf\n", max(array, 5));
	printf("整个数集:\n");
	show(array, 3);

	return 0;
}
void input(double(*a)[5], int n)
{
	int i, j;
	for (i = 0; i < n; i++)
	{
		printf("请输入第%d个数集中的5个值:", i+1);
		for (j = 0; j < 5; j++)
			scanf("%lf", &a[i][j]);
	}
}
double sub(const double *a, int n)
{
	int i;
	double total = 0;
	for (i = 0; i < n; i++)
		total += a[i];
	
	return total / n;
}
double sub_total(const double a[][5], int n)
{
	int i, j;
	double total = 0;
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < 5; j++)
			total += a[i][j];
	}

	return total / (n * 5);
}
double max(const double a[][5], int n)
{
	int i, j;
	double temp = a[0][0];
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < 5; j++)
		if (temp < a[i][j])
			temp = a[i][j];
	}

	return temp;
}
void show(const double a[][5], int n)
{
	int i, j;
	double max = a[0][0];
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < 5; j++)
			printf("%.2lf ", a[i][j]);
		printf("\n");
	}
}


13.下面是两个函数原型:

void show(double ar[], int n); //n是元素数

void show2(double ar2[][3], int n); //n是行数

a,编写一个函数调用,把包含数值8、3、9和2的复合文字传递给函数shows()。

b,编写一个函数调用,把包含2行3列数值的复合文字传递给函数show2(),其中第一行为8、3、9;第二行为5、4、1。

#include <stdio.h>

void show(double ar[], int n);
void show2(double ar2[][3], int n);

int main(void)
{
	show((double[]){ 8, 3, 9, 2 }, 4);
	show2((double[][3]){ { 8, 3, 9 }, { 5, 4, 1 } }, 2);

	return 0;
}
void show(double ar[], int n)
{
	int i;
	for (i = 0; i < n; i++)
		printf("%.2lf ", ar[i]);
	printf("\n");
}
void show2(double ar2[][3], int n)
{
	int i, j;
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < 3; j++)
			printf("%.2lf ", ar2[i][j]);
		printf("\n");
	}		
}