C++动态数组,多重指针问题

来源:互联网 发布:阿里云华北3是什么地方 编辑:程序博客网 时间:2024/05/16 18:28

今天遇到一个问题,要开辟一个二维指针空间,详细的看了一下网上有关动态指针、动态数组等开辟方法以及学习前辈们对于动态数组问题的解决经验!


一、 

首先说明一下多重指针问题,这里解释到第三层,为了方便理解,我们可以将三重指针理解为三维数组,

首先看一个经典的例子:

int ***a,x=10,y=20;
a=(int ***)malloc(sizeof(int)*x);//分配是什么?
for(i=1;i<10;i++){
(*a)[i]=(int *)malloc(sizeof(int)*y);//分配的又是什么?
}
for(i=1;i<x;i++)
 for(j=1;j<y;j++)
 {
(*a)[i][j]=10;//这样赋值对不
}

1:   *a=(int **)malloc(sizeof(int)*x);可以理解为二重指针的第一层,分配了10个int型的二重指针空间

a是三重指针的地址,*a是二重指针的首地址;(*a)[0];(*a)[1];(*a)[2];(*a)[3];(*a)[4]........

2:    (*a)[i]=(int *)malloc(sizeof(int)*y);这个代表每一个二层指针又被分配了空间;这里每一二层指针被分配了

20个(int *)的指针;即,每一二层指针又有20个孩子

3:   (*a)[i][j]=10;这样赋值正确,前边的(*a)是获取一个二维数组,后边是一个二维数组的一个元素。直接赋值是可以的

4:  我们同样可以理解为树形,几重指针就是几层树

以上是关于三维指针的解释,二维指针也就是可以理解成二维数组。

二、数组的指针、指针数组以及指向指针的指针

 

  考虑数组的指针的时候我们要同时考虑类型和维数这两个属性。换一句话,就是说一个数组排除在其中存储的数值,那么可以用类型和维数来位置表示他的种类。

A)一维数组
  在c和c++中数组的指针就是数组的起始地址(也就第一个元素的地址),而且标准文档规定数组名代表数组的地址(这是地址数值层面的数组表示)。例如:

int a[10]; int *p;

p=&a[0]//和p=a是等价的:

  因为a是数组名,所以他是该数组的地址,同时因为第一个元素为a[0],那么&a[0]也代表了该数组的地址。但是我们是不是就说一个数组名和该数组的第一个元素的&运算是一回事呢?在一维的时候当时是的,但是在高维的时候,我们要考虑到维数给数组带来的影响。
  a[10]是一个数组,a是数组名,它是一个包含10个int类型的数组类型,不是一般的指针变量噢!(虽然标准文档规定在c++中从int[]到int*直接转换是可以的,在使用的时候似乎在函数的参数为指针的时候,我们将该数组名赋值没有任何异样),a代表数组的首地址,在数字层面和a[10]的地址一样。这样我们就可以使用指针变量以及a来操作这个数组了。
所以我们要注意以下问题:

(1) p[i]和a[i]都是代表该数组的第i+1个元素;
(2) p+i和a+i代表了第i+1个元素的地址,所以我们也可以使用 *(p+I)和*(a+I)来引用对象元素;
(3)p+1不是对于指针数量上加一,而是表示从当前的位置跳过当前指针指向类型长度的空间,对于win32的int为4byte;

B)多维数组
  对于二维数组a[4][6];由于数组名代表数组的起始地址,所以a(第一层)和第一个元素a[0][0]地址的数字是相同的,但是意义却是不同的。对于该数组我们可以理解为:a的一维数组(第一层),它有四个元素a[0]、a[1]、a[2]、a[3](第二层),而每个元素又含有6个元素a[0][0],a[0][1],a[0][2],a[0][3],a[0][4],a[0][5](第三层),…到此我们终于访问到了每个元素了,这个过程我们经历了:a->a[0]->a[0][0];
  整体来讲:a是一个4行5列的二维数组,a表示它指向的数组的首地址(第一个元素地址&a[0]),同时a[0]指向一行,它是这个行的名字(和该行的第一个元素的首地址相同(第一个元素为地址&a[0][0]))。所以从数字角度说:a、a[0]、&a[0][0]是相同的,但是他们所处的层次是不同的。
  既然a代表二维数组,那么a+i就表示它的第i+1个元素*(a+i)的地址,而在二维数组中
*(a+i)又指向一个数组,*(a+i)+j表示这个数组的第j+1个元素的地址,所以要访问这个元素可以使用 *(*(a+i)+j)(也就是a[i][j])。
他们的示意图为(虚线代表不是实际存在的):

对照这个图,如下的一些说法都是正确的(对于a[4][6]):

a是一个数组类型,*a指向一个数组;a+i指向一个数组;a、*a和&a[0][0]数值相同;a[i]+j和*(a+i)+j是同一个概念;

  总结一下就是:我们对于二维指针a,他指向数组a[0,1,2,3],使用*,可以使他降级到第二层次,这样*a就指向了第一个真正的数组。对于其他的情况我们也可以采用相同的方式,对于其他维数和类型的数组我们可以采用相类似的思想。

