一、一维数组
1、一维数组的创建和初始化
数组是一组相同类型元素的集合。
①数组的创建方式:
type_t arr_name [const_n];
type_t 数组的元素类型
arr_name 数组名
const_n 常量表达式,用来指定数组的大小
const_n 在C99语法中可以用变量表示,称为变长数组
辨析:
int main()
{
int n = 0;
int arr[n];//是不对的,因为在一般的编译器中是不可以用变量定义数组的。
int i = 0;
int arr[] = { 1, 2, 3, 4, 5 };
for (i = 0; i<5; i++)
{
printf("%d ", arr[i]);
} //这是正确的,因为这并不是在定义数组,而是访问数组。
return 0;
}
②数组的初始化
<1>完全初始化:int arr[10]={1,2,3,4,5,6,7,8,9,10};
<2>不完全初始化:int arr[10]={1,2,3,4,5}; 只初始化了前五个数,后面的数默认为0。
当如此 int arr[ ]={1,2,3,4}; 定义时,数组会根据初始化的内容开辟相同大小的空间。
★数字的初始化是用{ },如:{1,2,3,4};
★字符的初始化是用{'a'},如:{'a','b','c'};
★字符串的初始化是用" ",如:"world";
注意:char arr1[ ]={'w','o','r','l','d'}; (1)
char arr2[ ]="world"; (2)
打印字符串时:(1)的结果为:world烫烫烫烫烫?
(2)的结果为:world
原因是(1)中\0的位置不确定,遇到\0才会停止打印。而(2)中字符串之后紧跟着\0
求字符串长度时:(1)的结果为随机值
(2)的结果为5
原因是(1)中\0的位置不确定,遇到\0才会停止数长度。而(2)中字符串之后紧跟着\0
求所占内存空间时:(1)的结果为:5
(2)的结果为:6
原因是(1)中只有五个字符,所以占五个字节。而(2)中字符串之后紧跟着\0,所以占6 个字节空间。
总结:①用(1)的方式做与字符串相关的操作时,不知道\0的位置。
②求整个数组所占内存、求字符串长度、打印字符串时,输入数组名整个数组就是研究对象,而当打印数组中的某一个数字或者字符时,就要明确指出是哪一个。
2、数组的使用
访问数组中的某一个元素时,用[ ] 下标引用操作符。
<1>数组是使用下标来访问的,下标是从0开始的,
<2>数组的大小是可以通过计算得到的。如:int arr[]={1,2,3,4,5};
int sz=sizeof(arr)/sizeof(arr[0]);
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]); //计算数组大小
for (int i = 0; i < sz; i++)
arr[i] = i; //下标访问数组
for (int i = 0; i < sz; i++)
printf("%d ",arr[i]); //下标访问数组
return 0;
}
3、数组在内存中的存储
<1>一维数组在内存中是连续存放的。
<2>随着数组中元素下标的增大,地址是由低到高变化的。
★地址是连续的,一个地址根据数据类型的不同占据的内存是不一样的。如:p是整型数组中第一个元素的地址,而p+1是整型数组中第二个元素的地址。
int main()
{
int arr[10];
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
printf("&arr[%d]=%p\n",i,&arr[i]);
}
return 0;
}
%p是按地址的形式打印,打印出来的数是十六进制的。如:printf(“%p”,0x12);三十二位计算机结果为:00000012
%x是按十六进制的形式打印。如:printf("%x",0x12);结果为:12
二、二维数组
1、二维数组的创建和初始化
①二维数组的创建方式
type_t arr_name [const_n1] [const_n2];
type_t 数组内元素的类型
arr_name 数组名
const_n1 常量表达式,表示的是二维数组的行
const_n2 常量表达式,表示的是二维数组的列
②二维数组的初始化
<1>如:int arr[3 ] [4 ]={1,2,3,4,5,6,7,8,9,10,11,12};
初始化的数字会先排满第一行,然后再排满第二行,依次往下排
<2>如:int arr[ 3] [4 ]={1,2,3,4,5,6};
不完全的初始化,根据初始化的内容先排满第一排,然后排第二排,如果不够,后面数字默认为0
<3>如:int arr[3 ] [ 4]={{1,2},{3,4},{5,6}};
这种初始化可以控制每一行的数字,不用依次排过去。如果某一行的数不够则本行不足的默认为0
<4>如:int arr[ ] [4]={{1,2},{3,4},{5,6}};
在二维数组的定义中,行数是可以省略的,但是列数不能省略。行数会根据初始化的内容来确定
2、二维数组的使用
<1>二维数组也是通过下标进行访问的,行下标是从0开始的,列下标也是从0开始的。
<2>二维数组的行和列是可以计算的。
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
int rsz = sizeof(arr)/sizeof(arr[0]); //数组行的计算
int csz = sizeof(arr[0])/sizeof(arr[0][0]); //数组列的计算
for (int i = 0; i < rsz; i++)
{
for (int j = 0; j < csz; j++)
{
printf("%d ",arr[i][j]); //用下标访问数组
}
printf("\n");
}
}
3、二维数组在内存中的存储
<1>二维数组在内存中也是连续存放的,一行内部是连续存放的,跨行也是连续存放的。
<2>二维数组的每一行都可以看作是一个一维数组。
如:int arr[ ] [4]={{1,2},{3,4},{5,6}} 第一行可以看作是数组名为arr[0]的一维数组,第二行可以看作是数组名为arr[1]的一维数组。
<3>随着下标的增长,地址是由低到高变化的。
#include<stdio.h>
int main()
{
int arr[3][4];
int rsz = sizeof(arr) / sizeof(arr[0]);
int csz = sizeof(arr[0])/sizeof(arr[0][0]);
for (int i = 0; i < rsz; i++)
{
for (int j = 0; j < csz; j++)
{
printf("&arr[%d][%d]=%p\n",i,j,&arr[i][j]);
}
}
return 0;
}
★二维数组的理解:
int arr[3][5];
可以理解成三个一维数组,数组名分别为arr[0],arr[1],arr[2]。
三个一维数组,每个一维数组中有五个元素。
三、数组越界
数组下标是有范围限制的,下标规定,从0开始,到n-1结束。任何小于0或者大于n-1的下标都是非法的。(C语言本身是不做数组下标的越界检查的)
四、数组作为函数参数
1、数组名是数组首元素的地址
但是有两个例外:
①sizeof(数组名) 这里面的数组名表示的是整个数组,求的是整个数组的大小。
②&数组名 这里的数组名也是表示的整个数组的大小。
2、数组作为函数参数的时候形参可以写成两种形式
①数组形式
②指针形式
案例:冒泡排序
void Bubble_sort(int* arr, int sz)
{
for (int i = 0; i < sz - 1; i++)
{
int flag = 1;
for (int j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp;
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = 0;
}
}
if (flag == 1)
break;
}
}
int main()
{
int arr[10] = { 10,9,8,7,6,5,4,2,3,1 };
int sz = sizeof(arr)/sizeof(arr[0]);
Bubble_sort(arr,sz);
for (int i = 0; i < sz; i++)
printf("%d ",arr[i]);
return 0;
}
五、数组练习
<1>三子棋,具体游戏实现及分析单独成章。
<2>扫雷游戏,具体游戏实现及分析单独成章。