数组就是内存中的一组连续的存储空间,用于存储一组连续的数据,是相同类型的集合。
一维数组
一维数组的创建
一维数组的定义:
类型名 变量名[常量表达式 ]
注:数组创建, [ ] 中要给一个常量才可以,不能使用变量
int arr[10];
类型名 变量名[常量表达式]
或者
int n = 10;
int arr2[n];
第二种要看编译器是否支持
一维数组的初始化
数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值
int arr1[10] = { 0 };
int arr2[10] = {1,2,3};
int arr3[ ] = {1,2,3,4};
字符数组
char ch1[ ] = {'a','b','c'};
char ch2[5] = {'a','b','c'};
char ch3[ ] = "abc";
char ch4[5] = "abc";
上述是一些初始化的区别
- arr1定义了10个元素,但暂时不需要赋值使用,所以先都给0,以免生成随机数造成影响
- arr2和arr3是不完全初始化其中:
- arr2定义了10个元素但是只给前三个元素赋初始值,剩余的元素全为0
- arr3则是根据元素个数,确定数组大小
- ch1也是根据初始化赋予元素的个数确定数组的大小
- ch2则是赋予了前三个元素,后面自动补\0 即(a,b,c,\0,\0)
- ch3也是根据初始化元素的个数确定大小,但不同的是,他会自动在后面生成一个\0结束符,因为这是字符串形式
- ch4同ch2,剩余的空位补\0
由上可知:
- 数组不完全初始化时,剩余的会补0(\0)
- 当没确定元素个数时可以通过赋予的初始值确定
- 字符串的形式会自动在后面生成一个结束符,所以定义数组存放字符串时,比须大于元素的个数
一维数组的使用
假如要对数组的所有元素按顺序赋值,那如何引用呢,需要对 [ ] 下标引用操作符进行操作,如:
int arr[10] = { 0 };
int i;
for(i = 0; i < 10; i++)
{
arr[i] = i+1;
} //10[arr]是等价的
运行后数组值为:1,2,3,4,5,6,7,8,9,10
由此可知,下标是从0开始的,[ ]可以对数组的元素进行访问
int arr1[] = {23,54,56,65,44,33};
sizeof(arr1)/sizeof(arr1[0])
//arr1放的是数组总地址
//arr1[0]放的是数组的首元素地址
当没有确定元素个数时,这个代码可以求出元素的个数(字符串会多一个结束符)
一维数组在内存当中的存储
那么数组是如何存储的,请看代码
int arr1[10] = {1,2,3,4,5,6,7,8,9,10};
int sz = sizeof(arr1)/sizeof(arr1[0]);
int *p = &arr1[0]; //把首元素的地址,存到指针p里
for(i=0; i<sz; i++)
{
printf("&arr1[%d] = %p <===> %p\n",i,&arr1[i],p+i);
} //每个地址相差4
//p+1:+1加的是单元格,即向后移动1 个位置表示指针变量指向下一个数据元素的首地址
//例如 int型 就是4个字节大小,就指向这4个字节的首地址,具体的地址大小要看原来指针指向的数据类型是什么,char*就是指向char的 int*就是指向int的
输出如下:
而当把 int *p = &arr1[0] 改为 char *p = &arr1[0]
可以发现 当指针p的类型变为char,数组地址从4字节的单元格变为了1字节的单元格,所以指针+1加的是单元格,是下一个位置,并不是让存的地址+1
观察输出结果可知
随着数组下标的增长,元素的地址,也在有规律的递增。 由此可以得出结论:数组在内存中是连续存放的,存放的总大小却决于数据类型与元素个数。
二维数组
二维数组的创建
二维数组的定义:
int arr[10][5];
类型名 变量名[常量表达式][常量表达式]
可以理解为10行5列
二维数组的初始化
int arr[3][5] = {1,2,3,4,5,6};
int arr1[3][5] = {{1,2},{3,4},{5,6}};
int arr2[][5] = {1,2,3,4,5,6};
int arr3[][5] = {{1,2},{3,4},{5,6}};
以上均为不完全初始化
arr :先给第一行赋值,1——5,6在第二行首,剩下的补0
arr1:把1和2放在第一行前两列,3和4放在第二行前两列,5和6放在第三行前两列
arr2:可以不确定行,但必须确定列,当第一行五列赋值之后,6在第二行剩下的补0,所以确定为2行5列
arr3:把1和2放在第一行前两列,3和4放在第二行前两列,5和6放在第三行前两列,可以确定行数是3
二维数组的使用
和一维数组同理
int main()
{
int i,sz1,sz2;
int arr1[][5] = {{1,2},{3,4},{5,6}};
sz1=sizeof(arr1[0])/sizeof(arr1[0][5]);
//可求列大小:用第一行的总长度 除以 第一行每其中一列的长度 = 列的长度
printf("%d\n",sz1);
sz2 = sizeof(arr1)/sizeof(arr1[0]);
//可求行大小:用总大小除 一行的总大小 = 行的长度
printf("%d\n",sz2);
for(i = 0; i< sz2; i++)
{
int j = 0;
for(j = 0; j < sz1; j++)
{
printf("%d ",arr1[i][j]);
}
putchar('\n');
}
return 0;
}
在这个代码中可以通过
sizeof(arr)/sizeof(arr[0]) 求出行数
总数组大小 除以 首行的大小 等于 行的数量
sizeof(arr[0])/sizeof(arr[][0]) 求出列数
首行的大小 除以 首行首列的大小 等于 列数
二维数组在内存当中的存储
二维数组在内存中的存储如下:
int main()
{
int i;
int arr1[][5] = {{1,2},{3,4},{5,6}};
for(i = 0; i< 3; i++)
{
int j = 0;
for(j = 0; j < 5; j++)
{
printf("&arr1[%d][%d] = %p\n",i,j,&arr1[i][j]);
}
}
return 0;
}
观察输出结果可知:
二维数组也是是连续存放的
随着下标变化二维数组的地址也是由低到高的
了解了二维数组,三维数组也是同理,就是[ ] [ ] [ ] 三个操作符
可以先把二维数组看作一个整体当作一维数组 在与三维数组结合
实际中三维及以上数组用的很少。
数组作为函数参数
在调用函数时,数组可以作为参数传递
示例1:二分查找法
通过下标找到其元素
int find(int temp[],int t,int sz)
{
//int sz = sizeof(arr)/sizeof(arr[0]); 结果是1
int first = 0;
int end = sz-1;
int mid = 0;
while(first <= end)
{
mid = first+(end - first)/2;
if(temp[mid] > temp[t])
{
end = mid - 1;
}
else if(temp[mid] < temp[t])
{
first = mid + 1;
}
else
{
return temp[mid];
}
}
}
int main()
{
int arr[] = {1,2,3,4,5,6,7};
int sz = sizeof(arr)/sizeof(arr[0]);
printf("%d\n",find(arr,0,sz));
return 0;
}
如上所示:假如将int sz = sizeof(arr)/sizeof(arr[0]); 放到find函数里计算,结果为1,而在主函数里结果为7。所以可知,当数组作为参数时,传递的是首地址。
示例2:冒泡排序法
void bubble_sort2(int *arr,int sz)
{
int i,j;
for(i = 0;i < sz-1;i++)
{
for(j = 0;j<sz-1-i;j++)
{
if(arr[j]<arr[j+1])
{
int temp;
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int i;
int sz = sizeof(arr)/sizeof(arr[0]);
bubble_sort2(arr,sz);
for(i = 0; i < 10; i++)
{
printf("%d",arr[i]);、
}
return 0;
}
上述代码同理,传递的是数组的首地址,数组本质上是指针,所以是传递首地址后通过指针访问数组上的元素
总结
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\n",sizeof(arr)); //40
putchar('\n');
printf("%p\n",arr); //数组的首地址
printf("%p\n",arr+1); //数组下一个元素的地址
putchar('\n');
printf("%p\n",&arr); //数组的总地址, 相当于对*p,&p求指针的大小
printf("%p\n",&arr+1); //下一个数组的总地址, 指针的总地址+1 相当于 有开辟了一个新的同样大小的指针
putchar('\n');
printf("%p\n",&arr[0]); //数组第一个元素的地址
printf("%p\n",&arr[0]+1); //数组下一个元素的地址
return 0;
}
由上可知:
-
数组名传递的是首地址但有两个例外
(1)sizeof(数组名) 不是首地址,此处代表整个数组,计算的是整个数组大小
(2)&数组名 不是首地址,此处代表整个数组,取出的是整个数组地址 -
sizeof求的是有效元素个数(包括\0)
-
strlen求的是字符串长度,从首元素开始计算,遇见‘\0’停止
-
初始化后空余的元素会被补 0(\0)
-
二维数组不能没有列标
-
数组在内存中,是连续存放的
-
随着数组下标的增长,地址是由低到高变换的
-
注意数组越界问题 即[ ]里的表达式常量不能比数组总元素个数小