C语言、二维数组指针中的 * 和 &
先从存储的角度对二维数组作一个全面的了解。二维数组在内存中的存储,是按照先行后列依次存放的。从内存的角度看,可以这样说,二维数组其实就是一个一维数组,在内存中没有二维的概念。如果把二维数组的每一行看成一个整体,即看成一个数组中的一个元素,那么整个二维数组就是一个一维数组,它以每一行作为它的元素,这个应该很好理解。
第一,来详细介绍二维数组与指针的关系。-
首先定义个二维数组 array[3][4],p 为指向数组的指针。
若p=array[0],此时p指向的是二维数组第一行的首地址,则 p+i 将指向array[0]数组中的元素array[0][i]。由以上所介绍的二维数组在内存中的存储方式可知,对数组中的任一元素array[i][j] ,其指针的形式为:p+i*N+j (N为每一行的长度)。 元素相应的指针表示法为:*(p+i*N+j) ,下标表示法为:p[i*N+j] 。
For Example:
array[4][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};
int * p=array[0];
数组array有四个元素,分别为array[0],array[1],array[2],array[3],每个元素为包含3个元素的一维数组,
如array[0]的3个元素为 array[0][0],array[0][1],array[0][2]。
元素array[2][2]对应指针为:array+2*3+2,
指针表示法为:*(array+2*3+2) ,
下标表示法为:array[2*3+2] 。
特别注意:虽然 array[0] 与 array 都是数组首地址,但两者指向的对象不同,这点要非常明确。array[0] 是一维数组的名字,它指向的是一维数组array[0]的首地址,所以 *array[0]与array[0][0]为同个值。而 array 是二维数组的名字,它指向的是所属元素的首地址,其每个元素为一个行数组。它是以‘行’来作为指针移动单位的,如array+i 指向的是第 i 行。对 array 进行 * 运算,得到的是一维数组 array[0] 的首地址,所以 *array 与 array[0] 为同个值。如果定义 int* p,p为指int类型的指针,指向int 类型,而不是地址。故以下操作 :p=array[0] (正确) ,p=array (错误) 。这点要非常注意。
第二,看看如何用数组名作地址表示其中元素。
对二维数组array ,array[0] 由 array指向,故*array 与array[0] 是相同的,依次类推可得 array[i] 由array+i 指向,*(array+i) 与array[i]是相同的。 因此,对于数组元素 array[i][j] ,用数组名表示为 *(*(array+i)+j) ,指向该元素的指针为 *(array+i)+j 。
注意:数组名虽然是地址,但与指向数组的指针性质不同。指针变量可以随时改变其所指向对象,而数组名不可以,一旦被定义,就不能通过赋值使其指向另外一个数组,但是在Java中则可以。
第三,顺便了解一下不太常用的‘行数组指针’。
对于二维数组array[4][3],与int* p 。二维数组名array 不能直接赋值给p。原因前面已讲过,两只的对象性质不同。 在C语言中,可以通过定义一个行数组指针,使得这个指针与二维数组名具有同样的性质,实现它们之间可以直接赋值。行数组指针定义如下:
int (*p)[3]; 它表示,数组 *p 具有三个int类型元素,分别为 (*p)[0] , (*p)[1] , (*p)[2] ,即 p指向的是具有三个int类型的一维数组,也就是说,p为行指针。此时,以下运算 p=array 是正确的。
第四,二维数组作为函数参数。
二维数组作为函数参数一般有两种方式:(1) void func(int **array){...} (2) void func(int array[ ][N])
注意第二种方式一定要指明二维数组的列数
当二维数组名作为函数实参时,对应的形参必须是一个行指针变量。
和一维数组一样,数组名传送给变量的是一个地址值,因此,对应的形参也必须是一个类型相同的指针变量,在函数中引用的将是主函数中的数组元素,系统只为形参开辟一个存放地址的存储单元,而不可能在调用函数时为形参开辟一系列存放数组的存储单元。
int main()
double a[3][4];
……
fun(a);
……
fun(double (*a)[n])
……
int (*p)[4]
定义了一个指针p,指向一个4个元素的数组。只是定义一个指针,
指向4个元素的数组
int *p[4]
定义了一个数组指针p[4],p的每个元素指向一个整型的数据。就
是数组里面保存的是指针
(1)int* p[2] 是一个指向int型的指针数组,即:p是包含两个元素的指针数组,指针指向的是int型。
可以这样来用:
#include
void main() {
int* p[2];
int a[3] = {1, 2, 3};
int b[4] = {4, 5, 6, 7};
p[0] = a;
p[1] = b;
for(int i = 0; i < 3; i++) cout << *p[0] + i;// cout << **p + i;
cout << endl;
for(i = 0; i < 4; i++) cout << *p[1] + i;// cout << **p + i;
}
(2)对于 int (*p)[2], 它相当于一个二维数组的用法,只是它是一个n行2列的数组,可以这样来用:
#include
void main() {
int (*p)[2];
int b[3][2] = {{1, 2}, {3, 4}, {5, 6}};
p = b;
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 2; j++) //cout << p[i][j]; //cout << *(*(p+i)+j);
cout << endl;
}
}
我看了你说的这两页,书上的原话是这样的:“在二维数组中,a+i,*(a+i),&a[i],&a[i][0]的值相等,即他们都是同一地址值”,要注意这个字眼“地址值”,也就是说,这几种表达方式并不是内存中存的数值,而是存放数组的地址的值,首先要弄明白,首地址这个概念,如果只写数组名,就代表数组的首地址,比如,一维数组a[9],a就代表数组首个元素的地址即首地址,a[0]是首个元素而&a[0],你可以把&理解成取地址符,即取出存放该数的地址,那么&a[0]就代表数组首个元素的地址了,即首地址,即a和&a[0]是一个意思,
在看二维数组,二维数组只是比一维数组多了一个“首地址”,即列的首地址,比如a[3][4],a[0]代表什么?a[0]是该二维数组第一行的首地址,这个根据前面我所说的能明白么?那么a代表什么呢?这个其实也是首地址,都是代表数组首行首列数据的地址,值是一样的,接下来就要根据上面的思路认真琢磨思考了,a+i代表什么?注意这个i,a[i][j],其中的i代表的是数组的行,j代表的是数组的列,a+i,其实是加了i行,也就是第i行的第一个元素的地址,可以理解成a[i](把a[i]这一行想象成以为数组,根据一维数组的思路思考,就能明白了),即a+i和a[i]的值相等,如书上所说,在根据上面解释一维数组的思路考虑,&a[i][0]代表什么意思?不难理解,这代表的是第i行的首地址,也就是第i行的第一个元素的地址,如书所说,a+i,a[i],&a[i][0]的值是一样的,
还有一个是*(a+i),这个就需要转转脑筋了,二维数组用指针怎么表示?用二维指针即:**a,那么a[3][4]这个数据(注意是一个数值)用指针怎么表示呢?*(*(a+3)+4),必须有两个“*”才行,要不就是一维数组了,里面是*(a+3)这代表的是第三行没错吧,第三行的什么呢?第三行的首地址没错吧,*(a+3)+4代表什么?代表第3行第4个元素的地址,在最前面加个“*”,*(*(a+3)+4)就是a[3][4]了,那么*(a+i)代表什么?代表第i行的首地址没错吧。。。,那么上面说的a+i代表什么呢?看看就知道了,也是第i行的首地址,也就是a+i和*(a+i)的值是一样的,如书所说,这样书上的那句话就都明白了
2、
关于这个问题,确实有点绕,如果能理解你问的第3个问题,这个就有点眉目了。。。
3、
在二维指针当中(注意指针和数组是不一样的,只是有时候表达的东西没什么区别罢了),在二维指针中,分行指针和列指针,比如上面的a[3][4],如果用指针的形式表示的话,一共有3个行指针,一个列指针,每个行指针都仅指向行的首地址,这个列指针指向的是,第一行的首地址,这个如果用图来表示就再好不过了,可是我没办法在这上面画图,就这样表示吧,看下面:
列指针: a
|
行指针1: a[0]—。。。。。。。
行指针2: a[1]—。。。。。。。
行指针3: a[2]—。。。。。。。
4、
我也不知道这样说对不对,反正我没试过,你不仿试试看。。。
其实这些问题都没必要深究,刚开始学这部分,确实是非常难懂,但只要写的程序多了,用的多了,自然而然就懂了,
最后,祝你成功
要回答你的这个问题,不妨先根据你的问题先编写一段小的示例程序:
#include <cstdio>
#include <cstring>
using namespace std;
int a[3][4];
int main(){
memset(a, -1, sizeof(a));
int i;
for( i = 0; i < 3; i++){
printf("&a[i][0] = %d\n", &a[i][0]);
printf("a + i = %d; *(a + i) = %d;\n", a + i, *( a + i));
printf("a[i] = %d; &a[i] = %d;\n", a[i], &a[i]);
printf("\n");
}
}
这段示例程序在我本地上运行的结果为:
&a[i][0] = 4214816
a + i = 4214816; *(a + i) = 4214816;
a[i] = 4214816; &a[i] = 4214816;
&a[i][0] = 4214832
a + i = 4214832; *(a + i) = 4214832;
a[i] = 4214832; &a[i] = 4214832;
&a[i][0] = 4214848
a + i = 4214848; *(a + i) = 4214848;
a[i] = 4214848; &a[i] = 4214848;
我想看到这个运行结果,你已经有所了解了,下面我再详细说一下:
指针量a作为一个二维数组的指针量,其本质是一个“指向指针的指针”(为了方便,下面称其为双重指针),或者你可以理解为a为int**类型的指针量。当a做加减运算时,得到的结果仍然是一个“双重指针”,即a + i是一个双重指针,它是一个指向了原数组第i行的起始位置的双重指针。
而*(a + i)原来的双重指针解引用后得到的一个指针变量,他是一个指向了原数组第i行的起始位置的“单重指针”。
可以看出,a + i和*(a + i)尽管数据类型不同(前者为双重指针,后者为单重指针),但是他们指向同一块内存地址,因此作为指针量,他们的值是相等的。注意,仅仅是说,他们的值在数学上是相等的,并非说“他们是等价的”。
你的第二个问题亦是同理:
首先,[]作为C语言的操作符,本身即具有“解引用”的语义。明白的说,就是a[i]与*(a + i)是完全等价的,而&a[i]即等价于a + i。由于易知a + i和*(a + i)在数值上相当,那么进行等价带花可以知道,&a[i]和a[i]在数值上相等。当然,此处仍非等价关系。
至于指向行的指针,所指的就是指向行首位置的指针量。
至于你的最后一个问题,正如我刚才一直强调的那样,等号两端仅仅是数值上的相等,而非等价关系,因此无法按照你所设想的方式进行归纳推导。
1.a[3][4]表示第三行,第四列。
a+i的a是一个数组,他会自动转化为指针,指向数组的第一行,也就是a[0],或者a[0][0],因为第一行的地址起始就是第一个元素地址。所以a[0]和a[0][0]是一样的。所以a+i起始就是a[0]+i,0表示数组的行,i表示列。
*(a+i)其中的a和i同上。
2.a[i]代表一个数值,假设你的数组时int类型,比如a[i]=6;那加上&就表示对该变量取地址,想想,6你怎么取地址还是6,对不对?所以都差不多。
3.你见过int *p;吧,这个数组在运算的时候自动回转化为指针,比如int *p; int a[10];你一定见过
p = a; 这把a赋值给了一个指针,这就说明a在其实是一个指针,又因为a相当于一个指针(他又是数组),我们说二维数组是两个一维数组,所以当然就有指向行与列的指针了。
4.关于这点,本人也没有深究过,但作为过来人,我想说的是,不要死扣语法,语法这东西是人定的,不是计算机定的,如果你实在要去研究这个***,那去学汇编,这可以帮助你。
C 语言,指针的麻烦 是 形式逻辑 不成立,也就是我们习惯的 数学 -- 代数学 方式 不成立。
所以 “可不可以从数学上推导出”-- 答案 是 不可以。
C 语言,声明中如果写:
int *a; // 声明 a 是 指针,指向 int
这时语句中 允许出现 *a 和 a, 不允许出现 &a. a表示指向的地址,*a 表示值。
声明中如果写:
int a; // 声明 a 是 整型变量,不是指针
这时语句中 允许出现 a 和 &a, 不允许出现 *a. a 表示值,&a表示a的地址。
我想,你已经看出 让人头痛之处。
int a[3][4]; // 声明 a 是 二维数组,
这时语句中 允许出现 a[j][i], &a[j][i], 不允许出现 *a[j][i].
二维数组的始地址是 &a[0][0].
初学者,掌握我上面说的即可。
a+i 和 *(a+i) 怎么会是一样的呢?-- 这是C语言 不严格性 造成的,C语言 说 二维数组a[][] 的始地址是 &a[0][0], 但也是 &a[0], 也是a.
a+i 呢 是 &a[0][0] + i * [4].
a+i 和 *(a+i) 是一样的 -- C语言里确实如此,这是C语言编译器里的计算方法造成的。
对于这些繁琐不实用的东西,不要深究,不要用它。
1.
a+i和*(a+i)不是一样的,a[i]和*(a+i)才是一样的。a[i]表示数组a第i个元素,而*(a+i)指的是先将a所在地址后移i位以后,读取地址存储的内容,是一样的。请注意这个*,是指针运算符。指针运算符(*)与解引用操作符(*)是一个东西。对指针使用解引用操作符时,直接相当于是对指针指向的对象的一个引用。dereference 用来取出指标所指物体的内容。
2.
为什么a[i]不是变量呢?i不是一直在变么?所以a[i]也是一直在变的。a[i]的地址也就随元素的变化,在变化啊!
3.
二维数组,可以看成线性代数里的行列式,这个懂吧。比如a[i][j],i代表的就是行,j代表的就是列了。这里就有i和j两个变量了,怎么定义指针,不用我教你了吧。其实你不用死掌握概念,会写程序,懂得用法了,就行了。
4.
不可以使用任意个星号,我曾经也是想象力很丰富,后来改掉了这个习惯,不再想象。
5.
希望对你有帮助,回答不正确的地方,请楼主海涵。
另外,希望楼主多看课本,多写程序,这样才会有丰富的经验。
C语言、二维数组指针中的 * 和 &
在二维指针当中(注意指针和数组是不一样的,只是有时候表达的东西没什么区别罢了),在二维指针中,分行指针和列指针,比如上面的a[3][4],如果用指针的形式表示的话,一共有3个行指针,一个列指针,每个行指针都仅指向行的首地址,这个列指针指向的是,第一行的首地址,这个如果用图来表示就再好...
利用指针数组求二维数组各行元素之和
int main(){ int a[ROWS][COLS] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; int sum[ROWS] = {0}; int *p[ROWS]; \/\/ 定义指针数组,每个元素都指向一行的首地址 for (int i = 0; i < ROWS; i++){ p[i] = a[i]; \/\/ 将每行首地址赋值给指针数组的...
C语言:通过指针引用多维数组一节中,二维数组a[0]和*(a+0)等价,怎么理 ...
a[0]是作为二维数组首元素的一维数组名,所以a[0]表示一维数组(实际是二维数组首行)首元素的地址,即,a[0]是a[0][0]的地址;数组名a是指针,指向其首元素(a[0]),即其值为&a[0](&a[0]指向a[0][0]),所以*(a)(即*(a+0))指向指向a[0][0];所以,a[0]与*(a+0)等...
编写程序,求二维数组中的全部元素之和(用指针实现)
include <stdio.h>int main(){int n,m,i,j,s=0,k=0; scanf("%d%d",&n,&m); int a[n][m],*p=&a[0][0]; for(i=0;i<n;i++) for(j=0;j<m;j++) a[i][j]=++k; for(;p
c语言编函数用指针求二维数组每列和
include <stdio.h>#include <malloc.h>#include <string.h>#define M 2 \/\/行数#define N 3 \/\/列数int *col_sum(int (*a)[N], int r, int c){ int i, j, k; int *array = (int *)malloc(sizeof(int)*c);memset(array, 0, sizeof(int)*c); for(i=0;i<r;...
二组数组与指针中a[i]和*(a+i)等价问题
你要记住 a[i]和*(a+i)等价的,但在二维数组中表示的是 地址。在一位数组中是直。你可以这样理解,一个一位数组,他的每个元素都是一个数组,那他就是一个二维数组,那么a[i]或*(a+i)就表示相应的那个元素,而这个元素是一个数组,当然a[i]或*(a+i)指的就是这一数组的首地址,以...
c语言数组中,这里怎么会有两个星号?什么意思?
p是二维数组的指针,也就是指向只针的指针,**表示连续两次取值。因为p是指向指针的指针,所以第一次取值,取出来的是个指针,第二次取值取出来的就是值了
在二维数组int[][]中,int *[]和int (*)[]还有int **分别是什么指针
int *[] 指存储指针的数组 int (*)[] 指存储二位指针某维中的某个的数据 例如(*a)[4]是第0维的想序号4的数据4 int ** 类同与上面那条,例如**a是第0维的想序号0的数据0 如果是*((*(a + 2)) + 3)是第2维的序号3的数据13 可以用下面函数简单的参考下 int main(){ in...
c语言求二维数组中所有元素的和
int main(void){ int ar[3][3]= { {1,2,3},{4,5,6},{7,8,9} };int num;int i,j;int k = 0,sum=0;int (*ptr)[3];ptr = ar;num = sizeof(ar) \/ sizeof(ar[0]);printf("num = %d\\n",num);printf("%d\\n",*(*(ptr +0) + 1));for(i = 0; i < 3...
二维数组 int w[3][5],那W表示什么?*(w+1)表示?
(*(w+1))代表的是第二行的第一个元素,也就是w[0][1]的值。如果上述解释中的资料是正确的,那么应该是印刷时漏了一个*,正确的表达应该是**(&w[0][0]+1)。在C语言中,数组名实际上就是数组的首地址,因此w就指向了整个二维数组w[3][5]的起始位置。当我们使用w+1时,其实是在数组...