栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言数组的深度分析

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言数组的深度分析

此文章在学习二维数组和数组指针,指针数组要对指针有一定的认识才容易读懂
此文章讲述了一维数组和二维数组要必须掌握的东西,以及二维数组在内存中存储的深入解析,数组指针一维数组指针和二维数组指针的不同操作
大家发现有错误和改善的地方,希望提出

文章目录
  • 一、一维数组(除字符数组)
    • 1. 一维数组的初始化与定义方式
    • 2. 一维数组在内存中的存储
  • 二、二维数组
    • 1.二维数组的初始化与定义
    • 2.二维数组的使用
    • 3.二维数组在内存中的存储方式
  • 三、一维字符数组
    • 1.一维字符数组的初始化与定义
    • 2.一维字符数组的细节
      • 1.字符串的输入
      • 2.字符的输入
    • 3.一维字符数组在内存中的存储方式
  • 四、学习数组作为形参时,先具备一些知识
  • 五、数组作为形参
    • 1.一维数组作为形参
    • 2.二维数组作为形参
  • 六、数组指针和指针数组
    • 1.一维数组的数组指针
    • 2. 二维数组的数组指针
  • 七、指针数组
  • 八、最后

一、一维数组(除字符数组) 1. 一维数组的初始化与定义方式
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	int arr2[10] = {1,2,3,4};
	int arr3[] = {1,3,4,5,6};
    // int arr4[10];不推荐
   int arr4[10] = {0};
	//	int arr5[];//错误

点击调试一刻,变量声明提前,得到的值是内存中的乱值


调试完毕后,才得到有效值,除了int arr4[10];未定义的,系统也没有赋予0

总结:
1. 没有赋值或定义时给足够多的空间时,多余的部分系统默认以0来初始化
2. int arr5[ ] ; 是一种错误的写法,
3. 不推荐int arr4[10]不赋值,建议赋予0。arr[10] = { 0 };

2. 一维数组在内存中的存储
	int main(){
	//一维数组的内存存储位置 
	int i , j;
	int arr[10] = {0};
	for(j=0;j<10;j++){
		printf("arr[%d]的内存存储位置为%Xn",j,&arr[j]);
	}
}
结果如下

总结:
1. 一维数组的在内存中存储是连续的,一旦开辟则是开启一连串连续的空间
2. 在定义为int 类型时,是4个字节,所以他们的地址相继为4个,同理float为8个,char为1个,sort int为1个等


二、二维数组 1.二维数组的初始化与定义
int main(){
	int arr[3][4] = {{1,2,3,4},{1,2,3,4},{1,2,3,4}};
	int arr2[3][4] = {{1,2,3,4}};
	int arr3[3][4] = {{1},{2},{3}};
	int arr4[3][4] = {0};
	int arr5[][4] = {1,2,3,4,5,6};
	int arr6[3][4]; //不推荐 
//	int arr5[][] 错误写法 
}

总结
1. 二维数组的第二个中括号不可以省略数字
2. 未赋值的系统默认用0来替代

2.二维数组的使用
int main(){
	//二维数组的使用
	//用循环遍历二维数组 
	int arr[3][4] = {{1,2,3,4},{4,5,6,7},{8,7,8,9}};
	int len = sizeof(arr) / sizeof(arr[0][0]);
	printf("%dn",len);
	int i , j;
	for(i=0;i<3;i++){
		for(j=0;j<4;j++){
			printf("%d ",arr[i][j]);
		}
		printf("n");
	}
}
结果如下:
12
1 2 3 4
4 5 6 7
8 7 8 9

注意: sizeof( ) 是求字节数的, 在这里 arr数组为int的类型占48个字节(其实底层是用计算地址来得出多少个字节的)。同理arr[0][0]为int类型计算出来占用了4个字节,从而就得出了长度len为12个。在C语言上一般用这种方式来实现求数组长度。这里之所以打印出来,是为了看一下是否有错误(可有可无的)

