【C语言详解】数组的存,用,在内存中的存储模式

1.数组是什么?

数组是一类相同类型数据的集合。众所周知,想要使用数据的第一步是把数据存储起来,在C语言中:数组可以存储数据。当我们想要把一类相同类型元素(比如1,2,3…)存储到计算机中时,我们就可以把它们存放到数组中,以达到把数据存储到计算机中的目的。

2.一维数组的创建和初始化

Q:创建一维数组的语法:

A:数组中元素的数据类型 数组名称[一个常量表达式,用来指定数组大小] ;

代码演示:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	int   arr[3+5];
//类型  名称[常量表达式]

	//创建一个名称是arr2的数组
	//用来存放5个字符
	char arr2[5];

  //创建一个名称是arr3的数组
	//用来存放30个浮点数
	float arr3[30];
	
	return 0;
}

Q:一维数组的初始化:

A:

  1. 在创建数组的同时,给数组的空间中存放一些合理的初始数据。这个操作即数组的初始化

  2. 在对数组进行初始化时,因为数组里放的不止一个数据值,所以用{}把这些初始化值括起来。

  3. 数组初始化分为完全初始化和不完全初始化。

    • 完全初始化指:创建空间大小为 n byte的数组,同时在数组中存放x(x=n)个初始数据值。
    • 不完全初始化指:只给数组的一部分空间存放初始值。剩余的空间都默认存放初始值0。
  4. 需要特别注意的是字符数组的初始化。字符数组的初始值可以是字符,也可以是字符串。事实上,创建字符数组就是为了更好的存放字符串。

  5. 指定数组大小和数组初始化可以同时操作(即完全初始化和不完全初始化)。也可以选择其一操作:第一种情况是,指定数组大小,不初始化。此时所有数据值默认为0。第二种情况是,不指定数组的大小,但是给数组初始化。此时数组的大小会根据初始化的内容来确定。

代码演示:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdio.h>
int main()
{
	//演示3;完全初始化和不完全初始化
	//创建空间大小为5byte的数组arr1,同时在arr1中存放5个初始数据值
	int arr1[5] = { 0,1,2,3,4 };
	//创建空间大小为5byte的数组arr2,同时在arr2中存放3个初始数据值。
	//此时,另外两个初始数据值默认为0。
	int arr2[5] = { 1,2,2 };
	
  //演示4:
	//字符数组的初始值是字符
	char ch3[5] = { '1','a','*' };:
	//字符数组的初始数据值是字符串。
	char ch4[5] = "abc";
  
  //演示5:
  //指定数组大小,同时初始化
  int a[3] = {0,1,2};
  int b[3] = {0};
  //指定数组大小,不初始化,此时数组c中所有数据值默认为0
  int c[5] ;
  //初始化,但不指定数组大小.此时数组d的大小为3
  int d[] = {1,2,3};


   
	//演示6:
	//分析数组ch5,ch6,ch7
	char ch5[5] = { 'a','b',99};
	char ch6[5] = { 'a','b','c'};
	char ch9[5] = {'a','b','99'};

 //演示8
  //试分析下列代码的运行结果:
  char ch7[] = "abc";
  char ch8[] = { 'a','b','c'};
  printf("%s\n",ch7);
  printf("%s\n",ch8);
  printf("%d\n",strlen(ch7));
  printf("%d\n",strlen(ch8));
  //库函数strlen可以用来求字符串长度

	return 0;
}
演示6:字符数组的初始值99,‘99’,"99"你分清了吗?

演示6调试结果:
在这里插入图片描述
演示6调试结果分析:

  • 在计算机看来,字符本质上就是一个整数。即每一个字符都有一个对应的数字编码。在C语言中,该数字编码就是ACSII码值。例如字符’c’的ACSII码值是99。字符’\0’的ACSII码值是0。所以在字符数组ch5,ch6中,初始值赋99就是初始值赋字符’c’,另外两个默认初始值为0即默认初始值赋字符’\0’。
  • 众所周知,单个字符在代码中用单引号’‘引起来,字符串用双引号’’ "引起来。
  • 在字符数组中,99是ACSII码值。'99’被计算机视为字符9。"99"被计算机视为字符串99。
演示8:字符串中’\0’的一二件事

