1. 一维数组
1.1 数组的创建
- 数组是一组相同类型元素的组合。
- []内的指定数组大小的必须是一个常量表达式。(只有C99标准支持变长数组概念)
//数组的创建
type name [const_n] //type为数组的元素类型
//name为数组的名字
//const_n是一个常量表达式,用来指定数组的大小
例:int arr [8];
char ch [5];
1.2 数组的初始化
- 数组的初始化是指,在创建数组的同时给数组的内容一些合理的初始值(初始化)。
- arr1[5] = { 1, 2, 3 }; 中初始化后的元素为 1, 2, 3, 0, 0
//数组的初始化
int arr1[5] = { 1, 2, 3, 4, 5 }; //完全初始化 5个元素
int arr2[5] = { 1, 2, 3 }; //不完全初始化 5个元素
int arr3[] = { 1, 2, 3 }; //会根据初始化的内容决定数组的大小 3个元素 相当于int arr1[3] = { 1, 2, 3 };
char ch1[5] = { 'a', 'b','c' }; //5个元素
char ch2[] = { 'a', 'b','c' }; //3个元素
char ch3[5] = "abc"; //5个元素
char ch4[] = "abc"; //4个元素 因为字符串以\0结束
char ch5[] = { 'a', 'b','c' }; //3个元素
char ch6[] = "abc"; //4个元素 因为字符串以\0结束
//打印 ch5 结果为 abc烫烫烫烫n 随机值
//打印 ch6 结果为 abc
1.3 一维数组的使用
- [] 下标操作符。
- 数组是使用下标来访问的,下标是从0开始的。
- 数组的大小可以通过计算得到。
//一维数组的使用
#include <stdio.h>
int main()
{
int arr[10] = { 1 }; //数组的不完全初始化,第一个元素初始化为1,其余元素初始化为0
int sz = sizeof(arr) / sizeof(arr[0]); //计算数组的元素个数 数组总大小、一个元素大小
int i = 0; //做下标
for (i = 0; i < 10; i++)
{
arr[i] = i; //输出数组的内容
printf("%d ", arr[i]);
}
return 0;
} //输出结果为:0 1 2 3 4 5 6 7 8 9
1.4 一维数组在内存中的存储
- 一维数组在内存中是连续存放的,随着数组下标的增长,元素的地址,也有规律的递增。(由低到高)
- 若知道数组中某个元素的地址,那么其余的地址均可被找到。
- %p 打印地址 —— 0x12→00000012
- %x 打印地址 —— 0x12→12
- 十六进制:0 1 2 3 4 5 6 7 8 9 a b c d e f ...
//一维数组在内存中的存储
#include <stdio.h>
int main()
{
int arr[10] = { 1, 2 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < 10; i++)
{
printf("&arr[%d] = %p\n", i, &arr[i]); //打印地址时用%p
}
return 0;
} /*输出结果为:&arr[0] = 0078FCE8
&arr[1] = 0078FCEC
&arr[2] = 0078FCF0
&arr[3] = 0078FCF4
&arr[4] = 0078FCF8
&arr[5] = 0078FCFC
&arr[6] = 0078FD00
&arr[7] = 0078FD04
&arr[8] = 0078FD08
&arr[9] = 0078FD0C*/
//打印结果每个整形的地址,一个整形占4个字节→32个二进制位→8个十六进制位,因此打印出来时是8位
2. 二维数组
2.1 二维数组的创建
//二维数组的创建
int arr[3][4]; //三行四列
char ch[3][5]; //三行五列
2.2 二维数组的初始化
//二维数组的初始化
int arr1[3][4] = { 1, 2, 3, 4 ,5}; //三行四列 不完全初始化,12个元素按行依次排列,后面的初始化为0
int arr2[3][4] = { {1, 2}, {3, 4},{5} }; //三行四列 不完全初始化,括起来代表一行
int arr3[][4] = { {1, 2}, {3, 4} }; //两行四列 不完全初始化,行可以省略,列不能省略
代码中的数组如下图所示:
2.3 二维数组的使用
- 二维数组的使用也是通过下标进行访问的,行、列均从0开始标号,如上图所示。
//二维数组的使用
#include <stdio.h>
int main()
{
int arr[][5] = { {1,2},{5,6},{9},{4} };
int i = 0;
for (i = 0; i < 4; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
} /*输出结果为:1 2 0 0 0
5 6 0 0 0
9 0 0 0 0
4 0 0 0 0*/
2.4 二维数组在内存中的存储
- 二维数组在内存中也是连续存储的,一行内部连续,换行也是连续的。换行并不影响地址的连续性。
- 若知道数组中某个元素的地址,那么其余的地址均可被找到。
//二维数组在内存中的存储
#include <stdio.h>
int main()
{
int arr[][5] = { {1,2},{5,6},{9},{4} };
int i = 0;
for (i = 0; i < 4; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]); //打印地址时用%p
}
}
return 0;
} /*输出结果为:&arr[0][0] = 00F9F868
&arr[0][1] = 00F9F86C
&arr[0][2] = 00F9F870
&arr[0][3] = 00F9F874
&arr[0][4] = 00F9F878
&arr[1][0] = 00F9F87C
&arr[1][1] = 00F9F880
&arr[1][2] = 00F9F884
&arr[1][3] = 00F9F888
&arr[1][4] = 00F9F88C
&arr[2][0] = 00F9F890
&arr[2][1] = 00F9F894
&arr[2][2] = 00F9F898
&arr[2][3] = 00F9F89C
&arr[2][4] = 00F9F8A0
&arr[3][0] = 00F9F8A4
&arr[3][1] = 00F9F8A8
&arr[3][2] = 00F9F8AC
&arr[3][3] = 00F9F8B0
&arr[3][4] = 00F9F8B4*/
//打印结果每个整形的地址,一个整形占4个字节→32个二进制位→8个十六进制位,因此打印出来时是8位
3. 数组作为函数参数
- 将数组作为参数传递函数,例如:实现一个冒泡排序。
3.1 数组名是什么?
- 所有的数组名都表示数组首元素的地址。两种特出情况除外:
- sizeof(数组名)—— 计算整个数组的大小。
- &数组名 —— 取出的是整个数组的地址。
//数组名
#include <stdio.h>
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6 };
int i = 0;
printf("%p\n", arr);
printf("%p\n", arr+1); //两者相差四个字节,一个整形的距离,即打印的是一个元素的地址
printf("%p\n", &arr); //两者相差40个字节,10个整形的距离,即打印的是整个数组的地址
printf("%p\n", &arr+1);
printf("%d\n", sizeof(arr));
return 0;
} /*输出结果为:004FF998
004FF99C
004FF998
004FF9C0
40*/
3.2 冒泡排序函数
- 冒泡排序:将一组无序的的数排列成一组有序的数。
- 冒泡排序思想:两两相邻的元素进行比较,不符合排序条件则进行两元素的交换。
例:排升序:将 9 8 7 6 5 4 3 2 1 0 按照升序排列:
9 8 7 6 5 4 3 2 1 0
8 9 7 6 5 4 3 2 1 0
8 7 9 6 5 4 3 2 1 0
8 7 6 9 5 4 3 2 1 0
8 7 6 5 9 4 3 2 1 0
8 7 6 5 4 9 3 2 1 0
8 7 6 5 4 3 9 2 1 0
8 7 6 5 4 3 2 9 1 0
8 7 6 5 4 3 2 1 9 0
8 7 6 5 4 3 2 1 0 9
- 以上为一趟冒泡排序,一个数字一定会出现在他最终出现的位置,也就是说一趟冒泡排序解决一个数字,接着不管9,继续进行下一趟冒泡排序。(10个数字,最多进行9趟冒泡排序)
第1趟 | 第2趟 | 第3趟 | 第4趟 | 第5趟 | ... | 第i趟 | ... | 第n-1趟 | |
数字个数 | n | n-1 | n-2 | n-3 | n-4 | n-i-1 | 0 | ||
比较次数 | n-1 | n-2 | n-3 | n-4 | n-5 | n-i | 1 |
//冒泡排序
#include <stdio.h>
void bubble_sort(int arr[], int s)
{
int i = 0; //下标从0开始
for (i = 0; i < s - 1; i++) //确定趟数
{
int j = 0;
for (j = 0; j < s - i - 1; j++) //减去已经排序的元素
{
if (arr[j] > arr[j + 1])
{
int tmp = 0;
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
int main()
{
int arr[] = { 7, 2, 6, 8, 5, 9, 0, 1, 4, 3 };
int a = 0;
int sz = sizeof(arr) / sizeof(arr[0]); //必须在外面求,因为数组名传递的是数组首元素的地址
bubble_sort(arr,sz);
for (a = 0; a < sz; a++)
{
printf("%d ", arr[a]);
}
return 0;
} //输出结果为:0 1 2 3 4 5 6 7 8 9
在以上的冒泡排序代码中,可以进行优化和修改:
- 冒泡排序的外层循环可以减少迭代次数。如果在某次循环中没有进行任何交换,说明数组已经排序完成,可以提前终止排序。
修改后的代码如下:
//优化后冒泡排序
#include <stdio.h>
void bubble_sort(int arr[], int s)
{
int i = 0; //下标从0开始
for (i = 0; i < s - 1; i++) //确定趟数
{
int j = 0;
int flag = 1;
for (j = 0; j < s - i - 1; j++) //减去已经排序的元素
{
if (arr[j] > arr[j + 1])
{
int tmp = 0;
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = 0;
}
}
if (flag == 1) //flag=1说明并没有进入if语句,没有进行交换,排序已完成可以直接跳出
break;
}
}
int main()
{
int arr[] = { 7, 2, 6, 8, 5, 9, 0, 1, 4, 3 };
int a = 0;
int sz = sizeof(arr) / sizeof(arr[0]); //必须在外面求,因为数组名传递的是数组首元素的地址
bubble_sort(arr, sz);
for (a = 0; a < sz; a++)
{
printf("%d ", arr[a]);
}
return 0;
} //输出结果为:0 1 2 3 4 5 6 7 8 9