转:行指针和列指针

转自:http://blog.csdn.net/haussuden/article/details/5965304

指针与多维数组

(主要指二维数组)

int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};

 行指针和列指针-专题分析

换个角度看世界:

如首行一样,将首行视为一个元素,一个特殊的元素,这个“特殊的”元素是一个一维数组。那么这个二维数组是由是由三个“特殊的”元素组成的一个“特殊的”一维数组。

a是这个“特殊的”一维数组的名称,也就是首地址,也就是第一个元素的地址,也就是第一行的首地址,是指首行一整行,并不是指某个具体元素。那么我们称之为“行指针”。同理:a+0,a+1,a+2,都是行指针。

 

结论:

表示形式

含义

指针类型

a或者a+0

指向第0

行指针

a+1

指向第1

行指针

a+2

指向第2

行指针

 

接下来,我们来放大观看首行,首行的元素分别是:a[0][0],a[0][1],a[0][2],a[0][3]。将其看作一个独立的一维数组,那么 a[0]就是这个数组的名称,也就是这个数组的首地址,也就是第一个元素的地址,也就是a[0]+0。a[0]和a[0]+0都是指具体的元素,那么我们称之为“列指针”。

 

结论:(第0行视为一维数组)

表示形式

含义

指针类型

a[0]

是一维数组的名称,也是它的首地址,而且是第1个元素的地址(a[0]+0

列指针

a[0]+1

0行,第2个元素的地址

列指针

a[0]+2

0行,第3个元素的地址

列指针

 

两个重要概念:行指针和列指针。

行指针:指的是一整行,不指向具体元素。

列指针:指的是一行中某个具体元素。

可以将列指针理解为行指针的具体元素,行指针理解为列指针的地址。

那么两个概念之间的具体转换是:

*行指针----列指针

&列指针----行指针

 

根据以上转换公式:

行指针

转换成:列指针

列指针等价表示

内容

内容等价表示

含义

aa+0

*a

a[0]

*a[0]

*(*a)

a[0][0]

a+1

*(a+1)

a[1]

*a[1]

*(*(a+1))

a[1][0]

a+2

*(a+2)

a[2]

*a[2]

*(*(a+2))

a[2][0]

对于元素a[1][2],其地址用列指针表示为a[1]+2,等价表示为*(a+1)+2,那么内容是*(*(a+1)+2)

 

 

列指针

行指针

等价表示

含义

a[0]

&a[0]

&a&(a+0)

0

a[1]

&a[1]

&(a+1)

1

a[2]

&a[2]

&(a+2)

2

示例1:用列指针输出二维数组。

#include <stdio.h>

void main()

{

   int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};

   int *p= a[0];   // 列指针的定义法

 

   for(; p < a[0] + 12; p++)

   {

     printf("%d ",*p);

   }

  

    return;

}

示例2:用行指针输出整个二维数组。

#include <stdio.h>

 

void main()

{

   int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};

   int (*p)[4]= &a[0]; // 行指针定义法或者int (*p)[4]= a;

   int i, j;

 

   for(i = 0; i < 3; i++)

     for(j = 0; j < 4; j++)

   {

     printf("%d ",*(*(p + i) + j));

   }

  

    return;

}

 

转自:http://soft.chinabyte.com/database/339/12423339.shtml

关于C语言行指针的详细解析

