目录
一维数组的创建和初始化
数组的创建
数组是一组相同元素的集合。
数组的创建方式:
type_t arr_name [const_n];
type_t:数组中的元素的类型,一个数组的元素必须是同一类型。
arr_name:数组的名字。
[const_n]:数组的大小也就是元素的个数,是一个常量表达式。
//举例
//创建整型元素的数组;
int arr[10];
int arr[5+5];
int arr['W'];
int arr['EW'];
//以上都是正确的创建方式,主要理解创建数组时使用整型表达式
int count=10;
int arr[count];
//以上是使用变量进行创建,在C99标准后,支持C99标准的编译器支持了变长数组,可以用变量进行创建,但是这种创建不能使用初始化。
数组的初始化
初始化就是在创建数组的时候赋值。
int arr[5]={1,2,3,4,5};
//这种是完全初始化,数据将充满整个数组。
int arr2[5]={1};
//非完全初始化,出了初始化的元素,其他的编译器会自动赋值为0。
char ch1[]={'a','b','c'};
//一维数组创建时可以不写常量表达式,由编译器计算数组的元素个数,这个数组元素是3个a,b,c。
char ch2[]="abc"
//字符串,创建字符串时,字符串末尾自带一个'\0',所以ch2的大小是4,元素是a,b,c,\0。
一维数组的使用
数组的使用需要用到" [ ] "下标引用操作符和下标。
下标就是数组元素的标记。
数组 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
下标 | 0 | 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 |
下标从0开始,代表数组中每个元素的位置。
//举例
//下面是顺序打印数组中元素的代码,为了打印必须使用数组的下标。
int main()
{
int arr[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
system("pause");
return 0;
}
//arr[i]中,i表示了数组的下标,放在[]操作符中,arr[i]的作用等于*(arr+i),其中arr是数组首元素的地址,i是步长。
//[]操作符有两个操作数,数组名和下标,数组名[下标]。
//数组的长度是可以计算的,sizeof(arr)计算出整个数组的长度,单位是字节,除以单个元素的长度,就是数组的大小
一维数组在内存中的存储
//以下的代码作用是打印数组元素的地址
int main()
{
int arr[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i;
for (i = 0; i < sz; i++)
{
printf("&arr[%d]=%p\n",i, &arr[i]);
}
system("pause");
return 0;
}
从上图的运行结果发现,数组中元素的地址是连续的,而且是间隔相等的,所以数组在内存中是连续存储的,下标实际上就是记录每个元素地址位置的步长。
数组是存放在内存的栈区中,VS2013中创建数组时,开辟的空间是从高地址到低地址,如下图
二维数组的创建和初始化
二维数组的创建
二维数组的创建于一维数组类似,只是坐标多了行坐标。
//举例
int arr[3][5];
注意:创建时可以省略行坐标,但是必须要列坐标,原因是列坐标用来确定每一行的元素有多少个,以此来确定下一行的开头。
二维数组的初始化
二维数组的初始化与一维数组相同,也是给数组赋值,需要注意的是不能省略列坐标。
//举例
int arr[][5]={{1,2,3},{2,3,4},{3,4,5},{4,5,6}};
上面创建的二维数组,一共4行,每行5个元素,在内存中体现为
第一行 | 1 | 2 | 3 | 0 | 0 |
第二行 | 2 | 3 | 4 | 0 | 0 |
第三行 | 3 | 4 | 5 | 0 | 0 |
第四行 | 4 | 5 | 6 | 0 | 0 |
//举例2
int arr[][5]={1,2,3,4,5,6,7,8,9,10,11,12};
上面创建的是3行,每行5列元素的数组。
第一行 | 1 | 2 | 3 | 4 | 5 |
第二行 | 6 | 7 | 8 | 9 | 10 |
第三行 | 11 | 12 | 0 | 0 | 0 |
二维数组的理解:
一、看作一维数组的数组,arr[3][5]。
数组名 | arr[0] | 1 | 2 | 3 | 4 | 5 |
数组名 | arr[1] | 6 | 7 | 8 | 9 | 10 |
数组名 | arr[2] | 11 | 12 | 13 | 14 | 15 |
二维数组可以看成一维数组的数组,每一行的数组名是arr[i],其中的某个元素是arr[i][j],因此可以计算出一个二维数组的行和列。
//举例
//计算几行
int main()
{
int arr[3][5] = { 0 };
int row = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", row);
system("pause");
return 0;
}
代码执行的结果为3,原理是sizeof(arr)计算的是整个数组的大小,sizeof(arr[0])计算的是第一行的大小,因此可以得到arr[0]是第一行的数组名,sizeof只有在计算数组名时,才能得到数组的大小。
//计算几列
int main()
{
int arr[3][5] = { 0 };
/*int row = sizeof(arr) / sizeof(arr[0]);*/
int col = sizeof(arr[0]) / sizeof(arr[0][0]);
printf("%d\n", col);
system("pause");
return 0;
}
代码执行的结果为五5。
二,看作一整个一维数组。
//举例
int main()
{
int arr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
int i = 0;
int row = sizeof(arr) / sizeof(arr[0]);
int col = sizeof(arr[0]) / sizeof(arr[0][0]);
for (i = 0; i < row*col; i++)
{
printf("%d\n", arr[0][i]);
}
system("pause");
return 0;
}
//这段代码的输出结果是1-15,数组中数组名是首元素的地址,二维数组中arr代表的是第一行的地址,所以不合适,arr[0]表示的是第一行第一个元素的地址,所以可以打印出整个数组,上面的代码中清楚的说明,二维数组可以像一维数组一样操作。
二维数组的使用
二维数组的使用也是通过下标和" [ ] "运算符。
//举例
//打印一个二维数组,从二维数组的角度打印
//思路,二维数组有行和列,所以循环打印的时候应当嵌套打印,row变量当作行,col变量当作列
int main()
{
int arr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
int row = 0, col = 0;
for (row = 0; row < 3; row++)
{
for (col = 0; col < 5; col++)
{
printf("%d\n", arr[row][col]);
}
}
system("pause");
return 0;
}
二维数组在内存中的存储
先看以下代码
//举例
int main()
{
int arr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
int row = 0, col = 0;
for (row = 0; row < 3; row++)
{
for (col = 0; col < 5; col++)
{
printf("&arr[%d][%d]=%p\n",row,col, &arr[row][col]);
}
}
system("pause");
return 0;
}
//这段代码是为了打印每一个二维数组的地址。
代码运行结果如下:
其中每个元素之间的地址相差4个字节,即使行下标变换后也是如此,因此可以知道,二维数组的保存也是将整个数组作为一个一维数组进行保存,在栈区中连续存储的。
数组越界
数组的越界是指数组的在使用时,下标超出数组的范围,该行为的会引发不确定的结果,同时编译器也很难检测。
//一维数组越界
int main()
{
int arr[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
int i = 0;
for (i = 0; i < 16; i++)
{
printf("%d ", arr[i]);
}
system("pause");
return 0;
}
//二维数组越界
int main()
{
int arr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
int row = 0, col = 0;
for (row = 0; row < 3; row++)
{
for (col = 0; col < 6; col++)
{
printf("%d ", arr[row][col]);
}
printf("\n");
}
system("pause");
return 0;
}
以上的越界仅仅是打印出随机的值,具体编程中越界可能会造成多种问题。
数组作为函数参数
实际编程中经常会传递数组,把数组作为函数参数传递,有两种方式,一种是数组,二是指针。
冒泡排序
思路:冒泡排序就是将相邻的两个数进行交换,没完成两个数之间的交换称为对,当一个数移动到合适的位置称为趟。
以下图为例,将10从第一个元素移动到最后一个元素是一趟,其中经过了9对交换。
每趟交换后,会将一个数放入正确的位置,所以该数组总共需要9趟交换,因为最后一个数字不需要交换。同时每趟交换时,因为有一个数字已经放入正确的位置,所以每趟的对数也会减少1,规律如下:
需要移动的元素 | 第几趟 | 对数 |
10 | 1 | 9 |
9 | 2 | 8 |
8 | 3 | 7 |
7 | 4 | 6 |
6 | 5 | 5 |
5 | 6 | 4 |
4 | 7 | 3 |
3 | 8 | 2 |
2 | 9 | 1 |
1 | 0 | 0 |
上表可以看到,10个元素的数组,冒泡排序需要9趟,每次的对数是从9开始递减的,所以可以得出趟数=元素个数-1,对数=元素个数-趟数。
//把降序的数组进行升序的排列
#define MAX 10
void BoboSort(int arr[])
{
int i = 0, j = 0;//i表示趟数,j表示对数
int sz = sizeof(arr) / sizeof(arr[0]);//计算数组的长度
int tmp = 0;
printf("排序函数内\n");
for (i = 0; i < sz - 1; i++)
{
for (j = 0; j < sz - i - 1; j++)
{
if (arr[j]>arr[j + 1])
{
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[MAX] = { 10,9,8,7,6,5,4,3,2,1 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
BoboSort(arr);
printf("排序函数外\n");
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
system("pause");
return 0;
}
上面这段代码的运行后,并没有将数组重新排序,根本原因是因为,数组传参是传递的是指针,因此BoboSort函数内的sz计算的是指针除以指针的大小结果为1,sz-1=0,i必须在小于0的时候才能进入循环,i的初始值为0,所以不能进入循环。
上面的代码也说明了数组传参时,传递的并不是数组,而是一个指针,那么数组名是什么的指针呢?
数组名是什么
上面的代码中,增加打印arr数组名以及arr形参的地址。
可以看到arr数组名与arr形参的值相同,同时与内存中数组首元素的地址相同。
因此可以得出结论数组名就是首元素的地址。
但是又两个特殊情况需要注意:
1、sizeof计算数组长度时,()内单独放一个数组名,此时数组名表示整个数组。
int main()
{
int arr[10];
int sz = sizeof(arr);
printf("%d", sz);
system("pause");
return 0;
}
2、&arr时,数组名表示整个数组。
下面这段代码,运行后arr+1的地址会比arr多40个字节。
int main()
{
int arr[10];
printf("arr的地址%p\n", &arr);
printf("arr+1的地址%p\n", &arr+1);
system("pause");
return 0;
}
正确的冒泡排序
考虑到数组名传参时只是传递首元素地址,所以函数要增加传递数组长度的形参。
//把降序的数组进行升序的排列
#define MAX 10
void BoboSort(int arr[],int sz)
{
int i = 0, j = 0;//i表示趟数,j表示对数
int tmp = 0;
printf("排序函数内\n");
for (i = 0; i < sz - 1; i++)
{
for (j = 0; j < sz - i - 1; j++)
{
if (arr[j]>arr[j + 1])
{
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[MAX] = { 10,9,8,7,6,5,4,3,2,1 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
BoboSort(arr,sz);
printf("排序函数外\n");
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
system("pause");
return 0;
}
数组的类型
int arr[10];
去掉数组名的就是数组的类型。
int [10];
int main()
{
int arr[10];
int sz = sizeof(int[10]);
printf("%d", sz);
system("pause");
return 0;
}
sizeof与strlen
sizeof计算的是类型的空间,strlen是计算'\0'之前的字符个数,前者不看空间内容,后者看空间内容。