C语言中二维数组的问题,纠结很久了,求解脱,谢谢

qa385879413 2012-04-22 01:21:34
C语言中 二维数组a[i][j]的地址为什么可以表示成*(a+i)+j

为什么 *(a+i)不是表示(a+i)地址里面存放的值?a[i]可以看成是一位数组,取值(一维数组)就等于地址?而不是地址里面存放的值?我在这个问题上纠结很久了。求详细解释,一针见血。谢谢啊。
...全文
5337 36 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
36 条回复
切换为时间正序
请发表友善的回复…
发表回复
duyiwuer2009 2012-06-03
  • 打赏
  • 举报
回复
如何实现一个跟int a[][M] 可以兼容的动态二维数组呢???
流星陨落 2012-05-03
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 的回复:]

C/C++ code
//在堆中开辟一个4×5的二维int数组
#include <stdio.h>
#include <malloc.h>
int **p;
int i,j;
void main() {
p=(int **)malloc(4*sizeof(int *));
if (NULL==p) return;
for (i=0;i<4;i++) {
……
[/Quote]
++
14号选手 2012-05-03
  • 打赏
  • 举报
回复
把这个数组分开看,行和列分开看你就会悟出来了
如果定义一个指针变量,是可以将数组的第一个元素也就是数组的入口(既数组名)传给指针
就比如你举得例子
二维数组a[i][j]的地址为什么可以表示成*(a+i)+j
此时a=[0],a+i=&a[i](这里的i代表向后移几位,1的话就是1,后面以此类推),所以*(a+i)=a[i]
到这里我觉得你应该明白了,i代表行,j就代表列
加i就是上下移动,就是移行,erj就是左右移动了
不知道这样说你明不明白
这都是根据我自己做题悟出来的,希望能帮到你
vikiliu0310 2012-05-03
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 的回复:]

引用 14 楼 的回复:

对于二维数组我是这么理解的,数组里面又存储的了一个数组

正解,例如一个int a[3][3]
a其实就是a[0]的地址
a+1就是a[1]的地址
a+2就是a[2]的地址

*(a+1)就是a[1][0]的地址
*(a+1)+1就是a[1][1]的地址
*(*(a+1)+1)就是a[1][1]的值
[/Quote]
++
搞清楚数组中第一个元素,跟数组名就是首地址,然后二维数组就很好理解了
例如a 就是a[0][0]的地址
那么*(a+i)等价于a[i][0]的地址
然后*(a+i)+j等价于 a[i][0+j]
Hammer42 2012-05-03
  • 打赏
  • 举报
回复
个人认为用动态存储开辟数组的例子是很有助于理解的,还有在二维数组a[i][j]中数组名代表第一个元素的地址即a[0][0],但是移动一下就会跳过j个元素大小的空间,其实a,a[0]都是指针,但是移动一下移动的空间不一样a[0]在二维数组的一维数组中移动,a在一维数组间移动
pathuang68 2012-05-03
  • 打赏
  • 举报
回复

#include <iostream>
using namespace std;

int main(int argc, char** argv)
{
int a[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 1. 如果要把上面的数组,用指针的眼光来看待的话,那么a是一个二级指针,
// 那么*a是一个一级指针,*(*a)才是数组里面的int值
// 2. *a或者*(a + 0)就是一维数组{1, 2, 3, 4}的首地址,其中的2的地址就是在该一维数组的首地址上+1,即*(a + 0) + 1
// *(a + 1)就是一维数组{5, 6, 7, 8}的首地址,其中的8的地址就是在该一维数组的首地址上+3,即*(a + 1) + 3
// *(a + 2)就是一维数组{9, 10, 11, 12}的首地址,其中的11的地址就是在该一维数组的首地址上+2,即*(a + 2) + 2
cout << *(*(a + 0) + 1) << endl; // 输出2
cout << *(*(a + 1) + 3) << endl; // 输出8
cout << *(*(a + 2) + 2) << endl; // 输出11

// 事实上,你可以用1级指针来解决多级指针的问题,如下面代码:
int* b = *a;
cout << *(b + 1) << endl; // 输出2
cout << *(b + 1 * 4 + 3) << endl; // 输出8,其中的4就是第二维的长度
cout << *(b + 2 * sizeof(a[0])/sizeof(int) + 2) << endl; // 输出11,也可以用这种方式求出第二维的长度

return 0;
}
赵4老师 2012-05-03
  • 打赏
  • 举报
回复
VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。
对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的时候。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
想要从本质上理解C指针,必须学习汇编以及C和汇编的对应关系。
从汇编的角度理解和学习C语言的指针,原本看似复杂的东西就会变得非常简单!
指针即地址。“地址又是啥?”“只能从汇编语言和计算机组成原理的角度去解释了。”

提醒:
“学习用汇编语言写程序”

“VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
想要从本质上理解C指针,必须学习C和汇编的对应关系。”
不是一回事!

不要迷信书、考题、老师、回帖;
要迷信CPU、编译器、调试器、运行结果。
并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。
任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!

不要写连自己也预测不了结果的代码!
henut 2012-05-03
  • 打赏
  • 举报
回复
楼主 我想这个应该是C编译器默认的编译规律吧

void main()
{
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int b=3;
int *n;
int *m;
int *p;
n=*(a+1);
m=*a+1;
p=*(a+1)+2;
}

vc反汇编的内容:

10: n=*(a+1);(n指向5)
0040D7D6 8D 45 E0 lea eax,[ebp-20h]
0040D7D9 89 45 C8 mov dword ptr [ebp-38h],eax
11: m=*a+1;(m指向2)
0040D7DC 8D 4D D4 lea ecx,[ebp-2Ch]
0040D7DF 89 4D C4 mov dword ptr [ebp-3Ch],ecx
12: p=*(a+1)+2;(p指向7)
0040D7E2 8D 55 E8 lea edx,[ebp-18h]
0040D7E5 89 55 C0 mov dword ptr [ebp-40h],edx

在这个过程中并有计算偏移量
所这些偏移植应该是在编译的时候就算好了的
要搞清这个问题恐怕得研究下编译器了
mars_man 2012-05-03
  • 打赏
  • 举报
回复
这个问题如果按照创建动态二维的思路去理解的话会发现更容易理解一点。
elegant87 2012-04-30
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 的回复:]

先从一维数组开始理解
a[i] 地址可以写成 a+i 取里面的内容就是*(a+i)

a[i][j]

*(a+i)+j 是不是就等于 a[i]+j 我们要取里面的内容 是不是就是要 *(a[i]+j) 也就是*(*(a+i)+j)

所以*(a+i)+j 表示的就是二维数组的地址
[/Quote]
正解
pois 2012-04-30
  • 打赏
  • 举报
回复
也可以这样访问

int a[3][3];
int *pi = (int *)a;
pi[i*3+j]就是a[i][j]
lenovo的天空 2012-04-30
  • 打赏
  • 举报
回复
就是这样的吧。。。[Quote=引用 7 楼 的回复:]

先从一维数组开始理解
a[i] 地址可以写成 a+i 取里面的内容就是*(a+i)

a[i][j]

*(a+i)+j 是不是就等于 a[i]+j 我们要取里面的内容 是不是就是要 *(a[i]+j) 也就是*(*(a+i)+j)

所以*(a+i)+j 表示的就是二维数组的地址
[/Quote]
modyaj 2012-04-28
  • 打赏
  • 举报
回复
a代表一栋大楼,a[i][j]就是这栋大楼中i层j号的房间了, 你要是a+i 估计就跳到小区房子的第i栋楼去了,不过你家住在一楼的第一间房间中,别人都会说 你家住在那栋楼 。。。。。明白否。。哈哈
SuperLy 2012-04-28
  • 打赏
  • 举报
回复
int a[i][j]
为什么 *(a+i)不是表示(a+i)地址里面存放的值?

这样理解比较轻松点:
这里的a是个二维数组名,其级别上属于“二维指针”即 int**(注意是级别上相同,不是完全等价),所以,用*取值降低一维后仍然是个指针,而不是值,要让它变成值,需要用两个“*”才行。
CHENGYAOJUN 2012-04-28
  • 打赏
  • 举报
回复
其实根本就不存在二维数组,从内存的角度来说都是一维的,如
1 2 3
4 5 6
7 8 9
也就是由三个一维数组组成,分别是a[0] = { 1,2,3},a[1] = {4,5,6},a[2] = {7,8,9}

由于数组名是指针常量,所以a + i就表示说指针移动i个位置,也就相当于a[i],

*(a + i)也就是指向a[i]这个一维数组的第一个元素的地址,再加上j(即:*(a+i)+j)即使说指针在移动j个位置

这样也就可以表示数组a在第i行第j列的地址(也就是a[i][j])

fanm2345 2012-04-28
  • 打赏
  • 举报
回复
a是指向第0行的指针,a+i是指向第i行的指针,*(a+i)则是指向第i行0列的指针*(a+i)+j即使指向第i行j列的元素的指针,楼主需要弄清楚二维数组中指向行的指针和指向列的指针,就容易明白了
wei_always 2012-04-27
  • 打赏
  • 举报
回复
个人认为!不是在编译原理的层面上理解的话 可以有不同的理解方式,我理解的方式是这样:把一个一维数组①的每个元素看成一个一维数组②,既然数组②是数组①的元素,必然要有操作每个元素的能力,那就有了数组指针(行指针)了,而数组②也要有处理它的元素,那就有了真正的元素指针。行指针和其第一个元素的的地址属性是相同的,只是类型属性不同。*行指针——元素指针,*元素指针——元素。纠正下a[i]是一个指针 ,不只是一个地址,至于他为什么是指针这是编译器规定的。
wszzfzsc 2012-04-27
  • 打赏
  • 举报
回复
指针这块是比较难,还是多看书练习!
dic_008 2012-04-23
  • 打赏
  • 举报
回复
对于二维数组我是这么理解的,数组里面又存储的了一个数组
yuhao113 2012-04-23
  • 打赏
  • 举报
回复
如果不是动态生成的二维数组,那么可以这么理解。
不管是一维数组还是二维数组,都是一块连续的内存空间,计算机都是不会区分几行几列的,所谓的几行几列都是我们思维的模式。这里二维数组的好处就是能够一定程度上接近我们的思维模式,我们可以定义几行几列。我们把每一行想象成一个维度上的一个数组,每一小数组的地址想象成另一个维度上的数组就清楚了。例如,对于一个3*3的二维数组,其实也是一个大小为9的一维数组,只不过每3个数又可以组成一个小数组,我们可以通过每个小数组的地址快速找到我们需要的数(a[1][2]),也可以通过(int *p = a;p = p+1*3+2)来获得。
lz的a+i是在另一维度中的a+i,这个维度中的数组每一个都是一个小数组的地址,你+1就是取下一个小数组的地址,+i就是取下i个小数组的地址。

如果是动态生成的二维数组,那么每个小数组还是一样的连续空间,只不过另一个维度的数组内保存的就是这些小数组的地址,这些小数组是零散地分布在内存中,但是每个小数组还是保持连续的。
加载更多回复(16)

70,029

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