数组与函数参数

本文详细讲解了数组与std::vector的区别,包括数组的灵活性与效率、复杂指针和数组类型的声明与理解,以及一维和二维数组在函数参数中的使用技巧。特别关注了初始化、赋值和标准库解决方案,以及常见误区和高效操作方法。
摘要由CSDN通过智能技术生成

声明时,下标优先级[]大于指针*。所以在声明一个指针指向一个数组时,应该加上小括号表明其指针属性。

一、数组和std::vector区别

内置数组是在连续空间中,存放同类对象的的一种数据结构,与std::vector的区别在于其灵活性,前者大小固定灵活性差,但是效率略高,后者大小动态调整,有对应的方法。

相同之处在于:

  • 都存放着同类对象
  • 每个成员都是无名的
  • 可以通过下标访问

因为数组本身是对象,因此可以定义数组的引用、数组的指针,该如何正确理解复杂的数组类型?

int *ptrs[10];//存放着10个int *的数组
int &ref[10]=/* ?*/;//不存在,因为10个int &数组要求元素占有空间
int (*Parray)[10]=&arr;//指向10个int的数组的指针
int (&arrRef)[10]=arr;//指向10个int的数组的引用
int * (&arry)[10]=ptrs;//10个int *的数组的引用

书中介绍了一种理解复杂指针声明的方法,从内到外,从右往左依次解释。从内到外指的是从小括号内部开始到外部,从右往左,从声明的右边开始,如int * (&arry)[10]=ptrs;从内往外,首先他是一个引用,从右往左,引用类型是大小为10的数组,数组的内容是int *

三、内置数组特点

  • 列表初始化只能用在初始化
  • 重新赋值请使用memset或者循环
  • std::array 允许列表初始化重新赋值
  • 作为函数参数只能是指针,长度信息可能需要额外参数(除非你处理上非常小心)
3.1 一维数组与函数参数

一维数组初始化、赋值和std::array解决的问题:

/****************一维数组************************/
//初始化
char ch[3] = { '1','2','3' };//char ch[3] { '1','2','3' } 元素间用逗号隔开,缺失部分用0补
//一个指针来读写这个数组
char* pChar = ch;//char * pChar=&ch[0]等价的
//一个只读指针来读这个数组
const char* pConstChar = ch;
//重新赋值
ch[0] = '4';
ch[1] = '5';
ch[2] = '6';
//如果是相同的值,可以用循环
for (int i = 0; i < 3; i++)
{
ch[i] = '0';
}
//也可以用memset
memset(ch, '0', sizeof(ch));

//但是不能再用初始化列表
//ch[3] = { '4','5','6' };

//你可以试试std::array,它可以这么初始化列表
std::array<char, 3> chArray = {'1','2','3'};//std::array<char, 3> chArray {'1','2','3'};
chArray={ '4','5','6' };

函数参数声明本质上只有一种,T fun(T * ptr,size_t size)

void printArr1(char * p,size_t n)//void printArr1(int p[], size_t n)
{
	for (size_t i = 0; i < n; i++)
	{
		putchar(p[i]);
	}
	std::cout << std::endl;
}

你也可声明一个数组的引用:

void printArr2(int (*RefArr)[10])
{
	for (int i = 0; i < 10; i++)
	{
		std::cout << RefArr[i] << std::endl;
	}
	std::cout << std::endl;
}
3.2 二维数组与函数参数

二维数组本质上是一维数组,每一个元素的内容仍为一维数组。

int arrTwo[3][4] = { {1,2,3,12} ,{4,5,6,11},{7,8,9,10}};//大括号内部为一个元素,元素间用逗号隔开,缺失部分用0补
//重新赋值
arrTwo[0][0] = 1;
arrTwo[0][1] = 2;
arrTwo[0][2] = 3;
arrTwo[0][3] = 4;
arrTwo[1][0] = 5;
arrTwo[1][1] = 6;
arrTwo[1][2] = 7;
arrTwo[1][3] = 8;
arrTwo[2][0] = 9;
arrTwo[2][1] = 10;
arrTwo[2][2] = 11;
arrTwo[2][3] = 12;
//同样不能再次用初始化列表重新给定值
//arrTwo[3][4] = { {1,2,3,12} ,{4,5,6,11},{7,8,9,10} };

//可以使用循环重新赋值
for (int i = 0; i < 3; i++)
{
	for (int j = 0; j < 4; j++)
	{
		arrTwo[i][j] = 33;//根据实际情况给值
	}
}
//二维数组在内存中也是连续分布的,所以可以使用memset
memset(arrTwo, 0, sizeof(arrTwo));
//其实二维数组是,一维数组的一个特例
//一维数组若其中的元素仍是数组,那么就是二维数组
//arrTwo[0] 一维数组的第一个,其内容为一维数组!

//下面这种指针是不能操作二维数组的
//因为他没有揭示出单个元素的类型:一维数组
//int* parrTwo = arrTwo;

//int* parrTwo[4] = arrTwo;//不正确,这个就是很多问题的根源,数组[]优先级高于指针*
int(*parrTwo)[4] = arrTwo;//[4]揭示了数组的结构

//为什么编译器一定要知道数组的结构?
//让二维数组指针的+1有意义
//你要是不知道结构,计算机如何找到下个元素(下个数组?)
int *prow1=arrTwo[0];//代表的是元素1,即第一个数组

//第一个数组第二个元素
prow1[1];

//直接一步到位
 *(*(arrTwo + 0) + 1);//arrTwo移动一个元素空间(数组空间4个int)取内容,内容为数组头指针
					  //移动头指针到第二个,取内容
//常用的其实是这个
 arrTwo[0][1];

比较难搞的其实是二维数组作为函数的处理材料:

void printArr2(int(* p)[4], size_t row, size_t col);
void printArr2_V1(int p[3][4], size_t row, size_t col);
void printArr2_V2(int p[][4], size_t row, size_t col);
void printArr2_V3(int* p, size_t row, size_t col);//传参时需要强制转换二维数组为一维数组
void printArr2_V4(int** p, size_t row, size_t col);//不建议用这个

注意了指针处声明列的个数是必须的,它提供指针加法移动的元素个数。如果你不打算将行列作为全局或者成员函数或固定行列信息,你需要额外提供行列信息,尽管你在声明处已经给定列的大小了(那是给编译器看的,不是给程序员看的!)

printArr2_V3实现:

void printArr2_V3(int* p, size_t row, size_t col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			std::cout << p[i*col+j] << " ";
		}
		std::cout << std::endl;
	}
}

对于printArr2_V4的实现:

int** p=(int **)malloc(3*sizeof(int *));
for (int i = 0; i < 3; i++)
{
	p[i] = arrTwo[i];
}
printArr2_V4(p, 3, 4);
for(int i=0;i<3;i++)
	delete p[i];

之所以不推荐二级指针作为参数操作二维数组,是因为以下几个原因:

  • 需要管理动态内存,容易出错
  • 还要使用循环告知每个数组的头指针

远远没有直接使用指向多个数组的指针来的舒服。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值