3.二维数组在内存中的存储方式
int main(){
	int arr[3][4] = {{1,2,3,4},{4,5,6,7},{8,7,8,9}};
	int i , j;
	for(i=0;i<3;i++){
		for(j=0;j<4;j++){
			printf("arr[%d][%d] 的内存地址是 %X",i,j,&arr[i][j]);
			printf("n");
		}
	}
}

总结:
1. 由此可见,二维数组的背后内存也是连续的
2. 其实也是一个线性的,本质上是个一维数组,只不过呀只是一维数组里面的元素是一个数组

对总结第2点来进行详细的说明,若理解了,则对二维数组和指针的结合有着巨大的帮助!!
1.现在我们都知道二维数组本质是一维数组里面嵌套着数组了,那么

2.现在证明一下

int main(){
	int arr[3][4] = {{1,2,3,4},{4,5,6,7},{8,7,8,9}};
	printf("%dn",arr[1]);
	printf("%xn",arr[1]);
	printf("%dn",arr[2]);
	printf("%xn",arr[2]);
	printf("%dn",arr[3]);
	printf("%xn",arr[3]);
}

结果如下:

结论:
1. 无论是用%d 还是 %x 都是打印出一个地址,无疑上面的图很好的说明了二维数组的结构
2. 因为arr[ 1 ],arr[ 2 ],arr[ 3 ]默认的是arr[ 1 ][ 0 ],arr[ 2 ][ 0 ],arr[ 3 ][ 0 ]的地址,所以他们相差的恰巧是3个字符,也就是12个字节
3. 二维数组不能省略后中括号的原因也可以知道了,那就是不告诉数组,数组不知道要怎么分配多少个,并且空间大小是多少
4. 此思维建议用于学习二维数组指针难点


三、一维字符数组

一维字符数组可以表示为字符串,但是一维数组可以存字符串也可以存字符(一般来说我们画一个等号)

1.一维字符数组的初始化与定义
int main(){
	char arr[20] = {"ni hao a"};
	char arr2[10] = "ni hao a";
	char arr3[] = "ni hao a";
	char arr4[] = {'w','o','h','e','n','h','a','o'};
	char arr5[5] = "12345";//这种初始化可能出问题,因为字符数组都是默认以''为结束标志的,所以要放一个进去,没空间放可能出错 
	补充:
	char arr6[20] = "";//这种方式是初始化,默认每一项都是'',注意不是空格	 
}

总结:
1. 定义的空间大于初始化的字符时,默认用’’来替补
2. 字符数组是以’’为结束标志的
3. 在 ASCII表中代表的是第一个数字0,代表空字符,所以遇到’’就转义为空字符
4. 字符数组里面的字符,是用对应的ascii码值进行存储的

2.一维字符数组的细节 1.字符串的输入

大多数人都想到的是scanf,但是注意的是,scanf缺点是,不可以输入空格,若输入空格,则结果如下

int main(){
	char s[20];
	scanf("%s",s);	 
	printf("%s",s);
}

输入: nihao ma
输出: nihao

但是除了<,?:"} 等等,空格也是字符,那么解决输入空格的问题,录入字符串的时候我们用gets函数,然而配套的也有puts函数

int main(){
	char s[20];
	gets(s);
	puts(s);
}

输入: nihao ma
输出: nihao ma

puts函数和printf函数是一样的输出功能,只是puts能自动换行–相当于多了一个n

2.字符的输入
int main(){
	char s[20];
	scanf("%c",&s[1]);
	scanf("%c",&s[2]);
	printf("%c  %cn",s[1],s[2]);
	printf("%d  %dn",s[1],s[2]);
}
结果如下:
          s
          s
        
          115  10

当两个scanf同时使用,想输入一个s后按下确认键打算给s[2]赋值是,可惜的是,第一个接收是正常了,第二个接收到了一个换行键’n’,然后程序以为你已经输入完毕了,一个是s,一个是’n’,然后结束了。
打印结果也是如此,s[2]结果是10,在ASCII表中是换行键的值
我们要解决这种输入字符出现的问题,引用fflush(stdin);这种函数的作用是清除缓存,清除确认键带来的缓存

