【C语言】数组的超详细解答,走过路过别错过-创新互联

在这里插入图片描述

创新互联长期为成百上千家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为四方台企业提供专业的做网站、成都网站制作四方台网站改版等技术服务。拥有十年丰富建站经验和众多成功案例,为您定制开发。文章目录
  • 前言
  • 数组
    • 一、一维数组
      • 1.1、初始化一维数组
      • 1.2、一维数组元素赋值
      • 1.3、一维数组传参
    • 二、二维数组
      • 2.1、二维数组的声明和初始化
      • 2.2、二维数组传参
      • 2.3、高维数组
    • 三、数组与指针
      • 3.1、指针简介
      • 3.2、数组名的含义
      • 3.3、指针访问数组
        • 指针访问数组元素
        • 指针访问整个数组


前言

在编程的道路上,我们处理的问题越发复杂,数据越发庞大,为了解决庞大的数据,学习数组便有很重要的意义。


数组

何为数组?

数组:数据类型相同的一系列元素组成的集合。
数组的下标有0开始

了解它的定义,就要开始学习如何使用它。


一、一维数组

一维数组是我们平时使用最多的数组
char str[23];
float candy[44];
int code[50];
以上均为一维数组 - 数组名后面仅有一个[ ]

1.1、初始化一维数组

我们将一维数组分为4部分:如 int arr[30] = {1,2,3};

  1. int - 数组内元素的类型
  2. arr - 数组名
  3. [30] - 数组内元素个数
  4. {1,2,3} - 数组元素的初始化

初始化数组,则要注意第3和第4点


  1. 数组元素个数的初始化
#include#define MAXN 20
int main(void)
{int n = 10;
	//数组元素个数的初始化
	int a1[4];              //YES
	int a2[5*3+1];          //YES
	int a3[sizeof(int)+4];  //YES
	int a4[MAXN];           //YES 
	int a5[-3];             //NO
	int a6[0];              //NO
	int a7[2.5];            //NO
	int a8[(int)2.5];       //YES
	int a9[n];              //c99之前的标准不允许
	int a10[] = {1,3,5};    //YES,元素个数为3
	return 0;
}

总结:[ ]内可以是常量和常量表达式(但结果应为正整数),符号常量(#define定义的);
至于变量需要编译器支持c99标准,为了代码的兼容性,因此即使你的编译器支持也最好别用。
也可以[ ]内为空,有后面的初始化赋值来决定元素个数


  1. 初始化赋值
#include#define MAXN 20
int main(void)
{//初始化时后面用 {},元素之间用逗号隔开 
	int arr1[] = {1,3,4,5 }; //元素个数为4
	int arr2[3] = {1,3,4,5 };//初始化列表中的项数多于数组元素个数,错误
	int arr3[2] = {1,3.4 };   //3.4为double类型,赋给int类型的元素会使数据被截断
	int arr4[3];              //未赋值,数组内元素为随机值
	int arr5[30] = {1 };     //第一个元素被赋值为了1,
	                         //后面29个元素被编译器自动赋值为0
	return 0;
}

总结:

  1. 当初始化列表中的项数少于数组元素时(上面最后一种情况),后面的元素值自动赋为0(字符数组是 \0 ;根据ASCII码表,\0 字符对应的值是 0,所以对于字符数组,也可以说后面的元素被赋值为0)
  2. 赋的值应当于元素的类型相对应(上面的第3种情况),虽然不会报错,但数据不准
  3. [ ]内为空时,编译器会自动匹配数组大小和初始化列表中的项数(上面第1种情况)

还有一种常用的初始化方法 - 用memset进行初始化

int arr[10];
memset(arr,0,sizeof(arr));
对于其作用机制,读者可自行查阅


字符数组进行赋值时

//以下情况允许
	char arr1[] = {'a','b','c','d' };
	char arr2[] = "aasdf";
	char arr3[] = {"acbhd" };
	//以下情况不允许
	char arr4[] = 'a';
	// 总结: 字符串可以不带括号
	//        但单个字符赋值时必须要{}

  1. **c99新特性 - 指定初始化器//

有一种情况:int arr[10],你只想将第6个元素初始化为6,其余为0,怎么写?
int arr[10] = {0,0,0,0,0,6};
c99可以这样写
int arr[10] = {[5] = 6};
而且
int arr[10] = { 1,2,[5] = 6,7,8};
arr内的每个元素的值为
1 2 0 0 0 6 7 8 0 0
在 指定初始化器后面的值 会接着在 初始化器后面 赋值


1.2、一维数组元素赋值

声明数组后,便可以借助数组下标对数组元素进行赋值

一般给数组赋值
常用循环

#include#define MAXN 20
int main(void)
{int arr[MAXN];
	for (int i = 0; i< MAXN; i++)
	{arr[i] = i;
	}
	return 0;
}

下面进行一些错误示范

#include#define MAXN 20
int main(void)
{int a1[MAXN] = {5,3,5,7 };
	int a2[MAXN];
	a1 = a2;             //错误
	a2[MAXN] = a1[MAXN]; //数组下标越界
	a2[3] = {5,3,4,2 }; //无作用,错误
	return 0;
}

1.3、一维数组传参
int a[10] = {1,2,3,4,5,6,7,8,9,10};
void nb1(int a[]) //[]内数字可省略
void nb2(int* pa) //用指针接受,至于为什么,后面的数组名含义有解释

二、二维数组

在实际应用中,一维数组并不够用,我们需要更高维的数组
列如:我想存储一年的天气情况,查询第m月第n天的天气
如果采用一维数组,那我需要先要算m月n日 距1月1日的天数,在带入下标,这并符合我们的习惯
难道不能像平面直角坐标系一样,通过 横坐标 和 纵坐标 来访问,而这就要用到二维数组


2.1、二维数组的声明和初始化

int arr[row][col];前一个[]为行,后一个[]为列

#includeint main(void)
{//以下为正确形式
	int a0[3][4];
	int a1[3][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6} };
	int a2[][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6} };
	int a3[][4] = {1,2,3,4,5,6,7,8,9,10 };//不足的补0
	//以下为错误形式
	int a4[][] = {{1,2,3,4},{2,3,4,5},{3,4,5,6} };            //不能省略列
	int a5[3][3] = {{1,2,3,4},{2,3,4,5},{3,4,5,6},{4,5,6,7} };//行列均超过限定数目,错误

	return 0;
}

