二维数组例题

amydog 2010-08-26 12:13:06
int i,j,a[4][2];
for(i=0;i<4;i++)
{
for(j=0;j<2;j++)
a[i][j]=i+j;
}
printf("%d",a[i][j]); //此时结果为2 我的问题是为什么是2 不是4(i=3,j=1,)
//下面为本人补充打印
printf("%p %p %d %d %d %p %p %p %p %p %p",&i,&j,i,j,a[i][j],&a[i][j],&a[3][1],&a[3][2],&[4][0],&a[4][1],&a[4][2]);
OK 题非常简单
补充打印结果为
&i=0012ff64
&j=0012ff58
i =4
j =2
a[i][j]=2
&a[i][j]=0012ff58
&a[3][1]=0012ff4c
&a[3][2]=0012ff50
&a[4][0]=0012ff50
&a[4][1]=0012ff54
&a[4][2]=0012ff58
答案大家也看到了

问题大家应该也看到了

从内存地址上来看,j a[4][2] a[i][j]是一样的

http://topic.csdn.net/u/20100818/19/d2feae9d-73db-44e1-8b31-431ea12e3e09.html?seed=735046337&r=67940727#r_67940727

这个帖子也是我发的,分数也是50分,目前还没结贴,大家可以继续跟帖,我这2天就结贴了

其中7楼的答复是

//****** 如果是在循环外打印的话,因为此时i=4 j=2,对于数组来说刚好出了有效范围,所以应该是溢出了,如果仔细分析下内存布局,估计正好溢出到j变量,所以是2

但是7楼没解释为什么溢出到j变量


在这里望各位大仙赐教
...全文
352 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
amydog 2010-08-31
  • 打赏
  • 举报
回复
谢谢各位大仙的耐心指导,虽然还是没看明白,毕竟你们说的溢出还没学到
visayafan 2010-08-26
  • 打赏
  • 举报
回复
for(j=0;j<2;j++)
分析一下它的执行过程:
j=0,j<2满足,执行后j自增,
j=1,j<2满足,执行后j自增,
j=2,j<2不满足,退出
所以最后j=2
同理i=4
a[4][2]这个数不在数组范围内,是随机数,你说在你的机子上是2,但在我的机子上是1
子庚 2010-08-26
  • 打赏
  • 举报
回复
在我的机器上那个越界位置打印出来的值不是2.可以和具体的编译器也有关系吧..
子庚 2010-08-26
  • 打赏
  • 举报
回复
int i,j,a[4][2];
for(i=0;i<4;i++)
{
for(j=0;j<2;j++)
a[i][j]=i+j;
}
printf("%d",a[i][j]); //此时结果为2 我的问题是为什么是2 不是4(i=3,j=1,)
lz看看你print语句的位置..当循环结束后.i 的值为 4 j的值为 2 你打印的是a[4][2]的值而这个位置是未定义的...

如果你改一下int i,j,a[4][2];
for(i=0;i<4;i++)
{
for(j=0;j<2;j++)
{
a[i][j]=i+j;
printf("%d",a[i][j]);
}
}

这下就正常了..请lz编程一定认真.

amydog 2010-08-26
  • 打赏
  • 举报
回复
请不要用天马行空的答案让我再次问你,如果你能从数组和指针为出发点能讲清楚就不要用貌似很深奥的完全无意义的回答,你懂,但是我不懂,我要的答案是我现在所学到知识点力所能及的理解 谢谢
lyrzhlgq 2010-08-26
  • 打赏
  • 举报
回复
刚刚在linux下调试了一下。gcc编译器确实也是将j和a[][]中保留了2个int的空间,不知道这是为什么。其实你用sizeof(a)的时候,他是32个字节,就是8个int,应该是0-3 x 0-1一共8个。但是编译器留足了0-4 x 0-1的空间。所以,配合我上边说的,最后a[4][2]就落到了他前一个分配空间j上。而至于你i和j的地址,我确实搞不懂了,除非你给出的数据有错误,否则我解释不了,我一直觉得这两个应该之差一个int,而且我调试也是这样。
另外,9楼。我认为你说的 栈是向下生长的 有点太武断了。这个应该是不确定的。要不很多比较严谨的程序上来会判断当前系统的栈的生长方向,尤其是系统及的代码。
实际上,我上边所说的判断栈的方法也有问题,那样判断并不准确。有可能会被编译器优化掉。
libinfei8848 2010-08-26
  • 打赏
  • 举报
