C语言学习第二阶段整理复习——数组

本文深入讲解C语言中数组的概念,包括一维数组和二维数组的定义、存储方式、数组名与内存单元的关系、数据个数的计算及数组首地址的加减法运算,通过实例帮助读者理解数组操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组

什么是数组? 数组是一组具有相同数据类型的变量的集合体, 其存储的位置紧挨在一起,是连续的

我把数组联想为一个大的阶梯教室,按照椅子背上面的座位号顺序排列的座位
阶梯教室的椅子
椅背上没有编号,可以和自己本校的带有编号的椅子进行配对,也可以将图中的椅子按照顺序进行编号即可。每个椅子可以坐下一个人(相当于可以存放相同数据类型的内存单元),椅背的编号就是内存单元所对应的数组下标。

一维数组

形如:

数据类型 变量名[常量表达式]

的定义形式,即为一维数组。其[]内存放的是具有相同数据类型的变量的个数

一维数组的数据在内存中的存储

按照低地址优先的话,一维数组的数据在内存中的存放顺序为:从数组下标为0的内存单元开始存放数据,一直到最后一个数据

写个代码测试一下:

void initArr(int arr[], int len) 
{
	for (int i = 0; i < len; ++i)
	{
		arr[i] = rand() % 100;
	}

}
void printArr(int arr[], int len) 
{
	for (int i = 0; i < len; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
#define ARRSIZE(a) sizeof(a) / sizeof(a[0])

int main(int argc, char* argv[])
{
	srand((unsigned int)time(NULL));

	int arr[100] = {0};
	initArr(arr, ARRSIZE(arr));
	printArr(arr, ARRSIZE(arr));
	return 0;
}

DEBUG版本扔到OllyDBG中进行查看:
Debug版本的数组初始化
默认采用的是__cdecl调用约定,调用者负责清理堆栈,参数入栈顺序为从右到左,故可以写出其调用函数的形式:

EBP == 0x0012FF80h
所以lea eax, [ebp - 190] == 0x0012FDF0h
initArr(0x0012FDF0h, 0x64);

到堆栈窗口查看是否符合我的逻辑推理:
堆栈显示图
在执行完毕for循环后,看打印出来的结果是不是和其存储在内存的数据是一样的。按照低地址优先原则,在0012FDF0处存的数据应该是0x0000000Fh,转换成10进制数据为15

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190128164341188.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Bhbmhkb25nNjUx,size_16,color_FFFFFF,t_70
输出结果:
在这里插入图片描述
故实验结果与所想一致。

数组名与内存单元的关系

数组名的本质是常量指针。

把数组名理解为内存单元的别名(相当于阶梯教室的门牌号),代表的是这块连续内存单元的首单元的地址,通过这个别名,可以直接读取相应的内存单元或者写入相应的内存单元。而数组下标是阶梯教室的座椅的座位号。通过数组名 + 数组下标可以唯一确定一个内存单元。

我个人的理解是,通过数组名[常量表达式]所在位置(在赋值号的左侧还是右侧)来确定是读取相应的内存单元数据还是写入相应的内存单元的数据,而不代表其所在的内存单元的地址。

类比于学生的学生名和学生的学号。通过学生名可以唯一找到这名学生(在现实生活中可能会重名,但是在C程序的编译阶段会保证变量签名是唯一的),可以读取这名学生的其他信息,也可以修改其相应的一些信息,但是不代表其学生号。因此,数据名[下标]仅仅是其别名,而不代表该内存单元的地址。

综上,获取数组名所对应的内存单元的地址需要利用到取地址符号&,故:&数组名[常量表达式]代表的是该内存单元的地址。

一维数组的数据个数的计算

可以利用#define进行计算

#define ARRSIZE(a) sizeof(a)/sizeof(a[0])

而如果用数据做函数的参数的话,它就会退化为一级指针,不能通过该定义进行求解数组的数据个数。

在上述例子中,也验证了这点,看initArr(int arr[], int len)的反汇编
在这里插入图片描述
其采用的是LEA EAX, DWORD PTR SS:[EBP - 190]PUSH EAX的形式,给函数initArr传递所需的参数,对于参数int arr[],其传递的是数组内存所在的内存单元的首地址,而不是将整块内存拷贝过去,因此当数组名作为函数参数传递时,退化为指针。

综上,当数组名作为函数参数传递时,不能直接计算其数据个数。

数组首地址的加减法运算

int a[10] - 定义一个数据类型为int型的数组。

注意:

int a[10] = {0};

a + 1; // 结果为多少?
&a + 1; // 结果为多少?

在这里,如果不带&的话,是加的sizeof(type(a));如果带&的话,是加的sizeof(type(&a))。(在这里的type是我自己定义的函数,即变量所对应的数据类型)

二维数组

二维数组就简单多了,是一维数组的扩展。不过在遍历二维数组的时候,要根据数据量的大小,来采用变量方式(行优先遍历或者列优先遍历)

如果数据量过大的话,采用列优先变量违背了局部性原理,可能会频繁的产生缺页中断,进行数据的换入换出,影响效率。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值