【剧前爆米花--C语言篇】C语言数组底层原理详解

作者:困了电视剧

专栏:《C语言初阶详解》

文章简介:这是一篇关于C语言数组相关知识的剖析,本篇文章基本涵盖了C语言数组所有的知识与底层原理,分析了数组在各种情况下所代表的意义。

  

 

目录

一维数组

一维数组的定义

一维数组的初始化 

完全初始化

不完全初始化

一维数组的存储

二维数组

二维数组的定义

二维数组的初始化

二维数组的存储 

二维数组初始化省略

数组名

[]操作符的再认识

常规情况

特殊情况

数组传参


一维数组

一维数组的定义

 

这是一维数组的定义规范,type_t是数据类型,const_n是一个常量表达式,用来表示数组的大小。

注意:这里的const_n必须是一个常量,不过在c99中引入了一个全新的概念——变长数组,变长数组支持数组的大小使用变量来指定,即可以通过输入的变量来精准的控制所需数组的大小。

一维数组的初始化 

一维数组的初始化,我们可以分为完全初始化和不完全初始化

完全初始化

完全初始化即将数组中的每一个位都赋上初值

#include <stdio.h>
int main() {
	int i;
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,5 };
	for (i = 0; i < 5; i++) {
		printf("arr1[i]=%d ", arr1[i]);
		printf("arr2[i]=%d\n", arr2[i]);
	}
	return 0;
}

运行结果为:

由此可见,我们在对一个数组进行初始化的时候可以不用明确指出数组的长度。

不完全初始化

 不完全初始化即不像完全初始化那样将数组中的每一个位置都赋上初值,未赋上初值的位置,整型会被默认赋上0,char型会被默认赋上'\0'。

#include <stdio.h>
int main() {
	int i;
	int arr1[5] = { 1 };
	char arr2[5] = { 'a' };
	for (i = 0; i < 5; i++) {
		printf("arr1[%d]=%d ", i, arr1[i]);
		printf("arr2[%d]=%d\n", i, arr2[i]);
	}
	return 0;
}

 如果我没有对数组进行任何初始化,那数组中的值将会是随机值(取决于此时数组指向的哪块空间)。

#include <stdio.h>
int main() {
	int i;
	int arr[5];
	for (i = 0; i < 5; i++) {
		printf("%d\n", arr[i]);
	}
	return 0;
}

一维数组的存储

一维数组在建立的时候会在栈区给数组开辟一个空间用来存储数组中的数值,并且这段空间是连续的,我们用一段代码来验证一下。

关于栈区有不理解的可以看我的这篇博客:https://blog.csdn.net/m0_62815572/article/details/127484662?spm=1001.2014.3001.5501

#include <stdio.h>
int main() {
	int i;
	int arr[5] = { 1,2,3,4,5 };
	for (i = 0; i < 5; i++) {
		printf("&arr[%d]=%p\n", i, &arr[i]);
	}
	return 0;
}

运行结果如下图: 

 在x64下,C语言的int类型占4个字节,所以由这个运行结果可知数组在栈区中是连续存储的。

二维数组

二维数组的定义

二维数组在定义上规则和一维数组一致,即在c99之前[]中的值都必须是一个常量表达式,二维数组在语法上的定义可以抽象理解为第一个[]中的值为行数,第二个[]中的值为列数。

二维数组的初始化

二维数组的初始化也分为完全初始化和不完全初始化,这里偷个懒展示一个不完全初始化。

#include <stdio.h>
int main() {
	int i, j;
	int arr[3][3] = { 1 };
	//arr[0][0] = 1;
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 3; j++) {
			printf("arr[%d][%d]=%d\n", i, j, arr[i][j]);
		}
	}
	return 0;
}

二维数组的存储 

先说结论:

 比较难以理解的,但对的。

 我们用一段代码来验证一下:

#include <stdio.h>
int main() {
	int i, j;
	int arr[3][3] = { 1 };
	//arr[0][0] = 1;
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 3; j++) {
			printf("&arr[%d][%d]=%p\n", i, j, &arr[i][j]);
		}
	}
	return 0;
}

 其运行结果为:

 我们可以清楚地看到arr[0][2]和arr[1][0]之间的地址只差了4个字节即一个int类型。