回复

int i,j,a[4][2];

//你事先把a[4][2]赋值或者事先打印出其值
a[4][2] = 100;
for(i=0;i<4;i++)
{
for(j=0;j<2;j++)
a[i][j]=i+j;
}
//再看下值
svtanto 2010-08-26
  • 打赏
  • 举报
回复
我再回你一帖,分析下为什么会溢出,希望对你有帮助。
C代码如下:

#include <stdio.h>

#define N 10

int main()
{
int idx;
int a[N];
// 这里的循环出口为什么是N+2,这个是我在VS2010下面调试出来的,其他的编译器不一定是这样
for (idx = 0; idx <= N + 2; ++idx) {
a[idx] = 0;
printf("idx=%d\t", idx);
}
printf("run at end...\n");
getchar();
return 0;
}

该C代码运行时候(Debug模式)的反汇编代码是:

int main()
{
00831390 push ebp
00831391 mov ebp,esp
00831393 sub esp,100h ;注意这里,VS一次性的分配了256个栈地址用于临时变量,
;这个数字是怎么算出来的,我不是很清楚,就是很大,多余局部变量用的空间,
;我们的代码是1个int和10个int,共需要44个字节
00831399 push ebx
0083139A push esi
0083139B push edi
0083139C lea edi,[ebp-100h]
008313A2 mov ecx,40h
008313A7 mov eax,0CCCCCCCCh
008313AC rep stos dword ptr es:[edi]
008313AE mov eax,dword ptr [___security_cookie (837000h)]
008313B3 xor eax,ebp
008313B5 mov dword ptr [ebp-4],eax
int idx;
int a[N];
for (idx = 0; idx <= N + 2; ++idx) {
008313B8 mov dword ptr [ebp-0Ch],0 ;注意这里,意味着idx的地址是ebp-0Ch,
;如果在循环中能把这个地址写入0,就会一直循环下去
008313BF jmp main+3Ah (8313CAh)
008313C1 mov eax,dword ptr [ebp-0Ch]
008313C4 add eax,1
008313C7 mov dword ptr [ebp-0Ch],eax
008313CA cmp dword ptr [ebp-0Ch],0Ch
008313CE jg main+68h (8313F8h)
a[idx] = 0;
008313D0 mov eax,dword ptr [ebp-0Ch]
008313D3 mov dword ptr [ebp+eax*4-3Ch],0 ;注意这里,如果eax(也就是idx)等于0,
;其实就是数组a的地址,所以a的地址是 ebp + 0*4 - 3Ch,就是ebp-3Ch,
;这是数组的偏移量为0的元素,如果是偏移量为10的元素,地址就是ebp-3C+4*10,
;可以推出,偏移量是12的元素(其实已经出了数组了,但是栈地址仍然是可以访问的),
;地址是ebp-3C + 4*12,也就是ebp-12,也就是ebp-0Ch,也就是idx,
;所以这个例子证明是可以溢出的,但是编译器不同,溢出地址不同,
;linux下面变量的定义是紧紧挨在一起的,所以数组的最后一个不能
;写的元素正好是另一个局部变量的地址,有条件可以在linux下
;反编译验证这一点
printf("idx=%d\t", idx);
008313DB mov esi,esp
008313DD mov eax,dword ptr [ebp-0Ch]
008313E0 push eax
008313E1 push offset string "idx=%d\t" (835750h)
008313E6 call dword ptr [__imp__printf (8382B4h)]
008313EC add esp,8
008313EF cmp esi,esp
008313F1 call @ILT+295(__RTC_CheckEsp) (83112Ch)
}
008313F6 jmp main+31h (8313C1h)
printf("run at end...\n");
008313F8 mov esi,esp
008313FA push offset string "run at end...\n" (83573Ch)
008313FF call dword ptr [__imp__printf (8382B4h)]
00831405 add esp,4
00831408 cmp esi,esp
0083140A call @ILT+295(__RTC_CheckEsp) (83112Ch)
getchar();
0083140F mov esi,esp
00831411 call dword ptr [__imp__getchar (8382B8h)]
00831417 cmp esi,esp
00831419 call @ILT+295(__RTC_CheckEsp) (83112Ch)
return 0;
0083141E xor eax,eax
}
00831420 push edx
00831421 mov ecx,ebp
00831423 push eax
00831424 lea edx,[ (831450h)]
0083142A call @ILT+120(@_RTC_CheckStackVars@8) (83107Dh)
0083142F pop eax
00831430 pop edx
00831431 pop edi
00831432 pop esi
00831433 pop ebx
00831434 mov ecx,dword ptr [ebp-4]
00831437 xor ecx,ebp
00831439 call @ILT+15(@__security_check_cookie@4) (831014h)
0083143E add esp,100h
00831444 cmp ebp,esp
00831446 call @ILT+295(__RTC_CheckEsp) (83112Ch)
0083144B mov esp,ebp
0083144D pop ebp
0083144E ret
svtanto 2010-08-26
  • 打赏
  • 举报
