2.3.1 数组——二维数组
题目2. 二维数组中的查找
- 二维数组的声明: 数据类型 数组名 [行数][列数]
- 二维数组的初始化:
① int array1[3][2]={4,2,5,6};//顺序初始化:按照先从左向右再由上而下地初始化,即第一行所有元素都初始化好以后再对第二行初始化。
② int array2[3][2]={{4,2},{5},{6}};//按行初始化:用一对大括号来表示每一行,跳过前一行没有初始化的元素,在行内从左向右地进行初始化。对于没有初始化的元素,则都是一个不确定的值。
题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
注意:C++规定,在声明和初始化一个二维数组时,只有第一维(行数)可以省略。比如:int array[][3]={1,2,3,4,5,6};相当于:int array[2][3]={1,2,3,4,5,6};
- 向函数传递二维数组:
① 以二维形式传递:
#include <iostream>
using namespace std;
bool compare(int a[][4],int nums,int rows,int colomns,int x)
{...}
int main()
{
int colomns = 4,rows = 4;
int a[4][4] = {1,2,8,9,2,4,9,12,4,7,10,13,6,8,11,15};
int x;
cin>>x;
cout<<compare(a,rows*colomns,rows,colomns,x)<<endl;
return 0;
}
② 以一维数组的形式传递:
bool compare(int *a,int nums,int rows,int colomns,int x)
{...}
int main()
{
int colomns = 4,rows = 4;
int a[4][4] = {1,2,8,9,2,4,9,12,4,7,10,13,6,8,11,15};
int x;
cin>>x;
cout<<compare(*a,rows*colomns,rows,colomns,x)<<endl;
return 0;
}
总结一下:
二维数组 :a[4][4],其长度为64B,a[0] = *a,a[1] = *a +1...它们的内容都是地址,都是这一行的地址,其长度为16个字节。**a = a[0][0],所占字节数为4(int型时),&(**a) == a[0]。
写到这,我们还有一个很重要的问题需要解决,那就是如何将二维数组从函数返回给主函数?
刚的方法是传入了一个二维数组,我们可以通过两种方法对其进行接收,通过同一内存区域的修改,以达到访问的效果。但如果我们是在调用的函数内生成了一个二维数组应该如何返回呢?因为调用函数的声明周期有限的,回想之前的博客写过关于以为数组的情况,写过这么一句话:“C++ 不允许返回一个完整的数组作为函数的参数。但是,您可以通过指定不带索引的数组名来返回一个指向数组的指针”,就比如说:
#include <iostream>
using namespace std;
int* function(int x)
{
static int a[3] = {0,1,2}; //可以这么写是因为a[3],如果是个变量就没办法使用static了
for(int i = 0;i < x;i++)
{
cout << a[i]<<"\t";
}
return a;
}
int main()
{
int x = 3;
int* a1 = function(x);
for(int i = 0;i < x;i++)
{
cout << a1[i]<<"\t";
}
return 0;
}
其中使用了static关键字,就是为了避免function函数调用结束后释放,即使返回了地址,但是那片内存已经不知道归属于谁了。
当然还有第二种方法:就是使用动态内存分配,这个是参照了博客:https://www.cnblogs.com/walter-xh/p/6192800.html里最后提到的内容,学到的。这里涉及到new和delete,这里有篇比较好的文章,想了解的可以去看一下:https://blog.csdn.net/codedoctor/article/details/76187567(这篇文章很赞鸭,讲的深入又通俗易懂)。我就言简意赅一些,我们来梳理一下new是做什么的,然后它从哪里申请变量,大概就可以清楚,为什么普通的变量会被释放,而它却可以活得漫长。
- new:
首先我们要知道关于new做的几件事:
1. new操作申请的内存是堆中的,而普通变量是栈中,声明周期因此不同;
2. 可能会调用构造函数,当然这取决于你使用的类型是否有构造函数如果常见的是简单变量,这步也可能省略。
3. 返回正确的指针
- delete:
堆中申请的这部分变量是需要程序员手动回收的,不然它就和主函数耗着。。。那么delete做了什么呢?
1. 定位到指针所指向的内存空间,然后根据其类型,调用其自带的析构函数(内置类型不用);
2. 然后释放其内存空间(将这块内存空间标志为可用,然后还给操作系统);
3. 将指针标记为无效(指向NULL)。
#include <iostream>
using namespace std;
char* replace(char* str1,const int num,int i)
{
// static char str2[num]; //static 定义的数组中索引值一定是一个常数,不能是符号,即使定义为const 的常量也不行
char* str2 = new char [num];
int j;
for(j = 0;j < i;j++)
{
str2[j] = str1[j];
}
str2[j] = '%';
str2[++j] = '2';
str2[++j] = '3';
for(i++,j++;j < num;j++,i++)
{
str2[j] = str1[i];
}
return str2;
}
int main()
{
/*定义一个字符串,之后改为输入一个字符串*/
char* str1 = "Hello world !";
int num = 0,i = 0; //num为字符串不包含'\0'的长度,'\0'==NULL?
while(str1[i] != NULL)
{
i++;
num++;
}
/*找到替换位置i,并调用函数进行替换*/
i = 0;
while(i != num)
{
if(str1[i] == ' ')
{
num+=4; //除增加的3个字符外,还需要增加'\0'字符串的结束标志
str1 = replace(str1,num,i);
i+=3;
}
i++;
}
cout<<str1<<endl;
return 0;
}
#include <iostream>
using namespace std;
int* function(int x)
{
/*创建一个对象*/
int* a = new int [3];
for(int i = 0;i < 3;i++)
{
a[i] = i;
}
for(int i = 0;i < x;i++)
{
cout << a[i]<<"\t";
}
cout<<endl;
return a;
}
int main()
{
int x = 3;
int* a1 = function(x);
for(int i = 0;i < x;i++)
{
cout << a1[i]<<"\t";
}
cout<<endl;
/*删除a1对象*/
delete a1;
for(int i = 0;i < x;i++)
{
cout << a1[i]<<"\t"; /*删除后,即使就不知道程序内的数据是个什么东东了*/
}
return 0;
}
ps:这里我选择使用 int* a = new int [3],而非 int* a = new int(3); 这里涉及到指针和数组的区别。可以参考博客里的例子去自己动手看:https://www.cnblogs.com/yuzhuwei/p/4173374.html。
那么如何返回一个二维数组?
类比于上面解决办法也可以通过 ① static关键字来实现:
#include <iostream>
using namespace std;
int* function(int x)
{
/*创建一个对象*/
static int a[3][3];
for(int i = 0;i < 3;i++)
{
for(int j = 0;j < 3;j++)
{
a[i][j] = j;
}
}
for(int i = 0;i < x;i++)
{
for(int j = 0;j < 3;j++)
{
cout << a[i][j]<<"\t";
}
cout<<endl;
}
cout<<endl;
return *a;
}
int main()
{
int x = 3;
int* a1 = function(x);
for(int i = 0;i < x;i++)
{
for(int j = 0;j < 3;j++)
{
cout << a1[i*x + j]<<"\t";
}
cout<<endl;
}
cout<<endl;
return 0;
}