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

C语言数组详解

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

C语言数组详解

C语言数组详解
  • 一.一维数组
    • 1.定义数组:
    • 2.数组的集成初始化:
    • 3.数组的不完全初始化:
    • 4.数组之间的赋值:
    • 5.数组的遍历输出
    • 6.一维数组在内存中的存储
  • 二.二维数组
    • 1.二维数组的创建:
    • 2.二维数组的初始化:
    • 3.二维数组的遍历:
    • 4.二维数组在内存中的存储:
  • 三.数组名,数组首元素地址和数组地址之间的三角关系
    • 1.数组名什么时候表示数组地址:
    • 2.数组名什么时候表示数组首元素地址:
    • 3.数组地址和数组首元素地址之间的关系和区别:

一.一维数组 1.定义数组:

数组是一组相同类型元素的集合

格式:<类型> 变量名称[元素数量];

注:

  • 在定义数组时必须给出元素数量,如果没有给出元素数量,则必须进行初始化。

  • 元素数量必须是整数,数组一旦创建,就不能改变大小:

int a[100]

元素数量为整数100,该数组创建后,不可改变它的大小。

数组的每个单元就是一个变量,用来存放元素。

对于数组的使用我们之前介绍了一个操作符: [] ,下标引用操作符,它其实就是数组访问的操作符。数组是使用下标来访问的,下标从0开始。

//定义一个数组
int arr[10];
//访问数组中下标为0的元素,即访问第一个元素
arr[0];

注:

C99之前:元素数量必须是编译时确定的字面量,C99开始我们已经可以用变量来定义数组的大小了,即C99支持变长数组,这要求编译器支持C99标准

int n =100;
int grade[n];

注: 变长数组不能被初始化

数组的下标必须是整数,那么字符可以做下标吗?比如:

int a[255];
a['A'] = 1;

这样的代码可行吗?为什么?

可以,因为字符有对应的ASCII码。

2.数组的集成初始化:

即用大括号一次性给数组中的所有元素赋值:

int a[3] = {1,2,3} 

数组在创建的时候可以不指定数组元素的个数就初始化,编译器会自己计算初始化元素的个数进而判断出数组的大小,但是对于下面的代码要注意区分内存是如何分配的:

char arr1[] = "abc";
char arr2[] = {'a','b','c'};

用字符串常量初始化数组arr1,由于字符串常量后面有一个隐藏的’’,因此数组arr1的大小是4字节,而数组arr2是典型的集成初始化,数组大小是3字节,尽管二者都没写数组的大小,但是编译器会根据初始化时元素的个数自动判断出数组的大小。

集成初始化时的定位:

int a[10] = {
	[0] =2,[2] = 3,6,
};//表示a[0] =2,a[2]=3,a[3]=6,其余元素的值都为零

用[n]在初始化数据中给出定位,没有定位的数据接在前面的位置后面,其它位置的值补零,也可以不给出数组大小,让编译器去算,这种初始化方式特别适合初始数据稀疏的数组,仅支持C99。

3.数组的不完全初始化:
int a[13] = {2};//第一个元素为2,其它所有元素为0

int count[number] = {0};
//这段代码与下面这段代码效果相同,都是将数组中所有元素初始化为零
for(i=0;i
	count[i] = 0;
}

注: 创建数组尽量初始化,这样数组是可控的。

4.数组之间的赋值:

你不能直接把一个数组赋值给另一个数组,即数组变量本身不能赋值,要把一个数组的所有元素交给另一个数组,必须采用遍历的方式赋值

int a[] = {1,2,3,4,5,6,7};
int b[] = a;//这是错误的

for(i=0;i
	b[i] = a[i];
}//这是唯一把一个数组赋值给另一个数组的方法
5.数组的遍历输出

一个个的打印数组的元素称为数组的遍历输出

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int sz = sizeof(arr)/sizeof(arr[0]);//获取数组的大小
//输出数组的内容
for(i=0; i
	printf("%d ", arr[i]);
}
6.一维数组在内存中的存储

