格式: 数据类型 数组名[元素个数];
元素个数一般表示是一个常量表达式,不能是任何变量及表达式。
但c99标准支持了变长数组创建,数组创建中元素个数可以是变量,但变长数组一旦创建数组长度(元素个数)便是确定的。变长数组不能在创建时初始化。
例如:
//含有10个字符元素的数组
char arr1[10];
//含有10个整型元素的数组
int arr2[10];
//含有20个浮点型元素的数组
double arr3[20];
与创建变量时相同,不对数组中的元素进行初始化,数组中元素均是随机值(垃圾值),所以要为数组元素赋上一些合理的初始值。
- 在创建时初始化。
- 先创建再初始化。
在创建时初始化。
完全初始化:数组中的元素均被初始化。
int arr1[5] = {1,2,3,4,5};
初始化时可以不指定数组的元素个数,那么数组的元素个数是初始化时元素的个数。
int arr2[] = {1,2,3};
不完全初始化:只对数组中部分元素进行初始化,其余未初始化的元素自动被初始化为0。
int arr3[5] = {1,0,3};
int arr4[5] = {0};
- 先创建再初始化。
//先创建再初始化。
int arr[5];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;
有些特殊的字符数组和字符串:
char arr1[] = {'a', 'b', 'c', 'd'};
char arr2[4] = {'a', 'b', 'c', 'd'};
char arr3[] = "abcd";
char arr4[5] = {'a', 'b', 'c','d', '\0'};
printf("arr1[ ] %d\n", sizeof(arr1));
printf("arr2[4] %d\n", sizeof(arr2));
printf("arr3[ ] %d\n", sizeof(arr3));
printf("arr4[5] %d\n", sizeof(arr4));
表面上看由相同的字符组成的字符数组和字符串,实际上有着不同:
- 字符数组由于不以\0结尾,所以字符数组的元素个数比字符串少1,故字符数组的长度比字符串少1个字节。
- 可能带来的影响是,使用
%s
格式打印时,字符串能正确打印,在'\0'
处停止打印。但字符数组则不一定能正确打印,原因在于%s
打印需要在'\0'
处停止,但字符数组不含'\0'
,所以字符数组自身的内容打印完之后会继续打印字符数组之后的内容,直到遇到'\0'
时才停止打印。
数组通过下标引用操作符[]
实现对数组元素的单独使用。操作符[]
内是数组的下标。
n个数组元素,第一个元素的下标是0,最后一个元素的下标是n-1。
int main() {
//数组初始化
int arr[10] = { 0 };
int i = 0;
//循环输出每个元素的值
for (i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
printf("\n");
//循环输入每个整型数组元素的值
for (i = 0; i < 10; i++) {
scanf("%d", &arr[i]);
}
//循环输出每个元素的值
for (i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
计算一个一维数组的大小:
#include
int main() {
int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
int a = sizeof(arr);
int b = sizeof(arr[0]);
int c = sizeof(arr) / sizeof(arr[0]);
printf("sizeof(arr) = %d\n", a);
printf("sizeof(arr[0]) = %d\n", b);
printf("sizeof(arr) / sizeof(arr[0]) = %d\n", c);
return 0;
}
一维数组中的元素在内存中是连续存放的,具体表现为相邻元素的地址均相差一个类型所占的字节。
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
#include
int main(){
int arr[10] = {0};
int i = 0;
for(i = 0; i < 10; i++){
//%p表示将打印的是变量的地址
printf("&arr[%d] = %p\n", i, &arr[i]);
}
return 0;
}
运行结果:不看每个元素具体的地址,因为每次运行时同一元素的地址也可能不相同,只看相邻元素地址的差值。
二维数组:数组元素是一维数组的一维数组。
格式:数据类型 数组名[row][column];
row是二维数组的行数,column是二维数组的列数。
先定义二维数组中元素的数据类型。数组名与变量名的定义相同。
//创建3行4列的字符数组,含有12个字符
char arr1[3][4];
//创建3行4列的整型数组,含有12个整数
int arr2[3][4];
//创建5行5列的浮点型数组,含有25个浮点数
double arr3[5][5];
三维数组创建:
int a[3][4][5];
先定义再初始化
int arr[3][4];
int i = 0;
for (i = 0; i < 3; i++) {
int j = 0;
for (j = 0; j < 4; j++) {
scanf("%d", &arr[i][j]);
}
}
for (i = 0; i < 3; i++) {
int j = 0;
for (j = 0; j < 4; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
定义时初始化
int arr1[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int arr2[3][4] = {{1,2,3},{5,6,7,8},{9,10}};
int arr3[3][4] = {1,2,3,4};
二维数组初始化时可以省略行,但不能省略列,省略的行可以通过计算得到。
int arr4[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
二维数组也通过下标[]
使用二维数组的元素。
分为行下标和列下标。
行下标范围为0~row-1
,列下标范围为0~column-1
。
#include
int main(){
//二维数组元素初始化为0
int arr[3][4] = {0};
int i = 0;
int j = 0;
//双重循环输入数组的各个元素
for(i = 0; i < 3; i++){
for(j = 0; j < 4; j++){
scanf("%d", &arr[i][j]);
}
}
//双重循环输出数组的各个元素
for(i = 0; i < 3; i++){
for(j = 0; j < 4; j++){
printf("%d ", arr[i][j]);
}
}
return 0;
}
二维数组理解上是多行多列的,但实际在内存中储存时是以一维数组的形式依次存放的。
也就是说,二维数组是元素为一维数组的一维数组。
一个简图:
int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
#include
int main() {
int arr[3][4] = { 0 };
int i = 0;
int j = 0;
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) {
printf("&a[%d][%d] = %p\n", i, j a[i][j]);
}
}
return 0;
}
对数组的粗心使用可能会导致数组越界的情况。
int arr[10];
下标的有效范围为09。对于具有n个元素的一维数组,下标的有效范围是0n-1。int arr[3][4];
行下标有效范围是02,列下标有效范围是03。数组时常作为函数的参数。
int arr[10] = { 0 };
例如数组名arr
一般情况下数组名是数组首元素的地址。
特殊情况:
sizeof
的操作数,数组名代表的是整个数组,sizeof求的是所占内存的大小,所以sizeof(数组名)
的结果是整个数组的大小,而不是数组首个元素(arr[0]
)的大小。#include
int main(){
int arr[10] = { 0 };
printf("sizeof(arr) = %d\n", sizeof(arr));
printf("sizeof(arr[0])%d\n", sizeof(arr[0]));
return 0;
}
&
的操作数为数组名,即&数组名
时,数组名代表的是整个数组,所以取出的也是整个数组的地址,而不是数组首元素的地址。也就是说整个数组的地址与数组首元素的地址只在数值上相同,但二者有着本质上的不同。#include
int main(){
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
printf("&arr = %p", &arr);
printf("&arr+1 = %p", &arr + 1);
printf("&arr[0] = %p", &arr[0]);
printf("&arr[0]+1 = %p", &arr[0] + 1);
return 0;
}
可以看到整个数组的地址&arr
与数组首元素的地址&arr[0]
数值上相同,但整个数组的地址+1&arr+1
后跳过了整个数组的大小,数组首元素+1&arr[0]+1
跳过了一个数组元素的大小。
[]
内写不写数组的元素个数效果都一样,一般都写上,有助于理解。sizeof(数组名)
不能求整个数组的长度,求的是一个指针的大小,也就是一个地址的大小。#include
int Add(int arr[], int sz);
int main() {
int arr[5] = { 1,2,3,4,5 };
printf("main sizeof(arr) = %d\n", sizeof(arr));
int sz = sizeof(arr) / sizeof(arr[0]);
int ret = 0;
ret = Add(arr, sz);
printf("ret = %d", ret);
return 0;
}
//求一维数组中所有元素之和
//具有相同效果的不同写法
//int Add(int *arr, int sz)
//int Add(int arr[5], int sz)
int Add(int arr[], int sz) {
printf("Add sizeof(arr) = %d\n", sizeof(arr));
int i = 0;
int sum = 0;
for (i = 0; i < sz; i++) {
sum += arr[i];
}
return sum;
}
#include
//对有sz个元素的一维数组降序排列
void cmp_sort(int arr[], int sz);
int main() {
//一维数组初始化
int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
//求一维数组元素的个数
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
//输出一维数组的所有元素
for (i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
printf("\n");
//对有sz个元素的一维数组降序排列
cmp_sort(arr, sz);
for (i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
return 0;
}
//对有sz个元素的一维数组降序排列
void cmp_sort(int arr[], int sz) {
int i = 0;
for (i = 0; i < sz; i++) {
int j = 0;
for (j = i + 1; j < sz; j++) {
if (arr[i] < arr[j]) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
}
int (*p)[4]
。对指针数组
int (*p)[4]
的解释:首先是一个指针,指向一个数组,数组中有4个元素,每个元素都是int。
#include
//计算二维数组所有元素之和
int Add(int arr[3][4], int row, int column);
int main() {
int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
int ret = Add(arr, 3, 4);
printf("ret = %d", ret);
return 0;
}
//计算二维数组所有元素之和
//具有相同效果的二维数组作为参数的定义方式
//int Add(int arr[][4], int row, int column)
//int Add(int (*p)[4], int row, int column)
int Add(int arr[3][4], int row, int column) {
int sum = 0;
int i = 0;
for (i = 0; i < row; i++) {
int j = 0;
for (j = 0; j < column; j++) {
sum += arr[i][j];
}
}
return sum;
}
二维数组作为函数参数时行数可以省略,但列数不能省略,因为本质为数组指针。
对二维数组作函数参数的进一步理解:
#include
int Add(int arr[3][4], int row, int column);
int main() {
int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
printf("main sizeof(arr) = %d\n", sizeof(arr));
printf("main sizeof(int(*)[4]) = %d\n", sizeof(int(*)[4]));
printf("main sizeof(arr[0]) = %d\n", sizeof(arr[0]));
printf("main sizeof(*(arr+0)) = %d\n", sizeof(*(arr + 0)));
printf("main sizeof(arr[0][0]) = %d\n", sizeof(arr[0][0]));
printf("main sizeof(*(*(arr+0)+0)) = %d\n", sizeof(sizeof(*(*(arr + 0) + 0))));
int ret = Add(arr, 3, 4);
printf("ret = %d", ret);
return 0;
}
//具有相同效果的二维数组作为参数的定义方式
//int Add(int arr[][4], int row, int column)
//int Add(int (*p)[4], int row, int column)
int Add(int arr[3][4], int row, int column) {
//这里的arr实际上是数组指针,sizeof算出的是地址的大小
printf("Add sizeof(arr) = %d\n", sizeof(arr));
//这里算的是数组指针的大小,是一个指针,sizeof算出的是地址的大小
printf("Add sizeof(int(*)[4]) = %d\n", sizeof(int(*)[4]));
//这里的arr[0]是二维数组的首元素,是一个一维数组名。而数组名单独作为
// sizeof的操作数时数组名代表整个一维数组,求出的是整个一维数组的大小。
//函数参数二维数组定义为3行4列,即每一个一维数组是四个整型元素
printf("Add sizeof(arr[0]) = %d\n", sizeof(arr[0]));
//arr本质是指针数组(int(*)[4]),指向一个一维数组。
//(arr+0)解引用则是通过指针数组访问了其指向的一维数组,
//故*(arr+0)相当于*arr相当于arr[0],是一维数组名。
printf("Add sizeof(*(arr+0)) = %d\n", sizeof(*(arr + 0)));
//arr[0][0]是二维数组中第一个一维数组中的的一个整型元素的值
printf("Add sizeof(arr[0][0]) = %d\n", sizeof(arr[0][0]));
//*(arr+0)是一维数组的数组名,*(arr+0)+0解引用则是通过一维数组名
//访问一维数组的第一个元素,*(*(arr+0)+0)相当于*(arr[0]+0)相当于arr[0][0]
printf("Add sizeof(*(*(arr+0)+0)) = %d\n", sizeof(sizeof( *(*(arr+0)+0) )));
int sum = 0;
int i = 0;
for (i = 0; i < row; i++) {
int j = 0;
for (j = 0; j < column; j++) {
sum += arr[i][j];
}
}
return sum;
}
运行结果:
运行结果:
观察到main函数与Add函数中计算sizeof(arr)
的本质不同。
本文主要写了有关C语言数组的内容。首先先了解了一维数组的创建,初始化,使用和在内存中的储存;然后就是二维数组的创建,初始化,使用和在内存中的储存。在对数组的使用中要预防数组的越界,知道数组越界可能会造成严重的影响。数组名也经常用作为函数传参,因此需要熟悉一维数组传参和二维数组传参的本质是什么。
END