最近在百度知道里面发现很多学习初学者搞不清楚行指针的具体概念,今天在这里作下详细的解释分析。

  行指针,顾名思义就是指向一行的指针。那么哪里会用到行指针呢,用的最多的,那就是二维指针了,大家都知道,我们通常把二维指针看成一个行列式,但是它在内存中的排序却是和一维指针一样的。

  比如组a[2][3]={{1,2,3}{4,5,6}},a是整个数组的首地址,同时也指向第一行元素,即a是一个行指针,它每加1,所指地址移动二维数组的一行,a+1指向第二行元素。

  对a取*,即*a指向第一行第一个数,*(a+1)指向第二行第一个数,可见,对行指针取值就成了列指针,此时它还是个指针。它每加1,所指地址移动一个元素,*(a+1)+1指向第二行第二个元素,也可以写成a[1]+1。

  **a(也可写成a[0][0])就是这个二维数组第一行的第一个元素,**(a+1)(也可写成a[1][0])就是第二行的第一个元素,*(*(a+1)+1)(也可写成a[1][1])是第二行的第二个元素。可见,对行指针取2次*就成了某个元素的值了,而不再是地址。

  有这样一个公式a[i][j]=*(*(a+i)+j),应该就知道为什么了吧…

  行指针还可以这样定义int (*p)[n],此处定义的p指针,每加1,移动n个地址位(针对不同二维数组定义不同的n值)比如下面的这段:

  int main(void)

  {

  int a[2][3] = {{1,2,3},{4,5,6}};

  int (*p)[3];

  p = a;

  p++;

  printf("%d",**p);

  }

  此段代码输出结果为4,p是个行指针,可以直接将a这个行指针直接赋值给它,此时p也指向二维数组的起始地址,即第一行。p++就会移动3个元素,从而指向第二行。用法同二维数组名a。

转自:http://www.cppblog.com/haosola/archive/2013/04/04/199089.html

基于C++二维数组的几个问题研究