二维数组初始化省略

我们在理清二维数组在内存中的存储后,思考一个问题:在初始化一个二维数组arr[i][j](i,j为常量)时,我能不能省略i或省略j。

先用代码说明结论:

可以省略掉行,但不能省略掉列。

其实我们可以这样进行理解,我们可以把二维数组看成是一个数组元素是数组的数组,行数就是存储的元素的个数,列数就是每个元素(数组)存储的数的个数。这样就可以对省略行数的二维数组进行初始化了。 

数组名

[]操作符的再认识

在我们使用数组的时候我们都知道,当我们需要使用数组中的某个元素时,我们会通过"数组名+[元素下标]"这种方法去访问,对于数组来说,数组名又是首元素的地址,我们也可以通过数组名加减的方式来对数组中的元素进行访问。

此时,我们不妨大胆猜测一下,[]代表什么意思,我arr[1]是不是就完全等价于*(arr+1)?我们用一段代码验证一下,由于加法具有交换律,所以arr+1也可以看成1+arr,所以如果1[arr]也能输出数组的第二个元素,那就说明完全等价。

 事实说明,完全可以这样理解。现在是不是对[]有了更深的理解了。

常规情况

在C语言中一个数组的数组名在常规情况下,他表示的是这个数组首元素的地址。

#include <stdio.h>
int main() {
	int arr[3] = { 1 };
	printf("&arr=%p\n", &arr);
	printf("&arr[0]=%p", &arr[0]);
	return 0;
}

运行结果如下:

特殊情况

数组名的意义存在两个特殊情况:

1、 sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小,单位是字节的

2、 &数组名,数组名表示整个数组,取出是整个数组的地址

#include <stdio.h>
int main() {
	int arr[10];
	printf("&arr[0]=%p\n", &arr[0]);
	printf("&arr[0]=%p\n", &arr[0]+1);

	printf("&arr=%p\n", arr);
	printf("&arr=%p\n", arr+1);

	printf("&arr=%p\n", &arr);
	printf("&arr=%p\n", &arr+1);

	return 0;
}

运行结果如下图:

 由此可以看到,arr表示数组首元素的地址,而&arr的输出结果也是首元素的地址,但是当&arr+1时——&arr的下一个地址却是走完整个数组后的地址。

数组传参

我们在写函数的时候,经常会写一些需要传数组形参的函数,当数组作为函数参数进行传递时,这个数组会发生一个降级,即他现在不管在什么情况下他都只能表示这个数组首元素的地址。

举个栗子:

#include <stdio.h>
int printArr(int arr[]) {
	int size = sizeof(arr) / sizeof(arr[0]);
	printf("%d\n", size);
	int i;
	for (i = 0; i < size; i++) {
		printf("%d ", arr[i]);
	}
}
int main() {
	int arr[3] = { 1,2,3 };
	printArr(arr);
	return 0;
}

我写一个输出数组所有元素的函数,我用sizeof这个函数来求这个数组一共有几个元素。

运行结果如下图;

可以看到我的size不是3而是2,在x64下,C语言的int型指针变量占8个字节,此时size正好是2,符合程序运行的结果。这意味着我传过去的arr仅仅是一个首元素的地址而不是数组地址,现在让我们改进一下程序:

#include <stdio.h>
int printArr(int arr[],int size) {
	printf("%d\n", size);
	int i;
	for (i = 0; i < size; i++) {
		printf("%d ", arr[i]);
	}
}
int main() {
	int arr[3] = { 1,2,3 };
	int size = sizeof(arr) / sizeof(arr[0]);
	printArr(arr,size);
	return 0;
}

 我仅仅把size的计算从printArr函数中移到main函数中,其运行结果如下:

这说明了两个知识点:

一是我之前提到过的数组名表示整个数组地址的特殊情况。

二是当数组名作为参数传出去后,它会进行一个降级,即只能表示首元素的地址,无法在表示整个数组的地址。 

以上就是我所总结的C语言的数组的全部内容,如有疏漏欢迎进行补充!

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值