C —— 数组

数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。

声明数组

要声明一个数组,需要指定元素的类型和元素的数量,比如需要声明一个长度为5的整型数组,代码如下:

int nums[5];

如果需要在声明数组的同时为其赋予初始值,代码如下:

int nums[5] = {101, 102, 103, 104, 105};

读取或者修改数组中的数据采用数组下标的形式,在 C 中,数组的下标固定从 0 开始计数,例如上面的数组变量 nums ,它的每个元素都有其下标与之对应,如下所示:

元素	101, 102, 103, 104, 105
		 ↑    ↑    ↑    ↑    ↑
下标	 0 ,  1 ,  2 ,  3 ,  4

故而,可以使用下标 1 来读取数组中的 102,如 nums[1],如果需要将其全部取出,可以使用循环结构,如下所示:

#include<stdio.h>

int main() {
	int i, nums[5] = {101, 102, 103, 104, 105};
	for(i = 0; i < 5; i++) {
		printf("nums[%d]=%d\n", i, nums[i]);
	}
	return 0;
}

执行结果:

nums[0]=101
nums[1]=102
nums[2]=103
nums[3]=104
nums[4]=105

除了可以使用下标读取数组中的值之外,还能使用下标修改对应下标的值,如下例所示:

#include<stdio.h>

int main() {
	int i, nums[5] = {101, 102, 103, 104, 105};
	for(i = 0; i < 5; i++) {
		nums[i] *= 10;
	}
	for(i = 0; i < 5; i++) {
		printf("nums[%d]=%d\n", i, nums[i]);
	}
	return 0;
}

执行结果:

nums[0]=1010
nums[1]=1020
nums[2]=1030
nums[3]=1040
nums[4]=1050

同样的,也可以使用循环结构对数组进行初始化操作,如下所示:

#include<stdio.h>
#include<stdlib.h>

int main() {
	int i, nums[5];
	for(i = 0; i < 5; i++) {
		nums[i] = rand();
	}
	for(i = 0; i < 5; i++) {
		printf("nums[%d]=%d\n", i, nums[i]);
	}
	return 0;
}

从上例可以看到,数组 nums 在声明的时候并未进行初始化操作,经过了第一个 for 循环后, 数组的每一个元素都存在元素了,再使用第二个 for 进行遍历输出,得到如下结果:

nums[0]=41
nums[1]=18467
nums[2]=6334
nums[3]=26500
nums[4]=19169

当然,这里使用到了一个 来自 stdlib 中的 rand 函数,该函数用于产生随机数,不过会发现,多次运行后,通过 rand 函数得到的数据几乎是一样的,这时需要初始化随机函数的种子,让系统时间作为种子,使得种子随着时间的变化而变化,可以产生不相同的随机数。修改源程序如下:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int main() {
	int i, nums[5];
	
	srand((unsigned)time(NULL));
	for(i = 0; i < 5; i++) {
		nums[i] = rand();
	}
	for(i = 0; i < 5; i++) {
		printf("nums[%d]=%d\n", i, nums[i]);
	}
	return 0;
}

这样调整之后,每次程序执行的结果就不一样了。

多维数组

C 支持多维数组。多维数组最简单的形式是二维数组。示例如下:

#include<stdio.h>

int main() {
	int cs[2][3] = {{101, 202, 303}, {505, 606, 707}};
	int x, y;
	for(x = 0; x < 2; x++) {
		for(y = 0; y < 3; y++) {
			printf("(%d, %d) = %d\n", x, y, cs[x][y]);
		}
	}
	return 0;
}

执行结果:

(0, 0) = 101
(0, 1) = 202
(0, 2) = 303
(1, 0) = 505
(1, 1) = 606
(1, 2) = 707

从本例可以看到,声明一个二维数组的实际上就是在一维数组的基础上再嵌套一个一维数组。对应的下标就复杂一点了,如下所示:


元素	{101, 202, 303}, {505, 606, 707}
		       ↑                ↑
一维下标        0                1
		  ↑    ↑    ↑      ↑    ↑    ↑
二维下标   0 ,  1 ,  2      0 ,  1,   2

同理,声明一个三维数组,只需要再嵌套一层即可,如下例所示:

#include<stdio.h>

int main() {
	int cs[2][3][2] = {
		{{101, 202}, {303, 404}, {505, 606}}, 
		{{707, 808}, {909, 111}, {222, 333}}
	};
	int x, y, z;
	for(x = 0; x < 2; x++) {
		for(y = 0; y < 3; y++) {
			for(z = 0; z < 2; z++) {
				printf("(%d, %d, %d) = %d\n", x, y, z, cs[x][y][z]);
			}
		}
	}
	return 0;
}

执行结果:

(0, 0, 0) = 101
(0, 0, 1) = 202
(0, 1, 0) = 303
(0, 1, 1) = 404
(0, 2, 0) = 505
(0, 2, 1) = 606
(1, 0, 0) = 707
(1, 0, 1) = 808
(1, 1, 0) = 909
(1, 1, 1) = 111
(1, 2, 0) = 222
(1, 2, 1) = 333

之前在循环结构章节中,提到过一个九九乘法表,展示的结果和二维数组非常相似,我们可以尝试将九九乘法表装载到二维数组中,让乘数和被乘数分别作为二维数组的坐标,乘积作为对应的坐标的值。如下例所示:

#include<stdio.h>

int main() {
	int x, y, product, mts[10][10];
	for(x = 1; x < 10; x++) {
		for(y = 1; y <= x; y++) {
			mts[x - 1][y - 1] = x * y;
		}
	}
	for(x = 1; x < 10; x++) {
		for(y = 1; y <= x; y++) {
			product = mts[x - 1][y - 1];
			if(product > 9) {
				printf("%dx%d=%d ", y, x, product);
			} else {
				printf("%dx%d=0%d ", y, x, product);
			}
		}
		printf("\n");
	}
	return 0;
}

执行结果:

1x1=01
1x2=02 2x2=04
1x3=03 2x3=06 3x3=09
1x4=04 2x4=08 3x4=12 4x4=16
1x5=05 2x5=10 3x5=15 4x5=20 5x5=25
1x6=06 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36
1x7=07 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49
1x8=08 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64
1x9=09 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81

当然,这里声明一个 10x10 的二维数组貌似有点浪费,毕竟坐标零位置的数据是没有被填充的,如果您有更好的解决方案,请在留评论区给我留言吧。

传递数组给函数

数组也是一个变量,传递到函数中的方式跟变量一样。所以在声明函数的时候,用于接受数组的形参可以申明为数组或者指针。

声明为数组类型的形参,代码如下:

#include<stdio.h>

void foreach(int arrays[], int size);

int main() {
	int nums[6] = {101, 202, 303, 404, 505, 606};
	foreach(nums, 6);
	return 0;
}

void foreach(int arrays[], int size) {
	for(int i = 0; i < size; i++) {
		printf("arrays[%d]=%d\n", i, arrays[i]);
	}
}

执行结果:

arrays[0]=101
arrays[1]=202
arrays[2]=303
arrays[3]=404
arrays[4]=505
arrays[5]=606

声明为指针类型的形参,代码如下:

#include<stdio.h>

void foreach(int *ps, int size);

int main() {
	int nums[6] = {101, 202, 303, 404, 505, 606};
	foreach(nums, 6);
	return 0;
}

void foreach(int *ps, int size) {
	for(int i = 0; i < size; i++, ps++) {
		printf("arrays[%d]=%d\n", i, *ps);
	}
}

执行结果:

arrays[0]=101
arrays[1]=202
arrays[2]=303
arrays[3]=404
arrays[4]=505
arrays[5]=606

在上一篇写 函数 的文章中给 swap 中的 int *x, int *y 传递数据的时候,用到了取址符 & 来传递地址值的,也就是 swap(&a, &b) 这样,但是在给数组地址值的时候,却没有使用到取址符 & 。关于这个内容,将会在后面 指针 的文章中详细说明。

从函数返回数组

C 语言不允许返回一个完整的数组作为函数的返回类型。但是可以通过指定不带索引的数组名来返回一个指向数组的指针。如下所示:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define SIZE 6

int* randArrays();