演示8调试结果:
在这里插入图片描述
演示8运行结果:
在这里插入图片描述
演示8调试结果分析:

  1. 字符串"abc"有一个隐藏的结束标志,即’\0’。
  2. 这里的 '\0’在数组中占据一个空间大小 ,即数组ch7中的初始化值"abc",它的真实面目是’a’‘b’‘c’‘\0’,所以数组ch7的大小是4 * 1byte。数组ch8中初始化值就是三个字符’a’,‘b’,‘c’,所以数组ch8的大小是3 * 1byte。
  3. 需要注意的是:在计算字符串长度时,'\0’仍作为字符串的结束标志,但‘\0’不算入字符串长度内。ch7中,字符串长度是3。ch8中,初值化的三个字符值构成的字符串没有结束标志,所以ch8中字符串长度不确定,是一个电脑生成的随机值。

演示8运行结果分析:

  • 数组是在栈上开辟的,栈是一个大空间。把大空间大致分为有用空间和空闲空间。我们在创建数组时,就是从栈这个大空间中申请一个空闲空间。(这里的空闲空间是一个相对的概念,有用空间代表着里面存着有用的数据,而在栈上开辟数组成功后,数组所占用的空间就从空闲空间变为有用空间了)。
    • 当开始执行%sch7的命令,计算机按照一定顺序开始访问栈,从ch7中字符串首地址开始打印,遇见’\0’停止打印。
    • 当开始执行%s,ch8的命令,计算机按照一定顺序开始访问栈,从ch8中字符串首地址开始打印,直到在栈里遇见\0停止打印。ch8中没有‘\0’,计算机就在栈中一直访问,直到遇见’\0’。烫烫烫烫蘟bc就是栈中其他空间的数据

微拓展:数组大小可以用变量指定吗?点击查看答案1

3.一维数组的使用

  1. 数组中的每个元素都有自己的下标,我们可以通过下标引用操作符[]找到元素的下标,从而找到元素。规定第一个元素的下标为0,后面元素的下标向后依次递增1。
  2. 需要注意的是:在上文,创建一维数组的语法中,那里数组名称后的[]只是一种语法形式,不是下标引用操作符。即在创建数组时使用的[]是语法形式,在访问数组元素时使用的[]就是下标引用操作符

代码演示:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	//演示1
	      int arr[5] = { 1,2,3,4,5 };
//该数组中元素下标依次对应是 0 1 2 3 4

   //演示2 
   int arr1[5] = {1,2,3,4,5};//在创建数组时使用的[]是语法形式
   printf("%d\n", arr1[0]);//在访问数组元素时使用的[]就是下标引用操作符

   //演示3
	//打印数组中的某个元素
	int arr2[5] = {1,2,3,4,5};
	printf("%d\n", arr2[0]);
	//打印数组中的全部元素(借助循环)
	int i = 0;
	int arr3[5] = { 1,2,3,4,5 };
	for (i = 0; i < 5; i++)
	{
		printf("%d", arr3[i]);
	}
	return 0;
}

运行结果:
在这里插入图片描述

4.编写代码计算数组中的元素个数

  1. 利用 sizeof() 分别计算出数组中所有元素所占的空间,和数组中任一个元素所占的空间。总空间除以单位空间就得到数组中的元素个数
  2. 数组也有数据类型。数组是一类相同数据类型元素的集合。元素的数据类型就是数组的数据类型。

代码演示:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
  //演示1
  //数组arr中存放了10个int类型的元素。即10个int类型向内存申请了4*10byte空间。其中每个元素都占4byte空间。
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("%d\n", sz);

  //演示2
	printf("%d\n", sizeof(arr)); //10*4byte
	printf("%d\n", sizeof(int [10])); 
	
	printf("%d\n", sizeof(arr[0]));//4byte
	printf("%d\n", sizeof(int));//4byte
	//一个int类型向内存申请4byte空间
	return 0;
}

运行结果:
在这里插入图片描述

5.一维数组在内存中的存储

Q:打印数组中每个元素在内存中的地址值

A:为了研究一维数组在内存中的存储,我们利用取地址符号&和%p操作2,把数组中每个元素在内存中的地址(值)3打印在屏幕上,从数组的地址入手研究一维数组在内存中是如何存储的。

代码演示:打印数组中每个元素的地址

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
//打印数组arr中6个元素的地址值
	int arr[6] = { 0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("&arr[%d] = %p\n",i,&arr[i]);
	}
	return 0;
}

运行结果:
在这里插入图片描述
运行结果分析:

  1. 众所周知十进制就是0,1,2,3,4,5,6,7,8,9,进位规则是满十进一。
  2. 在运行结果中,数组arr中各个元素的地址值是16进制数。同十进制,十六进制就是0,1,2,3,4,5,6,7,8,9,A(10),B(11),C(12),D(13),E(14),F(15)。进位规则是满十六进一。
  3. 观察数组arr中各个元素的地址值不难发现:== 后一个地址值 = 前一个地址值 + 4 ==。例如,第一个地址值是005DFC4C,C是12,C+4=16,遵循满十六进一,所以第二个地址值就是005DFC50。即满足规律:后一个地址值 = 前一个地址值 + 4。
  4. 为什么是+4呢?
    • 为什么是+:数组元素在内存中是连续存放的。
    • 为什么是4:一个int类型向内存申请一个4byte空间存放一个元素。数组arr的数据结构是int,即arr中的每个元素的类型都是int。

