1.一维数组
数组用来顺序存放一批相同数据类型。
1.1定义一个数组
数组和普通变量一样,需要先定义再使用。
下面来定义一个一维数组
类型说明符 数组名[整型常量表达式]
需要说明的是:
数组创建,在C99标准之前,[] 中要给一个常量才可以,不能使用变量。在C99标准支持了变长数组的概念,变长数组的大小可以由变量决定,但是 变长数组是不能初始化的。
int n=0;
scanf("%d",&n);
//int arr[n]={0};//错误,不能初始化
int arr[n];//只创建
int i=0;
for (i=0;i<n;i++)
{
arr[i]=i;
}
1.2数组的初始化
数组元素在定义(创建)后,不能马上参与运算。如果希望定义过的数组元素马上参与运算,可以在数组定义时给全部或者部分数组元素赋初值,这就是数组的初始化。
举例如下:
int arr1[10] = {
1,2,3};//部分初始化,后面的数组元素默认赋值为零
int arr2[] = {
1,2,3,4};//初始化里给全部元素赋值,可以在数组定义中即“[]”省略数组长度
int arr3[5] = {
1,2,3,4,5};//全部初始化
char arr4[3] = {
'a',98, 'c'};//十进制98就是字符b
char arr5[] = {
'a','b','c'};
char arr6[] = "abc";
要区分上面两个字符数组的长度!
我们可以用字符串常量直接初始化字符数组,但是,C语言自动在每个字符串后面加上’\0’作为字符串结束的标记,也就是说,arr6比arr5的长度大一个字节。
1.3 访问(使用)数组的元素
数组是使用下标来访问的,下标是从0开始。[]这个是数组访问的操作符,注意啦,访问中[]当然可以用变量如arr[i],上文中关于变长数组的讨论是基于定义(创建)一个数组的语法格式。
来个for循环访问数组中的全部元素,不过,在那之前,我们需要计算出数组的大小。
1.3.1 如何计算数组大小
int sz = sizeof(arr)/sizeof(arr[0]);
用sizeof操作符计算出整个数组的大小,再除以数组首元素的大小,就可以得到数组的大小(长度)了。
#include <stdio.h>
int main()
{
int arr[10] = {
0};//数组的不完全初始化
int sz = sizeof(arr)/sizeof(arr[0]);
int i = 0;
for(i=0; i<sz-1; i++)//注意下标从零开始访问
{
arr[i] = i;
}
for(i=0; i<10; ++i)
{
printf("%d ", arr[i]);
}
return 0;
}
1.3.2关于数组的类型
上面的代码中arr是变量名,数组的类型 长这样int [数组长度],我们用
变量arr的大小,在32位下,20字节,因为int四字节,所以整个数组的大小为4*5字节。
1.4 数组中的元素在内存中如何存储
可以看到,数组各个元素的地址相差4个字节,由此我们可以得出
1.数组中的元素在内存中是连续存放的。
2.随着下标增加,地址编号由低到高。
2.二维数组
2.1 二维数组的初始化
int arr[3][4] = {
1,2,3,4};
int arr[3][4] = {
{
1,2},{
4,5}};
int arr[][4] = {
{
2,3},{
4,5}};//二维数组如果有初始化,行可以省略,列不能省略
这里要注意二维数组初始化,行可以省略,列不能,很好想,因为列确定了一行有多少个元素,而又因为二维数组也是连续存放的,想象一根绳子,固定距离折一下,只要知道了那个固定距离,折完后有几行自然知道了。
2.2 二维数组中数据的存储
可以看到,每个地址相差四字节,也是连续存放,地址由高到低。
3.数组越界
数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。
所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了。
这个数组越界是不报错的(但VS有提示),所以要格外注意。
4.数组传参
当数组作为函数的参数,
首先我们要明白数组名就是数组首元素地址,但是有两个例外!
4.1 数组名是数组首元素地址及其特殊情况
1.当用操作符sizeof计算整个数组的大小,即 sizeof(数组名),此时数组名表示整个数组,算的是整个数组的大小。
2.使用 &数组名,数组名表示整个数组,取出的是这个数组的地址。
第一点,上面计算数组大小时用过了。
第二点,见下图注释
4.2 数组传参的指针接收和数组接收
首先让我们来看一个数组传参中经典的错误,拿冒泡排序法举例。(相邻比较,每一趟确定一个元素位置)
#include <stdio.h>
void bubble_sort(int arr[])//数组传参,类型?
{
int sz = sizeof(arr)/sizeof(arr[0]);
int i = 0;
for(i=0; i<sz-1; i++)
{
int j = 0;
//注意确定一个数位置后,下一次就少一趟
for(j=0; j<sz-i-1; j++)
{
if(arr[j] > arr[j+1])
{
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
int main()
{
int arr[] = {
3,1,7,5,8,9,0,2,4,6};
bubble_sort(arr);
for(i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
return 0;
}//错误
上面的sz算的可不是数组的大小,因为当数组传参的时候,实际上只是把数组的首元素的地址传递过去了。所以即使在函数参数部分写成数组的形式: int arr[] 表示的依然是一个指针,即int *arr 。
那么,函数内部的 sizeof(arr) 结果是4。(32位下的指针大小)
void bubble_sort(int arr[])
void bubble_sort(int arr*)
我们可以把这两种接受方式分别称为数组接收,指针接收,不过,来的都是数组首元素地址。
至于如何修改上面的冒泡法呢?
我们将
int sz = sizeof(arr)/sizeof(arr[0]);
放到主函数中就可以了,此时arr代表了整个数组,然后
void bubble_sort(int arr[], int sz)