int main(){
	char s[20];
	scanf("%c",&s[1]);
	fflush(stdin);
	scanf("%c",&s[2]);
	printf("%c  %cn",s[1],s[2]);
	printf("%d  %dn",s[1],s[2]);
}
结果如下
            N
            H
            N  H
            78  72
3.一维字符数组在内存中的存储方式
int main(){
	char s[12] = "how are you";
	int i;
	int len = sizeof(s) / sizeof(s[0]);
	for(i=0;i
		printf("第%d个字符的地址是%Xn",i,&s[i]);
	}
}

地址相差1,也是一个连续的内存地址,和上面的都一样


四、学习数组作为形参时,先具备一些知识
int main()
{
	int arr[10] = {1,2,3,4,5,6,7};
	printf("%Xn",arr);
	printf("%Xn",&arr[0]);
    printf("%Xn",&arr[1]);
}
结果如下:
		62FDF0
		62FDF0
		62fdf4


五、数组作为形参 1.一维数组作为形参
int check(int arr[],int len)
{
	printf("nnnn"); 
	printf("这是arr的地址首地址%Xn",arr);
	printf("这是arr下一个元素的地址%Xn",arr+1);
	printf("这是&arr默认为形式参数int arr[]首地址%Xn",&arr);
	printf("这是&arr+1取整个形参数组的下一个地址%Xn",&arr+1);
}


int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	int len = sizeof(arr) / sizeof(arr[0]);
	printf("这是arr的地址首地址%Xn",arr);
	printf("这是arr下一个元素的地址%Xn",arr+1);
	printf("这是&arr取整个数组的地址,默认为首地址%Xn",&arr);
	printf("这是&arr+1取整个数组的下一个地址%Xn",&arr + 1);
	printf("arr[9]的地址%Xn",&arr[9]);//arr[9]指向的是这个单元格的第一个字节的地址62FE14,固然他的尾巴是62FE17
	printf("arr[9] + 1的地址%Xn",&arr[9] + 1);//所以他的 &[9] + 1 是 62FE18
	check(arr,len);
}

总结:
1. 除了普通变量的传递是复制的以外,数组并不是通过复制的方式来传递,试想一下如果数组非常长的时候,那么性能是非常差的,所以用到了很好的传递方式,传地址的方式
2. 用形参作为参数的时候,在传递的那一刻,int arr[ ] 接收到了一个地址(这个地址是arr的地址,也就是arr[0]首元素的地址),假设arr[0]的地址是62FDF0。 相当于 int arr[] => 62FDF0;这个arr保存的是一个地址,作用相当于指针,所以用指针 int * X 接收也是一样的
3. 传递参数完成后,他就可以正常的像数组一样进行操作了
4. 在求数组长度的时候,要先求好了,用传参的方式传过去,否则求不到真实长度
5. 形参的接收格式有 : int arr[ ];int arr[20]里面如果写的话最好写的比原来的大;int * x;

2.二维数组作为形参
#include 


int check(int arr[],int len)
{
	printf("nnnn"); 
	printf("这是arr的地址首地址%Xn",arr);
	printf("这是arr下一个元素的地址%Xn",arr+1);
	printf("这是&arr默认为形式参数int arr[]首地址%Xn",&arr);
	printf("这是&arr+1取整个形参数组的下一个地址%Xn",&arr+1);
}


int main()
{
	int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,0,0};
	int len = sizeof(arr) / sizeof(arr[0][0]);
	printf("这是arr的地址首地址%Xn",arr);
	printf("这是arr下一个元素的地址%Xn",arr+1);
	printf("这是&arr取整个数组的地址,默认为首地址%Xn",&arr);
	printf("这是&arr+1取整个数组的下一个地址%Xn",&arr + 1);
	printf("&arr[2][3]的地址%Xn",&arr[2][3]);
	printf("&arr[2][3] + 1的地址%Xn",&arr[2][3] + 1);
	check(arr,len);
}