Q:一维数组在内存中是如何存储的?

A:数组元素在内存中是连续存放的

  1. 数组元素的地址值就是数组元素在内存中所占空间的编号。在内存中,数组arr中的6个元素分别所占的6块空间呈线性排列,同时随着数组元素下标的增长,数组元素的地址值也在有规律的递增

  2. 图片演示:
    在这里插入图片描述

  3. 思考:目前为止,我们可以通过哪几种方式使用一维数组中的元素?答案点击右上标4

Q:灵活应用一维数组在内存中的存储

A: 因为一维数组元素在内存中是连续存放的,所以知道数组的首元素地址,就能顺藤摸瓜找到后面的所有元素的地址,从而找到数组元素。

  1. 代码演示:
    • 创建指针类型的变量p,存放数组arr的首元素地址值,即arr中下标为0的元素的地址值。利用p+i顺藤摸瓜,在屏幕上打印数组arr中所有元素的地址。利用*(p+i)顺藤摸瓜,在屏幕上打印数组arr中的所有元素。
    • p+i:把p空间中存放的地址值作为首地址值,顺着首地址向后跳i个元素类型,这里的第i个元素类型申请的空间的编号 (元素地址值) 就是p+i
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	int arr[6] = {1,2,3,4,5,6};
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = &arr[0];
	//指针类型int*向内存申请了一块空间,命名为p,存放地址值。
	
	//演示1:理解p+i
	//p+i:把p空间中存放的地址值作为首地址值,顺着首地址向后跳i个int空间,这里的第i个int空间的地址值就是p+i。
	for (i = 0; i < sz; i++)
	{
		printf("%p == %p\n", p + i ,&arr[i]);
		
	}

	//演示2:
	//利用地址值打印数组中的元素
	int n = 0;
	for (n = 0; n < 6; n++)
	{
		printf("%d  ", *(p + n));//*是解引用操作符,把地址值转换成该空间中的元素值。
	}
	return 0;
}

运行结果:
在这里插入图片描述

  1. 图片演示
    在这里插入图片描述

一维数组的创建,初始化,使用以及在内存中如何存储就总结到这里啦,接下来我们要学习二维数组。一维数组只有一行,可以把二维数组简单理解为多行一维数组5


6.二维数组的创建和初始化

Q:创建二维数组的语法:

A:数组中元素的数据类型 数组名称[行][列] ;

代码演示:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	//创建一个二维数组arr,里面存放3*4(3行4列)个int类型元素
	int arr[3][4];

	//创建一个二维数组arr2,里面存放5*6(5行6列)个char类型元素
	char arr2[5][6];
	return 0;
}

Q:二维数组的初始化:

A:

  1. 对二维数组赋初始值时,可以忽略行和列,在不超过该二维数组存放元素的最大限度内(最大限度就是行*列的积),直接对数组赋初值。此时计算机默认初始值按照先行后列的顺序存放在数组中。空间未赋满时,默认初始值是0。
  2. 通过大括号{}中嵌大括号{},不忽略行和列,实现自己想给某行赋几个值,就赋几个值,想给某列赋几个值就赋几个值。未赋满的行默认初始值是0。
  3. 对二维数组初始化时,行可以省略,列不能省略。即二维数组中一行有几个元素必须在创建该数组时就规定。

代码演示

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	//演示1
	int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };

	//演示2
	int  arr2[3][4] = { {1,2},{3,4,5},{10,12} };

	//演示3
	int  arr3[][4] = { {1,2},{3,4,5},{10,12} };
	//通过内层{}可以判断出二维数组有几行,即行可以省,列不能省。
	return 0;
}

调试结果:
在这里插入图片描述

7.二维数组的使用

  1. 同一维数组的使用一样,利用下标引用操作符[],实现对二维数组中元素的访问。
  2. 二维数组中的每行每列都有下标。C语言规定:第一行下标是0,后面行下标依次递增1 。第一列下标也是0,后面列下标依次递增1 。我们可以通过下标引用操作符[]确定找到二维数组中元素的行下标和列下标,从而找到元素
  3. 在屏幕上打印二维数组中的某个元素
  4. 在屏幕上打印二维数组中的所有元素