总结:

  1. 若不进行初始化,行列均不能省
  2. 若进行初始化,可以省略行,但不能省略列(如a2,a3)
  3. 可以在{ }内再使用{ }进行归类,也可以不使用,编译器可以自动根据 列 的数量来归类(如a3)
  4. 行列应该后面列表的项数对应,如(a5)

2.2、二维数组传参
int a1[3][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6} };
void nb1(int a1[][4]);//不能省略列数
void nb2(int (*pa)[5]);//形参是数组指针,后面指针访问数组有讲。

2.3、高维数组

既然都有了有二维数组,自然少不了三维数组,四位数组;
如 int b[10][10][10];
可以这样理解:
一维数组 - 一行数据
二维数组 - 数据表
三维数组 - 一叠数据表
对于初学者,一维和二维数组已经够用,不建议继续研究更高维。

三、数组与指针3.1、指针简介

指针?什么是指针?本质来看指针是一个存储内存地址的变量
如int类型存储的是整数,int*(指针)则存储的是地址;
要理解指针需要从以下4个方面

  1. 指针的类型
  2. 指针所指向的类型
  3. 指针的值或者叫指针所指向的内存区
  4. 指针本身所占据的内存区

首先了解指针的声明

指针的声明如下:
int a = 10;//a占4字节
int* pa = &a;//拿到的是a的4个字节中的第一个字节的地址
指针大小:32位为4字节,64位为8字节
以上涉及两个操作符符

  1. &
    & 只有一个操作数 表示 取地址操作符
    & 有两个操作数时 表示 按位与操作符
  2. 访问a变量则通过解引用操作符(*)
    如 *p = 20; 则a变量存储的值变为20;

使用指针,最重要的是理解指针的类型的意义

指针类型的意义

  1. 指针类型决定了解引用的权限有多大
  2. 指针类型决定了指针走一步走多远。int类型+1,4字节,char类型+1,1字节
int main(void)
{//内存观察时-1
	int a = 0x11223344;//一个十六进制等于4个二进制,所以11223344刚好站4字节
	int* pa = &a;
	*pa = 0;//4个字节内存都变成了0
	//a变为 0x00000000
	int b = 0x11223344;
	char* pc = &b;
	*pc = 0;//1个字节变成了0
	//b变为 0x11223300
	return 0;
}

至于b为什么不是变为 0x00223344,这涉及 大小端存储 感兴趣的读者可自行查阅

现在我们以经初步了解指针,但指针作为c语言最难的一部分,主要体现在指针与其他知识的结合,而下面我们将介绍指针与数组的结合


3.2、数组名的含义

int a[10];
a为该数组的数组名
数组名在大多数情况下表示首元素的地址
在这里插入图片描述
a 等价于 &a[0]


但有两个例外

#includeint main(void)
{int a[10];
	// 例外1
	sizeof(a); //此时数组名表示整个数组,计算整个数组的大小
	// 例外2
	&a;        //此时数组名表示整个数组,取出整个数组的地址
	return 0;
}