总结:
1. 形参的书写方式一定要在第二个中括号带上值
2. 可以写为指针 int (*p)[ 4 ],可写为二维数组int arr[ ][ 4 ],arr[ 3 ][ 4 ]
3. 二维数组大致都和一维数组一样,传的是一个地址

六、数组指针和指针数组

*符号优先级是 ( ) > [ ] > ***
数组指针,那就是一个指向数组的指针 int ( p)[5] ,p先和*结合,然后再和中括号结合

1.一维数组的数组指针
int main()
{
 	int arr[3] = {1,2,3};
	int (*p)[3]; 
	p = &arr; //要用p来赋值,不带星号,就像指针一样,int *p;  p = &a;一样
	
	printf("%Xn",*p);//62FE00
	printf("%Xn",*&arr);//62FE00
	
	printf("%Xn",*(*p+1));//2
	printf("%Xn",*(arr+1));//2	


	printf("%dn",arr[0]);//1
	printf("%dn",(*p)[0]);//1
}

下面是对一维数组指针的解释

总结:
1. 为什么要用p=&arr 不用 p = arr,本质上都是地址,但是&arr是指向整个数组,把数组作为一个整体,而arr就指首个单元
2. 把p=&arr赋值后,p自然就指向了&arr 也就是 arr,所以在用数组指针操作的时候,可以操作为p + 1 =>相当于arr + 1。可以操作为(p)[0] =>相当于arr[0],要加( )括号的目的是,中括号的优先级比高,所以要保持arr等价于p那就让他们不可分开

2. 二维数组的数组指针

根据上面解释的二维数组在内存中的存储方式的总结2中说的很清楚,其实二维数组是一维数组,只是一维数组里面的元素存的是数组(可以想象存的是数组的地址)
二维数组的指针

int main()
{
 	int arr[3][4] = {{10,20,30,40},{50,60,70,80},{90,100,110,120}};
	int (*p)[4] = arr;//这里和一维数组的取值不一样,这里取arr首地址就可以了 (因为第一层arr存的是地址) 


	printf("%dn",*p);	
	printf("%dn",arr[1][1]); //60
	printf("%dn",*(*(p+1) + 1)); //60
	printf("%dn",(*(p+1))[1]); //60
}

总结:
1. 要注意小括号,中括号,星号的优先级问题,这样就轻而易举解决数组指针的困难题了。
2. 一维数组的数组指针和二维数组的数组指针还是有区别的(一维数组赋值需要取地址赋值,二维数组不需要),原因都是为了在使用*号取值的时候解决问题,如果不按照规定去写,有的编译器还会报错误像
dev++报了个警告

七、指针数组

指针数组无疑,是在数组里面保存的都是指针,他只存了指针也就是一些地址。你们仔细思考一下,是不是和二维数组第一层的那个数组很相似呢?
走入正题
指针数组的定义和初始化
int * p[5] 先和5结合,然后在和*号结合

int main()
{
	int *p[5];
	int arr[10] = {1,2,3};
	int arr2[5] = {1,2,3};
	int arr3[] = {1,23,4};
	int arr4[3][5]={1,23,4,3,43,5,7,4,6};
	int arr5[][4]={1,23,36,6,34,5,1};
	p[0] = arr;
	p[1] = arr2;
	p[2] = arr3;
	p[3] = arr4;
	p[4] = arr5;
	int i; 
	for(i=0;i<5;i++){
		printf("%dn",p[i]);
	}
}
结果如下:
              6487472
              6487440
              6487424
              6487360
              6487328

八、最后

作者也是一名C语言学习者和C语言爱好者,阅读多次C语言对数组部分的总结,若是阅读到,希望大家认可。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1030547.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号