二维数组使用一般有两种情况,一种是描述一个二维的事物。比如用1表示墙,用0表示通路,我们可以用二维数组来描述一个迷宫地图;用1表示有通路,0表示没有通路,我们可以用二维数组来描述几个城市之间的交通情况。还有一种是描述多个具有多项属性的事物。比如有多个学生,每个学生有语文、数学和英语三门成绩,我们就可以用二维数组来描述。对二维数组使用过程中的几个问题进行探究。
   关键词:C++;二维数组;动态分配;参数;指针
   C++程序中的new[]和delete[]作为动态内存分配的重要手段在程序设计中有着广泛应用,尤其应用在程序员自定义的类与结构的数组声明上。但由于C++程序语言中并没有给出显示的数组访问限制[1],极易造成"数组越界"的错误。这种错误会引起对非程序数据段的修改,而使得程序崩溃。
   1C++中二维数组的动态分配
   在C++中动态分配二维数组可以先申请一维的指针数组,然后该数组中的每个指针再申请数组,这样就相当于二维数组了,但是这种方法会导致每行可能不相邻,从而访问效率比较低。那么什么是真正的二维数组呢?C语言中的二维数组在内存组织形式是按行存储的连续的内存区域。所以,必须保证数组元素是按行存储的,而且也是最重要的是内存要连续http://www.wx-jr.com 
   所以,笔者设计如下的一个方法:
   假定二维数组的元素变量类型是MyType;可以是C语言接受的除void之外的任何类型,因为编译器不晓得void类型的大小;例如int,float,double等等类型;
   introw=2;/暂假定行数是2,这个可以在运行时刻决定;
   intcolumn=3;/暂假定列数是2,这个可以在运行时刻决定;
   void**ptdhead=NULL;//在后面说明为什么要用void**类型
   void**ptdBody=NULL;//在后面说明为什么要用void**类型
   ptdhead=(void**)malloc(sizeof(void*)*row+sizeof(MyType)*row*column);
   if(!ptdhead)
   returnFALSE;
   ptdBody=ptdhead+row;
   for(intncount=0;ncount<row;ncount++)
   ptdhead[ncount]=ptdBody+ncount*column*sizeof(MyType)/sizeof(void*);
   MyType**ptdheadRealse;
   ptdheadRealse=(MyType**)ptdhead;///强制转换为自己程序需要的二维数组元素类型的指针
   ptdhead=NULL;
   for(inti=0;i<row;i++)
   {
   for(intj=0;j<column;j++)
   {ptdheadRealse[i][j]=i+j;进行简单的初始化;}
   }
   这样的一种方法动态分配的二维数组,内存是连续的,是真正意义的C语言二维数组,满足所有二维数组访问的方法,而且内存利用效率高,程序性能好。
   2C++二维数组的传递
   用二维数组作为参数传递(用二维数组处理矩阵),但是希望接受传递二维数组参数的函数可以处理任意维度的数组(希望矩阵的行数和列数都是不固定的)。但一般传递二维数组的基本规则好像是这样的:可以用二维数组名作为实参或者形参,在被调用函数中对形参数组定义时可以可以指定所有维数的大小,也可以省略第一维的大小说明。如http://www.sd-ju.com 
   voidFunc(intarray[3][10]);voidFunc(intarray[][10]);
   二者都是合法而且等价,但是不能把第二维或者更高维的大小省略,如下面的定义是不合法的:voidFunc(intarray[][]);将二维数组当作参数的时候,必须指明所有维数大小或者省略第一维的,但是不能省略第二维或者更高维的大小,这是由编译器原理限制的。但是我们在编写程序的时候却需要用到各个维数都不固定的二维数组作为参数,这就难办了,编译器不能识别阿,怎么办呢?不要着急,编译器虽然不能识别,但是我们完全可以不把它当作一个二维数组,而是把它当作一个普通的指针,再另外加上两个参数指明各个维数,然后我们为二维数组手工寻址,这样就达到了将二维数组作为函数的参数传递的目的,根据这个思想,我们可以把维数固定的参数变为维数随即的参数。
   3C++中二维数组形参的传递
   voidFunc(intarray[3][10]);voidFunc(intarray[][10]);可以省略第一维的大小,错误的是voidFunc(intarray[][].这样的用法只能在初始化时可以用);这样写也是错误:voidFunc(constintm,constintn,intarray[m][n]);或voidFunc(intm,intn,intarray[m][n]);大家都知道数组的索引必须是个常量表达式,voidFunc(constintm,constintn,intarray[m][n]);如果constintm没有初始化,那么系统将m或n自动初始化为0,所以这样些是不对的,如果我们采用这样voidFunc(int**array,intm,intn)的形式,那么在实际的函数调用是,我们就要进行强制转换才可以用,我们可以这样调用voidFunc((int**)array,intm,intn);在函数调用时,要把数组形式写成指针形式如*((int*)array+n*i+j);直接写intarray[i][j]会导致错误,编译可以通过,在VC编译器中执行会出现异常,DEV编译器会出现一个随机值,原因就在于如果写成intarray[i][j],编译器无法正确的寻址,当然各种编译器对它的处理结果是不一样的。如果我们的形参是数组,那么我们在函数体中可以用指针也可以用数组形式,但是如果我们形参数中用的是指针,最好也用指针,有时用数组形式会出错,二维数组就是这样http://www.sd-ju.com 
   4二维数组中的指针问题
   1)用指针表示二维数组元素。要用指针处理二维数组,首先要解决从存储的角度对二维数组的认识问题。我们知道,一个二维数组在计算机中存储时,是按照先行后列的顺序依次存储的,当把每一行看作一个整体,即视为一个大的数组元素时,这个存储的二维数组也就变成了一个一维数组了。而每个大数组元素对应二维数组的一行,我们就称之为行数组元素,显然每个行数组元素都是一个一维数组。
   2)用二维数组名作地址表示数组元素。我们还可以得到二维数组元素的一种表示方法:对于二维数组a,其a[0]数组由a指向,a[1]数组则由a+1指向,a[2]数组由a+2指向,以此类推。因此,*a与a[0]等价、*(a+1)与a[1]等价、*(a+2)与a[2]等价,┅,即对于a[i]数组,由*(a+i)指向。由此,对于数组元素a[i][j],用数组名a的表示形式为:*(*(a+i)+j)。指向该元素的指针为:*(a+i)+j。数组名虽然是数组的地址,但它和指向数组的指针变量不完全相同。指针变量的值可以改变,即它可以随时指向不同的数组或同类型变量,而数组名自它定义时起就确定下来,不能通过赋值的方式使该数组名指向另外一个数组。
   3)行数组指针。在上面的说明中我们已经知道,二维数组名是指向行的,它不能对如下说明的指针变量p直接赋值:inta[3][4]={{10,11,12,13},{20,21,22,23},{30,31,32,33}},*p;其原因就是p与a的对象性质不同,或者说二者不是同一级指针。C语言可以通过定义行数组指针的方法,使得一个指针变量与二维数组名具有相同的性质。行数组指针的定义方法如下:数据类型(*指针变量名)[二维数组列数];例如,对上述a数组,行数组指针定义如下:int(*p)[4];它表示,数组*p有4个int型元素,分别为(*p)[0]、(*p)[1]、(*p)[2]、(*p)[3],亦即p指向的是有4个int型元素的一维数组,即p为行指针此时,可用如下方式对指针p赋值:p=a;
   下面说明一下,我碰到的问题,我们定义了一下如下的函数:voidfunction(double**array,intwidth,intheight)。
   然后我们定义了一个二维数组doublep[3][3]={{1,2,3},{4,5,6},{7,8,9}};
   当我们调用function时,即function(p,3,3),编译器会报错:
   errorC2664:'function':cannotconvertparameter1from'double[3][3]'to'double**'
   参数传递实际上是一个赋值的过程,为了便于说明我们底下都以赋值的方式加以说明。
   我们知道p是数组首地址,地址指向的是第一个行数组,在某种程度上来说可以把二维数组名理解为指针的指针,但是这两者是有区别的。
   double*p[3]和double(*p)[3]
   double*p[3]是一个指针数组,它是一个数组,里面存放了3个指针;double(*p)[3]它是一个数组指针,它是一个指针,这个指针指向的是一个数组,它和二维数组有相同的性质,具体说明可以看下分割线中红色的字体所示的部分,由此我们可以知道如下的赋值是可行的:doublep[3][3]={{1,2,3},{4,5,6},{7,8,9}}http://www.wx-jr.com 
   double(*pp)[3]=p;
   这里实际上的话也是应该执行了一次上面所说的自动转化,p转化了一个指向行数组的指针,然后赋值给了数组指针变量pp;另外,我们发现底下的赋值也是可行的:
   double*p[3];
   double**pp=p;
   这里实际上也是执行了一次上面所说的转化,p转化了一个指向指针变量的指针,然后赋值给了pp;这里看下,上面两次转化后唯一的的区别于一个是指向数组的指针,一个是指向指针的指针,而C++不允许接下来的再次转化,说明C++只支持数组到指针的一次转化,而二次转化没有支持。

 