接下来我们探讨数组在内存中的存储。 看代码:

#include 
int main()
{
	int arr[10] = {0};
	int i = 0;
	for(i=0; i
		printf("&arr[%d] = %pn", i, &arr[i]);
	}
	return 0;
}

输出结果如下:

仔细观察输出的结果,我们可以发现数组中的每个元素相差4字节,而该数组每个元素的大小都是4字节。 由此可以得出结论:数组中的元素在内存中是连续依次存放的,随着下标的增大,地址由低到高变化。

二.二维数组 1.二维数组的创建:
int a[3][5];//通常理解为a是一个3行5列的矩阵
2.二维数组的初始化:
  • 列数是必须给出的,因为列数一旦省略,编译器就无法判断出一行有多少个元素。
  • 行数可以省略,但是省略后必须初始化,由编译器根据初始化内容来判断有多少行
  • 每行一个{},逗号分隔
  • 最后的逗号可以存在,有古老的传统
int a[][5] = {
	{0,1,2,3,4},
	{2,3,4,5,6},
};
int a[][5] = {0,1,2,3,4,2,3,4,5,6}

这也是可以的,二维数组在内存中的排列和一维数组是相同的,可以将其想象为一个矩阵从左上角到右下角用你给出的数字逐行的填满。

二维数组的不完全初始化:

int a[][3] = {{0},{1},{2}};//即下面这个初始化

int a[][3] = {
	{0},//表示第一行第一列赋值为0,第一行第二列,第三列值均为0
	{1},//表示第二行第一列赋值为1,第二行第二列,第三列值均为0
	{2},//同上
};//3行3列的矩阵,

二维数组初始化为0:

