【C语言】数组的超详细解答,走过路过别错过-创新互联
- 前言
- 数组
- 一、一维数组
- 1.1、初始化一维数组
- 1.2、一维数组元素赋值
- 1.3、一维数组传参
- 二、二维数组
- 2.1、二维数组的声明和初始化
- 2.2、二维数组传参
- 2.3、高维数组
- 三、数组与指针
- 3.1、指针简介
- 3.2、数组名的含义
- 3.3、指针访问数组
- 指针访问数组元素
- 指针访问整个数组
前言
在编程的道路上,我们处理的问题越发复杂,数据越发庞大,为了解决庞大的数据,学习数组便有很重要的意义。
数组
何为数组?
数组:数据类型相同的一系列元素组成的集合。
数组的下标有0开始
了解它的定义,就要开始学习如何使用它。
一、一维数组
1.1、初始化一维数组一维数组是我们平时使用最多的数组
char str[23];
float candy[44];
int code[50];
以上均为一维数组 - 数组名后面仅有一个[ ]
我们将一维数组分为4部分:如 int arr[30] = {1,2,3};
- int - 数组内元素的类型
- arr - 数组名
- [30] - 数组内元素个数
- {1,2,3} - 数组元素的初始化
初始化数组,则要注意第3和第4点
- 数组元素个数的初始化
#include#define MAXN 20
int main(void)
{int n = 10;
//数组元素个数的初始化
int a1[4]; //YES
int a2[5*3+1]; //YES
int a3[sizeof(int)+4]; //YES
int a4[MAXN]; //YES
int a5[-3]; //NO
int a6[0]; //NO
int a7[2.5]; //NO
int a8[(int)2.5]; //YES
int a9[n]; //c99之前的标准不允许
int a10[] = {1,3,5}; //YES,元素个数为3
return 0;
}
总结:[ ]内可以是常量和常量表达式(但结果应为正整数),符号常量(#define定义的);
至于变量需要编译器支持c99标准,为了代码的兼容性,因此即使你的编译器支持也最好别用。
也可以[ ]内为空,有后面的初始化赋值来决定元素个数
- 初始化赋值
#include#define MAXN 20
int main(void)
{//初始化时后面用 {},元素之间用逗号隔开
int arr1[] = {1,3,4,5 }; //元素个数为4
int arr2[3] = {1,3,4,5 };//初始化列表中的项数多于数组元素个数,错误
int arr3[2] = {1,3.4 }; //3.4为double类型,赋给int类型的元素会使数据被截断
int arr4[3]; //未赋值,数组内元素为随机值
int arr5[30] = {1 }; //第一个元素被赋值为了1,
//后面29个元素被编译器自动赋值为0
return 0;
}
总结:
- 当初始化列表中的项数少于数组元素时(上面最后一种情况),后面的元素值自动赋为0(字符数组是 \0 ;根据ASCII码表,\0 字符对应的值是 0,所以对于字符数组,也可以说后面的元素被赋值为0)
- 赋的值应当于元素的类型相对应(上面的第3种情况),虽然不会报错,但数据不准
- [ ]内为空时,编译器会自动匹配数组大小和初始化列表中的项数(上面第1种情况)
还有一种常用的初始化方法 - 用memset进行初始化
int arr[10];
memset(arr,0,sizeof(arr));
对于其作用机制,读者可自行查阅
字符数组进行赋值时
//以下情况允许
char arr1[] = {'a','b','c','d' };
char arr2[] = "aasdf";
char arr3[] = {"acbhd" };
//以下情况不允许
char arr4[] = 'a';
// 总结: 字符串可以不带括号
// 但单个字符赋值时必须要{}
- **c99新特性 - 指定初始化器//
有一种情况:int arr[10],你只想将第6个元素初始化为6,其余为0,怎么写?
int arr[10] = {0,0,0,0,0,6};
c99可以这样写int arr[10] = {[5] = 6};
而且int arr[10] = { 1,2,[5] = 6,7,8};
arr内的每个元素的值为1 2 0 0 0 6 7 8 0 0
在 指定初始化器后面的值 会接着在 初始化器后面 赋值
1.2、一维数组元素赋值
声明数组后,便可以借助数组下标对数组元素进行赋值
一般给数组赋值
常用循环
#include#define MAXN 20
int main(void)
{int arr[MAXN];
for (int i = 0; i< MAXN; i++)
{arr[i] = i;
}
return 0;
}
下面进行一些错误示范
#include#define MAXN 20
int main(void)
{int a1[MAXN] = {5,3,5,7 };
int a2[MAXN];
a1 = a2; //错误
a2[MAXN] = a1[MAXN]; //数组下标越界
a2[3] = {5,3,4,2 }; //无作用,错误
return 0;
}
1.3、一维数组传参
int a[10] = {1,2,3,4,5,6,7,8,9,10};
void nb1(int a[]) //[]内数字可省略
void nb2(int* pa) //用指针接受,至于为什么,后面的数组名含义有解释
二、二维数组
在实际应用中,一维数组并不够用,我们需要更高维的数组
列如:我想存储一年的天气情况,查询第m月第n天的天气
如果采用一维数组,那我需要先要算m月n日 距1月1日的天数,在带入下标,这并符合我们的习惯
难道不能像平面直角坐标系一样,通过 横坐标 和 纵坐标 来访问,而这就要用到二维数组
2.1、二维数组的声明和初始化
int arr[row][col];前一个[]为行,后一个[]为列
#includeint main(void)
{//以下为正确形式
int a0[3][4];
int a1[3][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6} };
int a2[][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6} };
int a3[][4] = {1,2,3,4,5,6,7,8,9,10 };//不足的补0
//以下为错误形式
int a4[][] = {{1,2,3,4},{2,3,4,5},{3,4,5,6} }; //不能省略列
int a5[3][3] = {{1,2,3,4},{2,3,4,5},{3,4,5,6},{4,5,6,7} };//行列均超过限定数目,错误
return 0;
}
总结:
- 若不进行初始化,行列均不能省
- 若进行初始化,可以省略行,但不能省略列(如a2,a3)
- 可以在{ }内再使用{ }进行归类,也可以不使用,编译器可以自动根据 列 的数量来归类(如a3)
- 行列应该后面列表的项数对应,如(a5)
2.2、二维数组传参
int a1[3][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6} };
void nb1(int a1[][4]);//不能省略列数
void nb2(int (*pa)[5]);//形参是数组指针,后面指针访问数组有讲。
2.3、高维数组
三、数组与指针3.1、指针简介既然都有了有二维数组,自然少不了三维数组,四位数组;
如 int b[10][10][10];
可以这样理解:
一维数组 - 一行数据
二维数组 - 数据表
三维数组 - 一叠数据表
对于初学者,一维和二维数组已经够用,不建议继续研究更高维。
指针?什么是指针?本质来看指针是一个存储内存地址的变量
如int类型存储的是整数,int*(指针)则存储的是地址;
要理解指针需要从以下4个方面
- 指针的类型
- 指针所指向的类型
- 指针的值或者叫指针所指向的内存区
- 指针本身所占据的内存区
首先了解指针的声明
指针的声明如下:
int a = 10;//a占4字节
int* pa = &a;//拿到的是a的4个字节中的第一个字节的地址
指针大小:32位为4字节,64位为8字节
以上涉及两个操作符符
- &
& 只有一个操作数 表示 取地址操作符
& 有两个操作数时 表示 按位与操作符- 访问a变量则通过解引用操作符(*)
如 *p = 20; 则a变量存储的值变为20;
使用指针,最重要的是理解指针的类型的意义
指针类型的意义
- 指针类型决定了解引用的权限有多大
- 指针类型决定了指针走一步走多远。int类型+1,4字节,char类型+1,1字节
int main(void)
{//内存观察时-1
int a = 0x11223344;//一个十六进制等于4个二进制,所以11223344刚好站4字节
int* pa = &a;
*pa = 0;//4个字节内存都变成了0
//a变为 0x00000000
int b = 0x11223344;
char* pc = &b;
*pc = 0;//1个字节变成了0
//b变为 0x11223300
return 0;
}
至于b为什么不是变为 0x00223344,这涉及 大小端存储 感兴趣的读者可自行查阅
现在我们以经初步了解指针,但指针作为c语言最难的一部分,主要体现在指针与其他知识的结合,而下面我们将介绍指针与数组的结合
3.2、数组名的含义
int a[10];
a为该数组的数组名
数组名在大多数情况下表示首元素的地址
a 等价于 &a[0]
但有两个例外
#includeint main(void)
{int a[10];
// 例外1
sizeof(a); //此时数组名表示整个数组,计算整个数组的大小
// 例外2
&a; //此时数组名表示整个数组,取出整个数组的地址
return 0;
}
对于特例1,很好解释,只要看看结果就行。
对与特例2,我们通过 调试窗口 中的 监视窗口 来看
看图可知:
a表示的地址和&a表示的地址都一样
结果均为:0x0000000f9c0ffb58
这并不能看出二者的区别,但通过a+1,&a+1可看出
a+1 相比 a 的地址增加了 4
&a+1 相比 &a+1 的地址增加了 40
也就是说,a+1仅仅相当于跳过了一个元素,&a+1则跳过了一个数组
故 &a 取出的是一个数组的地址
3.3、指针访问数组指针访问数组元素
int main(void)
{int arr[] = {1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int i = 0;
for (i = 0; i< 10; i++)
{printf("%p = %p\n", &arr[i], p + i);
}
return 0;
}
从上述代码,我们不难发现 arr[i]的地址与 p+i指针内存储地址 相同,则 *(p+i) 的值为 arr[i];
这表明指针和数组的关系十分密切,事实上,c语言标准在描述数组表示法时也用到了指针
arr[i] 本质上是 *(arr+i)
注意:*(arr+i) != *arr+i *的优先级要高于运算符
前者是 arr[i]
后者是 arr[0] + i
编译器会自动将 arr[i] 转换为 *(arr+i) 而这就为数组描述法增添了其他几种写法
int main(void)
{int arr[] = {1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
//其一
arr[4] == *(arr+4) == 4[arr];
//其二
arr[4] == *(p + 4) == p[4] == 4[p];
return 0;
}
指针访问整个数组
有一种指针类型叫 数组指针,顾名思义:数组指针指向的是整个数组
int main(void)
{int arr[10] = {1,2,3,4,5,6 };
//数组指针的创建
//错误
int *parr[10] = arr;//*parr先与[]结合,arr不是数组地址
//正确
int(*parr)[10] = &arr;
//指针数组的声明
double* d[5]; //指针数组,共5个元素,每个元素类型为double*
double* (*pd)[5] = &d;//数组指针,指针pd 指向一个 指针数组d
//要点:类型与数组类型一样,括号内为指针
return 0;
}
注意指针数组的声明格式:
如上double* (*pd)[5] = &d;
- 类型一致,如上double* 对应 double*
- ()不能少
- & 不能少,若去掉则等号右边表示为首元素地址
为防止将数组指针与指针数组用混,看下方解释:
- int arr[5];//整型数组
- int* parr1[10];//整型指针数组
- int(*parr2)[10];//整型数组指针,该指针指向一个数组,数组10个元素,每个元素的类型是int类型
- int(* parr3[10])[5];//首先 parr3[10]是数组,去掉,剩int(*__)[5],为数组指针,所以parr3是一个存储数组指针的数组,该数组能够存放10个数组指针,每个数组指针能够指向一个数组,数组5个元素,每个元素是int类型
证明 数组指针真的指向整个数组,看下列代码:
int main(void)
{int arr[10] = {0 };
int* p1 = arr;
int(*p2)[10] = &arr;
printf("%p\n", p1);
printf("%p\n", p1+1);//跳了4,即跳了一个数组元素
printf("%p\n", p2);
printf("%p\n", p2+1);//跳了40,即跳了一个数组
return 0;
}
那么数组指针有什么用处呢?
首先数组指针指向的是整个数组,那么用在一维数组上肯定没啥用,它一般用在高维数组
#includevoid print1(int arr[][5], int r, int c)
{int i, j;
for (i = 0; i< r; i++)
{for (j = 0; j< c; j++)
{ printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print2(int(*pa)[5], int r, int c)
{int i, j;
for (i = 0; i< r; i++)
{for (j = 0; j< c; j++)
{ printf("%d ", *(*(pa + i)+j));
//*(pa + i)为arr[i]数组,不是元素,是数组,而像一维数组 *(p+i) 为 一个数组元素
}
printf("\n");
}
}
int main(void)
{int arr[3][5] = {{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
//print1(arr, 3, 5);
print2(arr, 3, 5);//arr数组名表示首元素地址,就是第一行元素
return 0;
}
其实读者不难发现,这数组指针好像也没啥用,反倒还将代码变得更复杂了
事实上并不是,而是如今学的太少,还轮不到去使用它,日后可能某些问题就非它不可了
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
网站名称:【C语言】数组的超详细解答,走过路过别错过-创新互联
分享URL:http://cdiso.cn/article/dcdhis.html