转自:http://hi.baidu.com/zyaijava/item/eb55d0a3c9574a34030a4da1

指针数组和数组指针总结

指针数组:数组元素全为指针数组,(是数组,元素全为指针)。

 一维指针数组, int *ptr[5]; 

数组指针:数组首元素的指针,(是指针,保存数组首元素的地址)。 

int (*ptr)[5];

 

赋值对比:

数组指针赋值 

int a[4][5]; 

int (*ptr)[5]=a; // a为二维数组名,相当于二级指针常量,ptr是指向一维数组(大小是5)的指针。

 

ptr是一个指针变量,它指向包含5个int元素的一维数组,此时ptr的增量以它所指向的一维数组长度为单位;

*ptr+i是二维数组a[0][i]的地址;

 

*(ptr+2)+3‍表示a[2][3]地址(第一行为0行,第一列为0列),*(*(p+2)+3)表示a[2][3]的值

 

ptr = a = &a[0] = &a[0][0];//这些意思是相同的

*ptr = ptr[0] = &a[0][0];//由于数组的特性:&a, a, &a[0], &a[0][0]都表示数组的首地址

【注意】:*ptr = ptr = ptr[0] = a = &a[0] = &a[0][0] 这里不是手误,数组指针中,确实是*ptr = ptr 

解释如下:

      类似一维数组int b[5], b = &b = &b[0],一维数组名表示整个数组的起始地址,跟首地址一样

      数组指针中,*ptr是 int[5]类型的,ptr是int[5]*, int[5]的地址是数组指针的首元素,故*ptr = ptr = ptr[0]

 