说到指向数组的指针,我们还可以声明一个指针变量让它指向一个数组。例如:

int (*p)[5];

这时p就是一个指针,要指向一个含有5个int类型元素的数组,指向其他的就会出现问题。
这个时候我们可以使用上面的什么东西来初始化呢?
我们可以使用*a,*(a+1),a[2]等。
原因很简单:我们在一个二维的数组中,那么表达方式有上面的相互类似的意义呢?只有 *a,*(a+1),a[2]等,

C)指针数组
  一个指针数组是指一个数组中的每个元素都是一个指针,例如:

int *p[10];//而不能是int (*p)[10]

或者

char *p[10];

此时p是一个指针(数值上和&p[0]一样);
在前面有int t[10];

int * pt=t;//使用pt指向t

那么这里我们用什么指向int *t[10]中的t呢?我们要使用一个指针的指针:

int **pt=t;

  这是因为:在int *t[10]中,每个元素是指针,那么同时t又指向这个数组,数组上和&t[0]相同,也就是指向t[0],指向一个指针变量,可以说是一个指针的指针了,所以自然要用

int **pt;


D)指针的指针
  一个指针变量内部可以存储一个值,这个值是另外一个对象的地址,所以我们说一个指针变量可以指向一个普通变量,同样这个指针变量也有一个地址,也就是说有一个东西可以指向这个指针变量,然后再通过这个指针变量指向这个对象。那么如何来指向这个指针变量呢?由于指针变量本身已经是一个指针了(右值),那么我们这里就不能用一般的指针了,需要在指针上体现出来这些特点,我们需要定义指针的指针(二重指针)。

int *p1=&i; int**p2=&p1;

综合以上的所有点,下面是我们常常看到一些匹配(也是经常出错的地方):

int a[3],b[2][3],c,*d[3]; void fun1(int *p); void fun2(int (*p)[3]); void fun3(int **p); void fun4(int p[3]); void fun5(int p[]); void fun6(int p[2][3]); void fun7(int (&p)[3]);

函数 不会产生编译时刻的可能值(但逻辑上不一定都对)

函数

不会产生编译时刻的可能值(但逻辑上不一定都对)

fun1

a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i]

fun2

bb+i

fun3

d

fun4

a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i]

fun5

a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i]

fun6

b

fun7

a

此段文章出处:http://apps.hi.baidu.com/share/detail/38102294


三、

对于动态数组问题:

参阅他人的建议得知:使用一维数组实现多为数组的功能。定义动态啊的一维数组比较简单,而且所派分的内存单元都是连续的,其实在内存中的存储都是在一块连续的内存块上存储,性质一样!所以建议使用一维数组实现多维的功能;

实例如下:

1、用一重指针

int i,j,num1,num2;
int *p=new int[num1*num2];
for(i=0;i<num1;i++)
{
     for(j=0;j<num2;j++)
    {
     p[i*num1+j]=i*j+5;//给数组赋值,调用的时候也是如此。
}


2、用二重指针

int num1,num2;
int **p;
p=new int*[num1];//开辟空间,理解起来有点难
for(int i=0;i<num1;i++)
    p[i]=new int[num2];


另外两个实例:

一维动态数组的实例:

  1. #include <iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int *arr;
  6. int n;
  7. cout<<"请输入一个整数指定数组大小:";
  8. cin>>n;
  9. arr = new int[n];
  10. for(int i=1;i<=n;i++)
  11. {
  12. cout<<"请输入第"<<i<<"个元素:";
  13. cin>>arr[i-1];
  14. }
  15.  cout<<endl;//输出一个空行
  16.   for(int i=1;i<=n;i++)
  17. {
  18. cout<<"第"<<i<<"个元素是:"<<arr[i-1]<<endl;
  19. }
  20. delete [] arr;//动态空间释放
  21. }
复制代码
二维动态数组的实例:
  1. #include <iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int **arr;
  6. int n=0,m=0;
  7. cout<<"请输入一个整数指定数组第一维:";
  8. cin >> n;
  9. cout<<"再输入一个整数指定数组第二维:";
  10. cin>>m;
  11. arr = new int*[n];
  12. for(int i=0;i<n;i++) //创建动态数组
  13. {
  14. arr[i] = new int[m];
  15. }
  16. cout<<"*****************下面是输入部分*****************"<<endl;
  17. for(int i=0;i<n;i++)//读入数组元素
  18. {
  19. for(int j =0;j<m;j++)
  20. {
  21. cout<<"请输入"<<"arr["<<i<<"]["<<j<<"]的值:";
  22. cin>>arr[i][j];
  23. }
  24. }

  25. cout<<"*****************下面是输出部分*****************"<<endl;
  26. for(int i=0;i<n;i++)//输出数组元素
  27. {
  28. for(int j =0;j<m;j++)
  29. {
  30. cout<<"arr["<<i<<"]["<<j<<"]="<<arr[i][j]<<endl;
  31. }
  32. }
  33. for(int i=0;i<n;i++)//动态空间释放
  34. delete[] arr[i];
  35. delete[] arr;
  36. arr=NULL;
  37. }