回复
这个题目还在搞?
关于内存布局的问题一下子讲清楚我还做不到,以下供参考:
函数调用时候的参数和局部变量是存在栈上的,栈是向下生长的。这些变量的存放可能是挨在一起的,就是说变量j紧接着二维数组a,但是二维数组a的最后一个元素是可以读不可以写的,所以是不分配内存的,所以访问这个位置其实是访问了变量j。具体的这些内容可能依赖于编译器,就是说不一定在每个编译器上都会溢出,据我所知gcc和cc编译时候的内存布局就不同,一定要研究这个现象,需要反编译函数开始的部分,逐个计算下局部变量的内存位置就明白了。
lyrzhlgq 2010-08-26
  • 打赏
  • 举报
回复
我理解是这样的。你在栈空间申请的变量i j a[][],从你给出的地址上看,明显栈空间是向上延伸。理论上编译器会将在栈中申请的空间连续分配,这样,应该是这3个变量在栈空间中地址连续分配,而从你的地址上看,a数组的空间是低下标低地址,这样如果a[4][2]应该是溢出了自己的空间,但是对于C语言来说,它都是地址,所以如果不出现page_fault的情况下是允许访问的。而访问的地址则落到了i或者j这边。
不过,我有一点不太明白,lz你确定这些数据都是对的吗?我手上现在没有环境,没有方法汇编你的程序,我认为i j a应该是连续分配空间,不过该差这么多
&i=0012ff64
&j=0012ff58
这两个变量差的有点多,而且怎么感觉不零不整的。
另外,如果a溢出,a[4][2]应该不是溢出1个int,而是3个int才对。所以说,如果a[4][2]真的溢出地址到了j,那么你的程序在j和a之间应该还定义了两个int变量才对。
等到我手上有环境的时候,我看看这到底是怎么回事。
canican 2010-08-26
  • 打赏
  • 举报
回复
我的机子上是1245120,你“跑到别人家”还问开门的“为什么是你”,
wibnmo 2010-08-26
  • 打赏
  • 举报
回复
不知道问题解决了没有
qazwhl845174869 2010-08-26
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 snowwhite1 的回复:]
lz学的真认真。当内存越界时,具体答案和内存中原来存放的值有关,也就是不同机器上运行结果都不同。另外,变量的存放顺序也没有硬性规定,是不是有事实上的标准我不清楚。
[/Quote]
正解!
flysnowhite 2010-08-26
  • 打赏
  • 举报
回复
lz学的真认真。当内存越界时,具体答案和内存中原来存放的值有关,也就是不同机器上运行结果都不同。另外,变量的存放顺序也没有硬性规定,是不是有事实上的标准我不清楚。
小枫 2010-08-26
  • 打赏
  • 举报
回复
学习!

69,373

社区成员

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

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