*(ptr+1) = ptr[1] = &a[1][0] = &a[5];

 

(*ptr)[0] = *(*ptr+0) = ptr[0][0] = a[0][0];

(*ptr)[1] = *(*ptr+1) = ptr[0][1] = a[0][1];

*(ptr+0)+1 = &ptr[0][1];

*(ptr+1)+1 = &ptr[1][1];

 

指针数组赋值(需要动态分配空间)

char *ptr[5] = {"one", "two", "three", "four","five"};//系统给ptr分配的空间不一定连续,

并且给ptr分配的空间取决于具体字符串的长度。

字符指针数组相比二维字符数组优点:

 

一是:指针数组中每个元素所指的字符串不必限制在相同的字符长度;

二是:访问指针数组中的一个元素是用指针间接进行的,效率比下标方式要高。 但是二维字符数组却可以通过下标很方便的修改  某 一元素的值,而指针数组却无法这么做。

 

指针数组,数组指针对比:

指针数组 是数组元素为指针的数组,其本质为数组

数组指针 是指向数组首元素的地址的指针,其本质是指针,(指针存放的是数组首地址的地址,相当于2级指针,这个指针不可移 动)

 

指针数组,二维数组对比:

二维数组:只要定义了一个二维数组(假设是二维字符数组),无论是否赋值,系统都会给他分配相应的空间,而且该空间一定是连续的, 其中每个元素表示一个字符,可以通过指定下标对每个元素进行访问和修改。

指针数组:系统分配指定大小的连续空间来存储元素的地址,每个元素是一个指向字符型数据的一个指针。

 

数组指针,二维数组对比在数组指针赋值里面已经详细说明了。

 

指针数组,数组指针,二维数组,二级指针的关系和区别:

二级指针:int **ptr;

指针数组: int *ptr[5];

数组指针: int (*ptr)[5];

二维数组: int ptr[3][5];

 

指针:本质是可变数组的首地址,这个可变数组,是指包含的内容的数量的可变性,指针另外是可动态申请和释放的,对内存来说 是利用比较充分。

占用空间:

  int **ptr: sizeof(ptr) = 4

  int *ptr[3]: sizeof(ptr) = 12

  int (*ptr)[5]: sizeof(ptr) = 4

  int ptr[3][5]: sizeof(ptr) = 3*5*4 =60

 

二级指针:

   (1) int **ptr,需要两次内存分配才能使用其最终内容,首先,ptr = ( int ** )malloc( sizeof(int *) * 3);此时就和

        int *ptr[3]意义相同了,然后在对3个指针进行内存分配, ptr[0] = (int*)malloc(sizeof(int) *5);

        【注】如果没有ptr的第一次内存分配,则ptr是个野指针,是无法使用,使用会出问题

              如果没有ptr[0]其他元素的第二次内存分配,则ptr[0]等元素是野指针,使用也会出问题.

   

   (2) int *ptr[3],这样定义,编译器已经为ptr分配了3个指针的空间,相当于int **ptr二级指针的第一次内存分配

        需要对ptr[0]对元素进行一次内存分配,否则是野指针。

 

   (3) int (*ptr)[5],是一群指针,每个指针都是指向5个整数的数组,如果动态分配k个指针内存的话如下:

       ptr = (int *)malloc(sizeof(int)*5 *k), ptr此时指向一片连续的空间,

       ptr[0]指向下标是0的5个整数数组的首地址.ptr[1]指向下标是1的5个整数数组的首地址.

   

   (4) int ptr[3][5],是二维数组,定义好,编译器已经为二维数组分配了3*5个指针的空间,并且是连续的。

 

   可以如下理解:

   int **ptr <==> int ptr[x][y]; //上面第一次分配x=3,y=5

   int *ptr[3] <==> int[3][y];//y的长度可以每个数组元素不同

   int (*ptr)[5] <==> int[x][5]; //x的长度每个元素必须一样

   int ptr[3][5] ;//

转载于:https://www.cnblogs.com/kira2will/p/3590748.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值