int arr[3][4] = {0};
3.二维数组的遍历:
for(i=0;i<3;i++){//遍历行号
	for(j=0;j<5;j++){//遍历列号
		a[i][j] = i*j;//a[i][j]表示第i行第j列上的单元
	}
}//a[i,j],这里的逗号是运算符,计算的结果是j,所以a[i,j]==a[j],这不是一种正确的表达方式
#include 
int main()
{
	int arr[3][4] = {0};
	int i = 0;
	int j = 0;
	
	for(i=0; i<3; i++)//遍历行号
	{
		for(j=0; j<4; j++)//遍历列号
		{
			arr[i][j] = i*4+j;//通过遍历给二维数组赋值
		}
	}

	//遍历输出
	for(i=0; i<3; i++)
	{
		for(j=0; j<4; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("n");//遍历完一行后换行
	}
	
	return 0;
}
4.二维数组在内存中的存储:

像一维数组一样,这里我们尝试打印二维数组的每个元素:

#include 
int main()
{
	int arr[3][4];
	int i = 0;
	int j = 0;
	
	for(i=0; i<3; i++)
	{
		for(j=0; j<4; j++)
		{
			printf("&arr[%d][%d] = %pn", i, j,&arr[i][j]);
		}
	}
	
	return 0;
}

输出结果如下:

通过结果我们可以分析到,其实二维数组中的元素在内存中也是连续存储的。

二维数组也可以看作是一维数组,即它的每一行都是一个一维数组,可以把第一行数组的数组名看作是arr[0],第二行数组的数组名看作是arr[1],以此类推…

三.数组名,数组首元素地址和数组地址之间的三角关系

灵魂三问:

  • 数组名什么时候表示数组地址?
  • 数组名什么时候表示数组首元素地址?
  • 数组地址和数组首元素地址有什么关系,它们之间有什么区别?
1.数组名什么时候表示数组地址:

数组名只有单独放在sizeof内部以及放在&后才代表整个数组的地址。

由于sizeof里的数组名a表示整个数组的地址,故sizeof(a)求出整个数组的大小。

以一维数组为例:

int a[] = {1,2,3,4,5,6,7,8};
sizeof(a);//sizeof给出整个数组所占据的内存的大小,单位是字节
sizeof(a[0]);//给出数组中单个元素的大小
sizeof(a)/sizeof(a[0])//得到数组一共有多少个元素

以二维数组为例:

int arr[3][4] = {0};
sizeof(arr)//这表示整个二维数组的大小
2.数组名什么时候表示数组首元素地址:

除了上述那两种情况,数组名都表示数组首元素地址。

以一维数组为例:

以二维数组为例:

如图所示:输出数组名的结果和输出数组首元素地址的结果相同,因此通常情况下数组名表示数组首元素地址。

值得我们注意的是,对于一维数组而言首元素地址就是第一个元素的地址,而对于二维数组而言:

二维数组的首元素就是第0行数组,所以二维数组的首元素地址就是第0行数组的地址。

如果用指针变量p来保存二维数组的地址:

arr[3][5] = {0);
int(*p)[5] = arr

p保存的是二维数组首元素的地址,即第0行数组的地址,因为指针变量p是int[5]类型的,所以p+1相当于跳过一个int[5],也就是p+1直接跳过第0行数组,指向第1行数组。

那么p+i就是指向第i行数组,相当于第i行的数组地址,*(p+i) 相当于了第i行的数组,第i行数组可以看作第i行的数组名,通常情况下数组名第i行数组名表示第i行数组首元素的地址,所以 *(p+i)也是第i行第一个元素的地址。

为了加深对上述概念的理解,我们可以看看下面这段代码:

int arr[3][4] = {0};
sizeof(arr[0]);

这里的sizeof(a[0])计算的是什么呢?

a[0]表示数组首元素,对于二维数组而言,首元素就是第0行数组,故这里的a[0]表示第0行数组,第0行数组又可以看作第0行数组的数组名,此时属于数组名单独放在sizeof里面的情况,故sizeof(a[0])表示第0行数组的大小,那么最终的值就是16。

我们再看看下面这段代码:

int arr[3][4] = {0};
sizeof(arr[0]+1);

a[0]表示第0行数组,也就是第0行的数组名,但是由于他不是单独放在sizeof内部,也不是放在&后面,故它表示的就是第0行数组首元素的地址,故arr[0]+1表示第0行数组第二个元素的地址,故sizeof(arr[0]+1)算出的结果是4/8。

我们最后再来看一段代码:

int arr[3][4] = {0};
sizeof(*(arr+1));

arr表示二维数组首元素的地址,即第0行数组的地址,那么arr+1就是第1行数组的地址,*(arr+1)表示第1行数组,第1行数组又可以看作第1行数组名,而此时它又是单独放在sizeof内部的,故sizeof( *(a+1))就表示第1行数组的大小,结果为16。

3.数组地址和数组首元素地址之间的关系和区别:

关系:

数组名既可以表示数组首元素的地址,也可以表示数组的地址,两个在数值上是一样的,但是含义不一样。

区别:

当数组名arr表示数组地址时,arr+1直接跳过一个数组的大小

如图所示,首先输出数组arr的地址,然后输出arr+1,二者之间刚好相差0x28,也就是相差十进制的40字节,即相差一个数组的大小。

当数组名arr表示首元素的地址时,arr+1表示下一个元素的地址,即第二个元素的地址。

如图所示输出arr+1的结果和输出&arr[1] (第二个元素的地址)是相同的,因此arr+1就表示第二个元素的地址。

故arr表示首元素(arr[0)]地址,那么arr+1就表示第二个元素(arr[1])的地址,以此类推,arr+2表示第三个元素(arr[2])的地址,arr+i表示arr[i]的地址。

因为数组是int类型的,也就是说数组中的每个元素都是int类型的,也就是说每个元素都是4字节大小,那么arr+1(第二个元素的地址)实际上比arr增加了4字节。

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

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

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