目录
前言:数组操作比较简单而作为及其常用的一种数据结构,除了初始化和销毁外,只可以进行存取和修改的操作,特此总结一下。
数组简介
数组数据结构特点
数组特点
1、数组内元素有一个共同类型
2、数组下标限定后则其维度和维界无法改变
3、数组操作简单,除创建和销毁外,数组只有存取和修改元素的操作。
4、数组所有元素占用内存中的连续空间
数组存储结构
数组占用连续空间,则一维数组相当于一个顺序存储的线性表,例如int arr[5],含有5个int类型的一维数组
对于一个二维数组,可以看成一维数组的一维数组,例如 int arr[2][3],包括两行三列,可以看成一个2个单元的数组,每个单元是一个3个int的数组。
数组指针
对于一维数组而言 folat p[5]; 则p为首地址即&p[0],p+1为&p[1] 但p=p+1 ; p++;这样的操作算错误,首地址一旦确定,无法赋值修改。
对于二维数组指针,则相对复杂一些。例如我们定义一个int a[2][3]的一个数组。
我们定义一维数组时int p[3],我们把p看作a[2],就方便理解了,因此a[0],a[1]就指向a[0][0],a[1][0]的首地址,继续套娃,a就是指向a[0]的地址,a+1指向a[1]首地址,因此它一加1就直接加载了一行所以行地址:a+i
而对于a[0],a[1]看待指针是按照列移动,因此列地址:*(a+i)=a[i] (对于*(a+i)是对行地址解引用),然后举一个代码例子
#include<iostream> using namespace std; int main() { int pt[2][3] = { 1,2,3,10,20,30 }; //普通方式取出元素和地址 for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { cout << pt[i][j] << "\t" << &pt[i][j] << "\t"; } cout << endl; } //行地址取出元素 for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { cout << *(pt[i] + j) << "\t" << pt[i]+j << "\t"; //*(*(a + i) + j) } cout << endl; } for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { cout << *(*(pt + i) + j) << "\t" << *(pt + i) + j << "\t"; } cout << endl; } return 0; }
运行结果:
数组在函数中的传递形式
举一个例子,数组作为形参传递,第一个输出12,第二个输出4,第三个4,原因是再传数组时,传递的其实时数组的首地址,指针的大小为4,因此数组作为形参本质上传递的是地址
#include<iostream>
using namespace std;
void ShowSize(int a[3])
{
cout << sizeof(a) << endl;
}
void ShowSize1(int* a)
{
cout << sizeof(a) << endl;
}
int main()
{
int pt[3] = { 1,2,3 };
cout << sizeof(pt) << endl;//12
ShowSize(pt);//4
ShowSize1(pt);//4
return 0;
}
函数返回数组指针
数组指针和指针数组
数组指针:指向数组的指针,(*p)[n]
指针数组:指针构成的一个数组,*p[n]
需要明确一个优先级顺序:()>[]>*,所以:
(*p)[n]:根据优先级,先看括号内,则p是一个指针,这个指针指向一个一维数组,数组长度为n,这是“数组的指针”,即数组指针;
*p[n]:根据优先级,先看[],则p是一个数组,再结合*,这个数组的元素是指针类型,共n个元素,这是“指针的数组”,即指针数组。
数组指针代码案例:
#include<iostream>
using namespace std;
//一维数组指针测试
void test01()
{
//一个一维数组
int a[5] = { 1,2,3,4,5 };
//数组指针
int(*p)[5];
p= &a;//赋值a的地址,*p=a
cout <<"a:" <<a << endl;//输出首元素地址
cout << "p:" << p << endl;//p表示数组地址,输出首元素地址
cout << "*p:" << *p << endl;//解引用为a
cout << "&a[0]:" << &a[0] << endl;//&a[0]地址
cout << "p[0]:" << p[0] << endl;//数组首元素地址
cout << "&a[1]:" << &a[1] << endl;//a[1]地址
cout << "p[1]:" << p[1] << endl; //p[1]地址并不和&a[1]相等
cout << sizeof(p) << endl;//p指针 4
cout<<sizeof(*p) << endl;//*p=a 20
cout << sizeof(p[1]) << endl;//20
//输出函数内容
for (int i = 0; i < 5; i++)
{
cout << (*p)[i] << " ";
//cout << *(*p + i) << endl; 两种一样可以输出 把*p看成a
}
}
void test02()
{
//指针数组,一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2],所以要分别赋值
int (*pp)[3];
int c[2][3] = { 1,2,3,4,5,6 };
pp = &c[0];
//将二维数组赋给指针数组
int* p[3]; //一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2],所以要分别赋值
int m[3][4];
for (int i = 0; i < 3; i++)
p[i] = m[i];//m[i]行地址储存3个变量
}
int main()
{
test01();
test02();
return 0;
}
指针数组案例:
void test03()
{
int a = 1, b = 2;
int*p[2];
p[0] = &a;
p[1] = &b;
cout << a << "\t"<< & a << endl;//a a地址
cout << *p[0] << "\t" << p[0] << endl;//a a地址
cout << b << "\t"<<&b<<endl;//b b地址
cout << *p[1] << "\t" << p[1] << endl; //b b地址
cout << p << endl;//与a不相等
}
函数返回数组指针
想清楚上面细节后,接下来函数返回数组指针
1、可以给数组起别名,后声明函数。
方式a:typedef int arr[5];
方式b:using arr=int [5];
2、可以定义为 类型说明符 (*函数名(参数表))[数组维度]
3、C++11标准中 可以 auto 函数(参数表)->(*)[数组维度]
decltype关键字可以返回声明对象的类型,因此比较方便
//例如构建一个float[5]类型的数组指针函数为Func,方式1
typedef float arr[5];
//或者using arr=float[5]
arr* Func(int x)
{
float a[5]={1,2,3,4,5}
return &a;
}
方式2,
1、Func为函数,需要一个int x作为参数,
2、(*Func(int x))表示对函数结果进行取地址
3、(*Func(int x))[5]表示返回一个大小为5的数组
4、int放在最前面,限定返回数组的类型
int(*Func(int x))[5]
{
int a[5] = {1,2,3,4,5};
return &a;
}
方式3,尾置返回类型
auto Func(int x)->int(*)[5]
{
int a[5] = {1,2,3,4,5};
return &a;
}
方式4,decltype
int a[] = { 0,1,2,3,4 };
int b[] = { 5,6,7,8,9 };
decltype(a)* Func(int x)
{
return (x % 2) ? &a : &b;
}
返回后输出数组
#include<iostream>
using namespace std;
auto Func(int x)->int(*)[5]
{
static int a[5] = {1,2,3,4,5};
return &a;
}
int main()
{
int(*p)[5] ;
//首地址
p = Func(2);
for (int i = 0; i < 5; i++)
{
cout <<"地址为:"<< * p + i<<" 值为:"<<*(*p+i) << endl;
}
return 0;
}
把数组设置为静态变量后就不会因为局部变量的原因无法返回,如果没有static,那么数组a在函数返回a就不存在,生命期结束,每个内存单元永远都在,只不过数据不再受保护,将无法输出,输出结果
但加入static后