对于特例1,很好解释,只要看看结果就行。
在这里插入图片描述
对与特例2,我们通过 调试窗口 中的 监视窗口 来看
在这里插入图片描述
看图可知:
a表示的地址和&a表示的地址都一样
结果均为:0x0000000f9c0ffb58
这并不能看出二者的区别,但通过a+1,&a+1可看出
a+1 相比 a 的地址增加了 4
&a+1 相比 &a+1 的地址增加了 40
也就是说,a+1仅仅相当于跳过了一个元素,&a+1则跳过了一个数组
故 &a 取出的是一个数组的地址


3.3、指针访问数组指针访问数组元素
int main(void)
{int arr[] = {1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	int i = 0;
	for (i = 0; i< 10; i++)
	{printf("%p = %p\n", &arr[i], p + i);
	}
	return 0;
}

在这里插入图片描述


从上述代码,我们不难发现 arr[i]的地址与 p+i指针内存储地址 相同,则 *(p+i) 的值为 arr[i];
这表明指针和数组的关系十分密切,事实上,c语言标准在描述数组表示法时也用到了指针

arr[i] 本质上是 *(arr+i)

注意:*(arr+i) != *arr+i *的优先级要高于运算符
前者是 arr[i]
后者是 arr[0] + i

编译器会自动将 arr[i] 转换为 *(arr+i) 而这就为数组描述法增添了其他几种写法

int main(void)
{int arr[] = {1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	//其一
	arr[4]  ==  *(arr+4) == 4[arr];
	//其二
	arr[4]  == *(p + 4)  == p[4] == 4[p];
	return 0;
}

指针访问整个数组

有一种指针类型叫 数组指针,顾名思义:数组指针指向的是整个数组

int main(void)
{int arr[10] = {1,2,3,4,5,6 };
	//数组指针的创建
	//错误
	int *parr[10] = arr;//*parr先与[]结合,arr不是数组地址
	//正确
	int(*parr)[10] = &arr;
	//指针数组的声明
	double* d[5];         //指针数组,共5个元素,每个元素类型为double*
	double* (*pd)[5] = &d;//数组指针,指针pd 指向一个 指针数组d
	//要点:类型与数组类型一样,括号内为指针
	return 0;
}

注意指针数组的声明格式:
如上double* (*pd)[5] = &d;

  1. 类型一致,如上double* 对应 double*
  2. ()不能少
  3. & 不能少,若去掉则等号右边表示为首元素地址

为防止将数组指针与指针数组用混,看下方解释:

  1. int arr[5];//整型数组
  2. int* parr1[10];//整型指针数组
  3. int(*parr2)[10];//整型数组指针,该指针指向一个数组,数组10个元素,每个元素的类型是int类型
  4. int(* parr3[10])[5];//首先 parr3[10]是数组,去掉,剩int(*__)[5],为数组指针,所以parr3是一个存储数组指针的数组,该数组能够存放10个数组指针,每个数组指针能够指向一个数组,数组5个元素,每个元素是int类型

证明 数组指针真的指向整个数组,看下列代码:

int main(void)
{int arr[10] = {0 };
	int* p1 = arr;
	int(*p2)[10] = &arr;
	printf("%p\n", p1);
	printf("%p\n", p1+1);//跳了4,即跳了一个数组元素

	printf("%p\n", p2);
	printf("%p\n", p2+1);//跳了40,即跳了一个数组
	return 0;
}

那么数组指针有什么用处呢?
首先数组指针指向的是整个数组,那么用在一维数组上肯定没啥用,它一般用在高维数组

#includevoid print1(int arr[][5], int r, int c)
{int i, j;
	for (i = 0; i< r; i++)
	{for (j = 0; j< c; j++)
		{	printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
void print2(int(*pa)[5], int r, int c)
{int i, j;
	for (i = 0; i< r; i++)
	{for (j = 0; j< c; j++)
		{	printf("%d ", *(*(pa + i)+j));
			//*(pa + i)为arr[i]数组,不是元素,是数组,而像一维数组 *(p+i) 为 一个数组元素
		}
		printf("\n");
	}
}
int main(void)
{int arr[3][5] = {{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	//print1(arr, 3, 5);
	print2(arr, 3, 5);//arr数组名表示首元素地址,就是第一行元素
	return 0;
}

其实读者不难发现,这数组指针好像也没啥用,反倒还将代码变得更复杂了
事实上并不是,而是如今学的太少,还轮不到去使用它,日后可能某些问题就非它不可了


在这里插入图片描述

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


新闻名称:【C语言】数组的超详细解答,走过路过别错过-创新互联
当前地址:http://hbruida.cn/article/dcdhis.html