文章目录
一维数组的创建和初始化
数组的创建
数组创建的格式:
type_t arr_name [const_n]
//type_t 是指数组的元素类型
//arr_name是自定义的数组的名称
//const_n是一个常量表达式,用来指定数组的大小
数组创建的具体方式:
//正确的方式
int arr1[10]; //类型是int、名称是arr1、大小是10
char arr2[1+2]; //类型是char、名称是arr2、大小是3
#define N 5 //N是一个标识符常量
float arr3[N]; //类型是float、名称是arr3、大小是5
int a = 8;
double arr4[a]; //该方式在C99标准之前不支持,因为[]中该放一个常量
//该方式在C99标准中支持该方式,称为变长数组
.
.
.
//错误的方式
int arr5[]; //只创建数组时,没有定义数组大小
数组的初始化
数组的初始化是指,在创建数组的同时往数组里放几个合理元素
//正确的方式
int arr1[5] = {1, 2, 3, 4, 5};//数组大小为5,放入了五个元素
int arr2[10] = {1, 2, 3, 4, 5, 6};//数组大小为10,放入了6个元素,其余元素自动为0
//当初始化的元素小于数组大小时,称为不完全初始化,其余元素自动全为0
//定义字符串时,只能用char,正确的字符串末尾是应有一个'\0'的
char arr3[5] = "abcd";//数组大小是5,放入了一个字符串,看似是4个字母,结尾默认有一个'\0'
//所以该方法数组的大小至少要比想填入字符串的字母个数大于1
char arr4[] = "abcdef";//该方法定义字符串也是可以的,相当于把字符串的地址给了数组名arr4
int arr5[5] = {'a', 'b', 'c', 'd', 'e'};//该方法后面没有自动补'\0'
//错误的方式
int arr6[] = {1, 2, 3};//没有说明数组的大小就初始化,因为不知道大小怎么知道放几个元素呢
int arr[3] = {1, 2, 3, 4, 5, 6};//放入的元素数大于了数组的大小
char arr[5] = "abcde"//因为字符串默认有一个'\0',所以数组的空间大小不够了
一维数组的使用
#include <stdio.h>
int main()
{
int arr[10] = { 0 };//创建了一个数组,里面存放10个int类型的元素,元素全为0
int size = sizeof(arr) / sizeof(arr[0]);//计算数组的大小
//通过下标引用操作符[]访问数组的每个空间
//对数组内容进行赋值
for(int i = 0; i < size; ++i)
{
arr[i] = i;
}
//输出数组的内容
for(int i = 0; i < size; ++i)
{
printf("%d ", arr[i]);
}
return 0;
}
总结:
1. 数组是使用下标来访问的,下标从0开始
1. 使用操作符[ ]和下标,可以访问数组的每块空间
一维数组在内存中的存储
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int size = sizeof(arr) / sizeof(arr[0]);
for(int i = 0; i < size; ++i)
{
printf("&arr[%d] = %p\n", i, &arr[i]);
}
return 0;
}
可以看到随着数组下标的增长,元素的地址也在有规律的递增。
由此可以得出结论:数组在内存中是连续存放的,这也是为什么数组可以用下标来访问每一个空间的原因
二维数组的创建和初始化
一维数组我们知道是在一条直线上存储数据,也就是在一条轴上
那么二维数组可以想象成在x轴上和y轴上都存储数据
二维数组的创建
//跟一维数组大同小异
int arr1[3][4]; //第一个[]代表有几行元素,第二个[]代表有几列元素
double arr2[5][6];
.
.
.
二维数组的初始化
//正确的方法
//使用{}初始化时,初始化了指定行和列的元素
int arr1[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
//初始化了第一行的前两列元素和第二行的前三列元素,其余元素自动为0
int arr2[3][4] = {{1, 2}, {3, 4, 5}};
//不使用{}初始化时,先初始化第一行的每一列,再初始化第二行的每一列,以此类推
int arr3[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
//只初始化了第一行的前四列和第二行的前两列,其余元素自动为0
int arr4[3][4] = {{1, 2, 3, 4, 5,6};
//也可以只指定列数,不指定行数,默认从第一行开始初始化,列数满了自动换行
int arr5[][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
//第一行只初始化了前两列,后两列自动为0,然后换行初始化了前三列,后一列自动为0
int arr6[][4] = {{1, 2}, {3, 4, 5}};
//错误的方法
//只指定行数时,是不可以的,因为不知道每一列放多少元素,所以没法初始化
int arr6[3][] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
二维数组的使用
跟一维数组极其相似
#include <stdio.h>
int main()
{
int arr[3][4] = { 0 };//创建了一个三行四列的数组,元素大小为12个,全为0
for (int i = 0; i < 3; ++i)//按行初始化
{
for (int j = 0; j < 4; ++j)//按列初始化
{
arr[i][j] = i * 4 + j;
}
}
for (int i = 0; i < 3; ++i)//按行输出
{
for (int j = 0; j < 4; ++j)//按列输出
{
printf("%-2d ", arr[i][j]);
}
printf("\n");//输出完每一行,换行
}
return 0;
}
二维数组在内存中的存储
像一维数组一样,打印出每一个元素的地址进行观察
#include <stdio.h>
int main()
{
int arr[3][4];
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
}
}
return 0;
}
我们可以看出,随着下标的增长,元素的地址也在有规律的增大,由此我们可以得出二维数组在内存中也是连续存储的
数组越界
数组是有大小的,也就是说数组的下标是有范围限制的
数组的下标规定是从0开始的,假设数组有n个元素,那么最后一个元素下标为n - 1
#include <stdio.h>
int main()
{
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for(int i = 0; i <= 10; ++i)
{
printf("%d\n", arr[i]);//当i等于10的时候,越界访问了
}
return 0;
}
那么当然二维数组也会存在是否越界的问题
数组作为函数参数
在写代码时,数组经常用来作为参数传给某个函数,比如冒泡排序
正确的写法
#include <stdio.h>
void bubble_sort(int arr[], int size)
{
for(int i = 0; i < size - 1; ++i)
{
for(int j = 0; j < size - 1 - i; ++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 };
int size = sizeof(arr) / sizeof(arr[0]);
printf("before:\n");
for (int i = 0; i < size; ++i)
{
printf("%d ", arr[i]);
}
bubble_sort(arr, size);
printf("\nafter:\n");
for (int i = 0; i < size; ++i)
{
printf("%d ", arr[i]);
}
return 0;
}
错误的写法
#include <stdio.h>
void bubble_sort(int arr[])
{
int size = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < size - 1; ++i)
{
for (int j = 0; j < size - 1 - i; ++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 };
int size = sizeof(arr) / sizeof(arr[0]);
printf("before:\n");
for (int i = 0; i < size; ++i)
{
printf("%d ", arr[i]);
}
bubble_sort(arr);
printf("\nafter:\n");
for (int i = 0; i < size; ++i)
{
printf("%d ", arr[i]);
}
return 0;
}
可以看到错误的写法没有产生排序的效果,
两种方法的区别就是数组的大小一个是在传参前计算好传过去的,另一个是在传参之后在函数内部计算的。那么就极有可能是这个区别导致了方法二没有产生效果
我们可以看到size的大小发生了变化,还可以看到第二个sizeof(arr)下有警告提示
我们先看下面
数组名是什么
#include <stdio.h>
int main()
{
int arr[10] = { 1, 2, 3, 4, 5 };
printf("%p\n", &arr[0]);
printf("%p\n", arr);
printf("%d\n", arr[0]);
printf("%d\n", *arr);
printf("%d\n", sizeof(arr[0]));
printf("%d\n", sizeof(arr));
printf("%p\n", arr);
printf("%p\n", arr + 1);
printf("%p\n", &arr);
printf("%p\n", &arr + 1);
return 0;
}
可以看到
数组首元素arr[0]的地址和数组名arr的地址相同
数组首元素arr[0]的内容和*arr的内容相同
由此可以得出,数组名就是首元素地址,解引用数组名就是数组的第一个元素
还可以看到
sizeof(arr[0])计算数组第一个元素的大小为4
sizeof(arr)计算数组名的大小为4
arr + 1的地址比arr的地址大4
&arr + 1的地址比&arr的地址大40
由此可以得出,在两种情况下,数组名arr不是数组首元素的地址,*arr不是数组首元素
- sizeof(数组名),这时,数组名是整个数组
- &arr,这时,数组名是整个数组的地址
这时,我们就可以回答上面遗留的问题,为什么冒泡排序的方法二是错的
因为数组名是首元素地址(不包括那两种特殊情况),在传参时,形参接收到的是一个地址,而当我们对形参指针sizeof时,计算的是一个指针的大小,而指针大小在32位平台下是4个字节,在64位平台下是8个字节,而上面我们用的是32位平台。
还可以看到
sizeof(arr[0])计算数组第一个元素的大小为4
sizeof(arr)计算数组名的大小为4
arr + 1的地址比arr的地址大4
&arr + 1的地址比&arr的地址大40
由此可以得出,在两种情况下,数组名arr不是数组首元素的地址,*arr不是数组首元素
- sizeof(数组名),这时,数组名是整个数组
- &arr,这时,数组名是整个数组的地址
这时,我们就可以回答上面遗留的问题,为什么冒泡排序的方法二是错的
因为数组名是首元素地址(不包括那两种特殊情况),在传参时,形参接收到的是一个地址,而当我们对形参指针sizeof时,计算的是一个指针的大小,而指针大小在32位平台下是4个字节,在64位平台下是8个字节,而上面我们用的是32位平台。