int main() {
	int *ps = randArrays();
	for(int i = 0; i < SIZE; i++, ps++) {
		printf("arrays[%d]=%d\n", i, *ps);
	}
	return 0;
}

/**
 * 从函数返回数组
 */
int* randArrays() {	
	static int nums[SIZE];
	srand((unsigned)time(NULL));
	for(int i = 0; i < SIZE; i++) {
		nums[i] = rand();
	}
	return nums;
}

执行结果:

arrays[0]=24988
arrays[1]=19923
arrays[2]=10648
arrays[3]=28478
arrays[4]=9435
arrays[5]=31751

函数 randArrays 内的变量 nums 采用 static 修饰的原因是啥?

因为变量生命周期的原因,当函数执行结束后,函数体内的局部变量自然被销毁,在之前的 C 存储类 的文章中提到过:使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。函数直接返回该局部变量将会在编译的时候抛出警告:

warning: function returns address of local variable

有关指针的更多内容请移步到《指针

数组排序

若存在一个无序的数组 nums, 如何实现将升序或者降序排列。

选择排序(Selection sort)

#include<stdio.h>

int main() {
	int nums[] = {4, 5, 2, 45, 7, 821, 16, 333, 8, 342, 1};
	int len = sizeof(nums) / sizeof(nums[0]);
	// 是否升序
	int asc = 0;
	for(int i = len - 1; i > 0;i--) {
		// 查找最大或最小的数字的索引
		int maxindex = 0;
		for(int j = 0; j < i + 1; j++) {
			if(asc ? nums[maxindex] < nums[j] : nums[maxindex] > nums[j]) {
				maxindex = j;
			}
		}
		// 将找到的数字与第 i 个进行交换
		int t = nums[i];
		nums[i] = nums[maxindex];
		nums[maxindex] = t;
	}
	// 输出结果:
	for(int i = 0;i < len;i++) {
		printf("%d ", nums[i]);
	}
	return 0;
}

冒泡排序(Bubble Sort)

#include<stdio.h>

int main() {
	int nums[] = {4, 5, 2, 45, 7, 821, 16, 333, 8, 342, 1};
	int len = sizeof(nums) / sizeof(nums[0]);
	// 是否升序
	int asc = 0;
	for (int i = 0;i < len - 1; i++) {
		for (int j = 0; j < len - 1 - i; j++) {
			if (asc ? nums[j] > nums[j + 1] : nums[j] < nums[j + 1]) {
				int t = nums[j];
				nums[j] = nums[j + 1];
				nums[j + 1] = t;
			}
		}
	}
	
	// 输出结果:
	for(int i = 0;i < len;i++) {
		printf("%d ", nums[i]);
	}
	return 0;
}

插入排序(Insertion Sort)

#include<stdio.h>

int main() {
	int nums[] = {4, 5, 2, 45, 7, 821, 16, 333, 8, 342, 1};
	int len = sizeof(nums) / sizeof(nums[0]);
	// 是否升序
	int asc = 0;
	
	if(len > 1) {
		for(int i = 1; i < len; i++) {
			int t = nums[i], j = i;
			for( ; j > 0 && (asc ? nums[j - 1] > t : nums[j - 1] < t); j--) {
				nums[j] = nums[j-1];
			}
			nums[j] = t;
		}
	}
	
	// 输出结果:
	for(int i = 0;i < len;i++) {
		printf("%d ", nums[i]);
	}
	return 0;
}

希尔排序(Shell’s Sort)

#include<stdio.h>

int main() {
	int nums[] = {4, 5, 2, 45, 7, 821, 16, 333, 8, 342, 1};
	int len = sizeof(nums) / sizeof(nums[0]);
	// 是否升序
	int asc = 0;
	
	for (int gap = len >> 1; gap > 0; gap = gap >> 1) {
		for (int i = gap; i < len; i++) {
			int t = nums[i], j = i - gap;
			for ( ; j >= 0 && (asc ? nums[j] > t : nums[j] < t); j -= gap) {
				nums[j + gap] = nums[j];
			}
			nums[j + gap] = t;
		}
	}
	
	// 输出结果:
	for(int i = 0;i < len;i++) {
		printf("%d ", nums[i]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值