1.数组元素作为函数参数的使用
/*
数组元素,单独拿出来就与普通变量一样
int nums[] = {1, 2, 3};
nums[0] = 1;
1)赋值: nums[0] = 123;
2)取值: int res = nums[0];
3)运算: nums[0]++; === nums[0] = nums[0] + 1;
函数参数传递的方式
如果函数的参数是如例一样有基本类型定义(int,long,float,char...)
那么函数的调用就为值传递,实际调用时使用的参数是实参.
在调用中,将实参的数值(内存中存储的值)复制一份,传递给函数的形参
*/
练习:2、写一个程序,初始化一个数组。要求数组长度为10,里面的值为0-9的随机数,并且每一个数字出现一次。
# define LEN 128
// 1,创建数组与必要的变量
int nums[LEN], i = 0, temp, index;
// 2,开始循环创建随机数
while(i < 10) {
// 3,生成随机数
temp = arc4random_uniform(10); // 0-9的随机数
// 0 i=0 index==i 成立 i++,随机数存到nums[0]中
// 1 i=1 如过创建的随机数与上一次一样,就等于本次什么也不做
// 2 i=1 随机数不同,index == i 成立,i++,随机数放到 nums[1] 中
// 4,判断temp是否在数组中
for (index = 0; index < i; index++) {
if(temp == nums[index]) {
// 表示找到相同的了,即数组中存在
break;
}
}
// 5,怎么判断是存在呢?
if(index == i) {
// for循环正常结束,即不存在
nums[i++] = temp;
}
}
// 将随机数打印出来即可
for(int j = 0; j < 10; j++) {
printf("%d.\t%d\n", j + 1, nums[j]);
}
2.将数组名作为函数参数的使用
/*
将数组名作为函数参数
调用语法:
int nums[] = {...};
func(nums);
需要注意,函数的声明语法为
返回类型 函数名(数组类型 形参变量名[]);
void func(int arr[])
补充说明:
1)数组名作为函数参数,此时是传递的数组的首地址
2)传递的时地址,那么成为引用传递
3)如果传入的时地址,那么在函数中的数组与在函数外的数组是同一个数组
4)函数传参的过程中,只是将数组的地址传递过去了,数组的长度会丢失
5)一般数组名作为函数参数的时候,函数会带有第二个参数,用于表示数组的长度
void func(int arr[], int length);
有的代码会将数组作为函数参数的函数定义为
void func(int arr[3], int length); // 针对初学者好理解
// int * int []
void func(int *arr, int length); // 指针的写法
如果是传递的地址,就称为引用传递.引用传递函数内对数据的修改会在函数结束后保持下来
void func(int num) {
num = 10;
}
void func(int num[]) {
num[0] = 10;
}
*/
练习:初始化一个数组,并用函数求该数组的和,最大值,最小值,平均值
// 写一个可以对数组初始化的函数
void initArray(int nums[], int length) {
printf("请输入数字\n");
for (int i = 0; i < length; i++) {
scanf("%d", &nums[i]);
}
}
// 写函数去求数组的最大最小\和与平均
int max(int nums[], int length) {
int maximun = nums[0];
for (int i = 1; i < length; i++) {
if(nums[i] > maximun) maximun = nums[i];
}
return maximun;
}
int min(int nums[], int length) {
int minimun = nums[0];
for (int i = 1; i < length; i++) {
if(nums[i] < minimun) minimun = nums[i];
}
return minimun;
}
int sum(int nums[], int length) {
int sum = 0;
for (int i = 0; i < length; i++) {
sum+=nums[i];
}
return sum;
}
double avg(int nums[], int length) {
return sum(nums, length) * 1.0 / length;
}
int main(int argc, const char * argv[]) {
int nums[10];
initArray(nums, 10);
printf("最大值为%d\n", max(nums, 10));
printf("最小值为%d\n", min(nums, 10));
printf("平均值为%lf\n", avg(nums, 10));
printf("和为%d\n", sum(nums, 10));
3.冒泡排序
冒泡排序的思想:每趟从第一个开始依次比较,选出一个最大的或最小的值排在最后,直到排成有序数列为止.
//推倒思路
// 写一个可以对数组初始化的函数
void initArray(int nums[], int length) {
printf("请输入数字\n");
for (int i = 0; i < length; i++) {
scanf("%d", &nums[i]);
}
}
// 写函数去求数组的最大最小\和与平均
int max(int nums[], int length) {
int maximun = nums[0];
for (int i = 1; i < length; i++) {
if(nums[i] > maximun) maximun = nums[i];
}
return maximun;
}
int min(int nums[], int length) {
int minimun = nums[0];
for (int i = 1; i < length; i++) {
if(nums[i] < minimun) minimun = nums[i];
}
return minimun;
}
int sum(int nums[], int length) {
int sum = 0;
for (int i = 0; i < length; i++) {
sum+=nums[i];
}
return sum;
}
double avg(int nums[], int length) {
return sum(nums, length) * 1.0 / length;
}
int main(int argc, const char * argv[]) {
int nums[10];
initArray(nums, 10);
printf("最大值为%d\n", max(nums, 10));
printf("最小值为%d\n", min(nums, 10));
printf("平均值为%lf\n", avg(nums, 10));
printf("和为%d\n", sum(nums, 10));
排序算法代码:
void bubbleSort(int nums[], int length) {
for (int i = 0; i < length - 1; i++) {
for (int j = 0; j < length - 1 - i; j++) {
if(nums[j] > nums[j+1]) {
int temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
}
}
}
}
4.选择排序
排序思想:将数列第一个位置的数设为最小或最大,每次与第一个比较,满足条件就放在第一个,再从第二个开始,以此类推.
//推导过程
for (int i = 0; i < 4; i++) {
if(nums[0] > nums[i]) {
temp = nums[0];
nums[0] = nums[i];
nums[i] = temp;
}
} // 2, 9, 7, 3
for (int i = 1; i < 4; i++) {
if(nums[1] > nums[i]) {
temp = nums[1];
nums[1] = nums[i];
nums[i] = temp;
}
}// 2, 3, 9, 7
for (int i = 2; i < 4; i++) {
if(nums[2] > nums[i]) {
temp = nums[2];
nums[2] = nums[i];
nums[i] = temp;
}
}// 2, 3, 7, 9
for (int i = 3; i < 4; i++) {
if(nums[3] > nums[i]) {
temp = nums[3];
nums[3] = nums[i];
nums[i] = temp;
}
}// 2, 3, 7, 9
选择排序算法代码:
for (int j = 0; j < n - 1; j++) {
for (int i = j + 1; i < n; i++) {
if(nums[j] > nums[i]) {
temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
}
}
}
选择排序算法代码优化:
int min = -1;
for(int j = 0; j < 4 - 1; j++) {
min = j; // 记录了最小取值的下标(索引)
for (int i = j + 1; i < 4; i++) {
if(nums[min] > nums[i]) {
min = i;
}
}
// 将最小的与第j项交换
if(min != j) {
temp = nums[min];
nums[min] = nums[j];
nums[j] = temp;
}
}
5.二分法查找(折半查找)
算法思想:将数列第一个元素设为high,最后一个设为low,取mid等于high+low除以2,判断要查找的值比mid所指的元素大还是小,如果大证明在mid右边的区域,将low设为mid+1,如果小则将high-1,以此类推,如果high
// 方法的返回值可以用于表示下标,如为-1表示没有找到
int indexOf(int nums[], int length, int key) {
int low = 0, high = length - 1, mid;
while (low <= high) {
mid = (low + high) / 2;
int obj = nums[mid];
if(key == obj) {
return mid;
} else if(key > obj) {
// 我的数据在 mid 到 high 之间
low = mid + 1; // 12
} else {
// 在 low 与 mid 之间
high = mid - 1;
}
}
// 说明没有找到
return -1;
}
6.二维数组
在C语言中,如果某一个数据需要用两个数据才能表示,可以使用二维数组
语法:
数组数据类型 数组名[常量表达式][常量表达式];元素类型 数组名[行数][列数];
二维数组的初始化
1)定义的同时初始化
-> 分段初始化
int a[2][3] = {
{ 1, 2, 3 },
{ 4, 5, 6 }
};
-> 连续初始化
int a[2][3] = {
1, 2, 3,
4, 5, 6
};
-> 多维数组可以省略第一个下标
int a[][3] = {
1, 2, 3,
4, 5, 6
};
- 二维数组的内存结构
- 二维数组在概念上是二维的,即需要两个下标进行标识。虽说二维数组是一个二维结构,但是在实际硬件存储中依旧是一维的。也就是说,内存结构是线性结构,存储的数据也只能是线性的。那么二维数组是如何存储呢?
由于二维数组可以看做是,元素为一维数组的一维数组。因此内存中先存储数组的第一个元素,而第一个元素又是一个数组,也就是说首先存储这个数组。然后再存储第二个数组。例如:
int a[3][4];
逻辑结构为:
00 01 02 03
10 11 12 13
20 21 22 23
而此处第0个元素为
a[0] 00 01 02 03
因此内存首先存储这个数组,然后存储第1个数组
a[1] 10 11 12 13
最后存储第2个数组
a[2] 20 21 22 23
也就是说内存的物理结构为:
00
01
02
03
10
12
13
20
21
22
23
这里需要注意的是:
1)数组名表示数组的首地址
2)数组的首地址等于第一个数组的首地址
3)数组的首地址也等于第一个元素的首地址
// 二维数组的第一个元素与子数组与二维数组的首地址一样
// nums[0][0]
// array[0]
printf("\n\n\n");
printf("nums\t\t=\t%p\n", nums);
printf("nums[0]\t=\t%p\n", nums[0]);
printf("&nums[0][0]\t=\t%p\n", &nums[0][0]);
//nums 与 nums[0] 和 nums[0][0]的地址相同
printf("\n\n\n");
printf("nums[1]\t=\t%p\n", nums[1]);
printf("&nums[1][0]\t=\t%p\n", &nums[1][0]);
//nums[1]与nums[1][0]的地址相同
printf("\n\n\n");
int nums[2][3];
printf("%lu\n", sizeof(nums)); // 24
printf("%lu\n", sizeof(nums[0])); //12
printf("%lu\n", sizeof(nums[1])); //12
- 二维数组名做函数参数
二维数组名作为函数参数
数组名表示数组的首地址,因此将数组名作为函数参数是引用传递
需要注意的是:
1)在函数中,数组会丢失长度信息
2)函数定义中,可以省略数组的第一个长度
二维数组名作为函数参数只需要搞清楚函数如何定义
void func(int nums[][列数], int rows, int cloumns);
练习:输入数字初始化二维数组
int rows, cols;
printf("请输入两个数字\n");
scanf("%d", &rows);
scanf("%d", &cols);
int num[rows][cols];
// 利用循环初始化
for(int i= 0; i< rows; i++) {
for(int j = 0; j < cols; j++) {
scanf("%d", &num[i][j]);
}
}
printf("输入完毕,打印结果为\n");
for(int i= 0; i< rows; i++) {
for(int j = 0; j < cols; j++) {
printf("%d\t", num[i][j]);
}
printf("\n");
}
7.迷宫游戏
1、游戏界面简介
“`
{#,#,#,#,#,#},
{#,O,#,#, , },
{#, ,#,#, ,#},
{#, , ,#, ,#},
{#,#, , , ,#},
{#,#,#,#,#,#},
其中 # 代表墙,O 代表人,空白代表路
利用 w,s,a,d 控制小人走出迷宫
这里利用一个二维数组记录迷宫的结构,为例方便编写,假定向下为 x 轴,向右为 y 轴。如此一来,数组就可以定义为 a[x][y]
2、控制说明
W 或 w 代表向上,实际只需要将 x - 1 即可
S 或 s 代表向下,只需要 x + 1
A 或 a 代表向左,只需要 y - 1
D 或 d 代表向右,只用 y + 1 即可
实现代码:
int main(int argc, const char * argv[]) {
// 1,设计字符数组,用于显示地图与玩家的位置
char map[ROW][COL]={
{'#','#','#','#','#','#'},
{'#','O','#','#',' ',' '},
{'#',' ','#','#',' ','#'},
{'#',' ',' ','#',' ','#'},
{'#','#',' ',' ',' ','#'},
{'#','#','#','#','#','#'}
};
char c, cTemp, temp;
int i = 1, j = 1;
while(1) {
// 2,打印地图
printMap(map);
// 判断用户是否赢了
if(j == COL - 1) break;
// 3,提示用户输入
printf("请玩家操作:w(上),s(下),a(左),d(右)\n");
// 4,获取用户数组
scanf("%c", &c);
scanf("%c", &cTemp);
// 5,判断wsad进行处理
switch (c) {
case 'w': // 向上 i - 1
// 6,判断下一个位置是否可以走
// 如果可以就走过去(交换空格与0的位置)
// 7,判断是否已经走出来了
if(map[i - 1][j] == ' ') {
// 可以走
// 先将 i,j 与 i-1,j 进行交换
temp = map[i][j];
map[i][j] = map[i - 1][j];
map[i - 1][j] = temp;
i--;
}
break;
case 's': // 向下 i + 1
if(map[i + 1][j] == ' ') {
temp = map[i][j];
map[i][j] = map[i + 1][j];
map[i + 1][j] = temp;
i++;
}
break;
case 'a': // 向左 j - 1
if(map[i][j - 1] == ' ') {
temp = map[i][j];
map[i][j] = map[i][j - 1];
map[i][j - 1] = temp;
j--;
}
break;
case 'd': // 向右 j + 1
if(map[i][j + 1] == ' ') {
temp = map[i][j];
map[i][j] = map[i][j + 1];
map[i][j + 1] = temp;
j++;
}
break;
}
}
printf("恭喜你通关\n");