代码演示:

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
int main()
{
	//演示3
	int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n", arr[1][2]);

	//演示4
	int arr2[3][4] = { 1,2,3,4,5,6,7,8,9,10 };
	//进入行:0 1 2
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		//进入列0 1  2  3
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%2d", arr2[i][j]);
		//arr数组的第三行是两位数,所以在打印的时候会出现 每列 对不齐的情况。
	  //可以我们利用%2d,即按两位数的形式打印,如何位数不够,在前面补空格(右对齐)。%-2d是在后面补空格(左对齐)。
		}
		printf("\n");
	}
	return 0;

}

运行结果:
在这里插入图片描述
在这里插入图片描述图片演示理解:在这里插入图片描述注意:图片中二维数组arr的布局仅仅是我为了理解行和列画的图,该布局并不代表arr在内存中的存储形式。 二维数组在内存中到底是如何存放的见下文!!!

8.二维数组在内存中的存储

Q:打印二维数组中每个元素在内存中的地址值

代码演示:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++) 
		{
			printf("&arr[%d][%d] = %p\n", i,j, &arr[i][j]);
		}
	}
	return 0;
}

运行结果:
在这里插入图片描述
运行结果分析:
从运行结果中我们可以看到:二维数组在内存中的存储并不像我们理解行和列时所看到的那样呈矩阵排列,而是在上一行末尾与下一行开始之间仍然满足规律:后一个地址值 = 前一个地址值 + 4

前一个地址值 + 4 =后一个地址值
arr[0][0] 0079F760 + 4 =0079F764 arr[0][1]
arr[0][1] 0079F764 + 4 =0079F768 arr[0][2]
arr[0][2] 0079F768 + 4 =0079F76C arr[0][3]
arr[0][3] 0079F76C + 4 =0079F770 arr[1][0]
arr[1][3 ] 0079F77C + 4 =0079F780 arr[2][0]
arr[2][2] 0079F788 + 4 =0079F78C arr[2][3]

Q:二维数组在内存中是怎么存储的?

A:

  1. 二维数组在内存中依然是连续存放的
  2. 图片演示:
    在这里插入图片描述

Q:灵活应用二维数组在内存中的存储

A:
1.因为二维数组元素在内存中是连续存放的,所以知道数组的首元素地址,就能顺藤摸瓜找到后面的所有元素的地址,从而找到数组元素。
2.思考写代码实现二维数组中知道首元素地址顺藤摸过找到后面所有元素的地址,并打印在屏幕上。
代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	int arr[3][4] = { 1,2,3,4,5,6 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0][0]);
	int* p = &arr[0][0];

	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%p == %p\n", p +(4*i + j), &arr[i][j]);
		}
	}
	return 0;
}

运行结果如下:
在这里插入图片描述



好了,本篇到这就结束了,相信正在学C语言的你,通过本篇,已经会用数组存储数据,会使用数组,并了解了数组在内存中的具体布局。如有不对的地方,欢迎各位大佬来指正!
最后,小比特,大梦想,祝我们早日成为技术流!
在这里插入图片描述

我是一朵忽明忽暗的云,关注我,更多精彩等着你!我们一起进步!



  1. C99语法标准之前规定,数组的大小必须是常量表达式指定。但在C99语法标准中,引入了变长数组的概念。即在变长数组中,允许数组的大小用变量来指定,但同时规定变长数组不能初始化。我所使用的IDEvs2019,它的编译器MSVC虽然支持C99语法,但是不支持语法中的变长数组。 ↩︎

  2. 常识积累:
    符号 | 含义
    -------- | -----
    %d | 以整数形式打印
    %c | 以字符形式打印
    %s | 以字符串形式打印
    %p | 以地址形式打印 ↩︎

  3. 地址值是指针类型的变量。指针类型就是对数据类型申请的空间中存放的数据范围进行限定,限定该空间中只能存放地址值。计算机中用 数据类型* 表示指针类型。例如int*,char*,flout*,double*都是指针数据类型,他们向内存申请相应的空间存放地址值。 ↩︎

  4. 1.利用下标引用操作符,找到元素的下标,从而找到元素。
    2.利用取地址符号&,找到元素在内存中的地址值,即直接定位到存放元素的空间,从而找到元素。 ↩︎

  5. 如果把二维数组的每一行看做是一个一维数组,那么每一行的一维数组也有数组名。例如在二维数组arr[3][4]中,arr[0],arr[1],arr[2]分别是二维数组第一行,第二行,第三行的数组名,以此类推。 ↩︎

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值