各位帮帮忙,小女子谢谢啊~~~~~~~~~~~~~~~~~~~~

时间:2022-07-08 16:56:07
若有说明int a[4][4],*p=a[0];则对数组元素a[i][j]之值的正确引用是
A *(*(p+i)+j)  B *(p[i]+j)
C p[i*4+j]     D *(a[i]+j)

求解释,~~~~(>_<)~~~~ 

57 个解决方案

#1


不做强转,题目编译能过?

#2


你可以编个程序测试下。

#3


引用 1 楼 qq120848369 的回复:
不做强转,题目编译能过?

答案选C,这是为什么?还有D为什么不行呢?不明白

#4


还是用赵老师的话回答你.
VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
想要从本质上理解C指针,必须学习汇编以及C和汇编的对应关系。
从汇编的角度理解和学习C语言的指针,原本看似复杂的东西就会变得非常简单!
指针即地址。“地址又是啥?”“只能从汇编语言和计算机组成原理的角度去解释了。”

#5


楼上是啥?我水平太次,看不懂啊。。。。。。

#6


指针的东西  复杂的我一般画个图。。  各位帮帮忙,小女子谢谢啊~~~~~~~~~~~~~~~~~~~~

#7


有木有人正视一下俺的题啊。。。。太简单了么?你们都不屑回答??

#8


D也是对的,我认为啊

CD 都是对的






引用 3 楼 linnnn2012 的回复:
引用 1 楼 qq120848369 的回复:
不做强转,题目编译能过?

答案选C,这是为什么?还有D为什么不行呢?不明白

#9


a在内存中的状态是按顺序 a[0][0], a[0][1], a[0][2], a[0][3], a[1][0], ..., a[3][3],
a[0]获得的是指向a[0][0]的指针,把它赋值给p后,想得到指向a[i][j]的指针就可以写p + i * 4 + j,获得其内容就是*(p + i * 4 + j)等价于p[i * 4 + j]

#10


D是错的,a[i]+j,实际等于a[i+j]...是不是这样?

#11


引用 9 楼 bluewanderer 的回复:
a在内存中的状态是按顺序 a[0][0], a[0][1], a[0][2], a[0][3], a[1][0], ..., a[3][3],
a[0]获得的是指向a[0][0]的指针,把它赋值给p后,想得到指向a[i][j]的指针就可以写p + i * 4 + j,获得其内容就是*(p + i * 4 + j)等价于p[i * 4 + j]

那D呢,我觉得D也可以啊

#12


D是对的...
建议楼主下次这样的题目直接写程序来试验...

#13


我编译过,cd都可以

#14



a[i]是一个行指针,其值为:第i个一维数组的首元素的地址

然后+j, 即a[i][j]

a[i][j] 反汇编估计是这样的:两步骤:1,定位到第i个一维数组的首元素的地址,2:偏移

所以cd都是对的

不对请指出

另外:楼主可以自行测试。。我没有测试


引用 10 楼 czh3642210 的回复:
D是错的,a[i]+j,实际等于a[i+j]...是不是这样?

#15


我觉得对于这种问题,先在编译器上试下,然后自己有些思考,然后查下书,这样对于知识的理解就会更深

#16


好感动,我明白 了,刚自学C不久,有点迷糊,谢谢各位的指点啊!(*^__^*) 

#17


还是用赵老师的话回答你.
VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
想要从本质上理解C指针,必须学习汇编以及C和汇编的对应关系。
从汇编的角度理解和学习C语言的指针,原本看似复杂的东西就会变得非常简单!
指针即地址。“地址又是啥?”“只能从汇编语言和计算机组成原理的角度去解释了。”

#18


蛋疼的东西~~

#19


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

赵老师是谁呀

#20


引用 14 楼 caddor 的回复:
a[i]是一个行指针,其值为:第i个一维数组的首元素的地址

然后+j, 即a[i][j]

a[i][j] 反汇编估计是这样的:两步骤:1,定位到第i个一维数组的首元素的地址,2:偏移

所以cd都是对的

不对请指出

另外:楼主可以自行测试。。我没有测试


引用 10 楼 czh3642210 的回复:
D是错的,a[i]+j,实际等于a[i+j]...是不是这……

嗯……一时半会没反应过来

#21


学聪明了,,直接把题目写成小女子了。。。。


哈哈哈哈哈哈。。。

目前没看到详细且正确的答案。。。

答案是CD

#22


引用 21 楼 mingliang1212 的回复:
学聪明了,,直接把题目写成小女子了。。。。


哈哈哈哈哈哈。。。

目前没看到详细且正确的答案。。。

答案是CD

哈哈,那你有何高见呐?(*^__^*) 

#23


是否可以认为p=&a[0][0]?

#24


纠正一下,我犯了错误,还是基础不扎实

a[i]不是行指针,是数组名,

在表达式汇中,自动转位a[i]的首元素的地址

对于&a[i]才是获得行指针




引用 14 楼 caddor 的回复:
a[i]是一个行指针,其值为:第i个一维数组的首元素的地址

然后+j, 即a[i][j]

a[i][j] 反汇编估计是这样的:两步骤:1,定位到第i个一维数组的首元素的地址,2:偏移

所以cd都是对的

不对请指出

另外:楼主可以自行测试。。我没有测试


引用 10 楼 czh3642210 的回复:
D是错的,a[i]+j,实际等于a[i+j]...是不是这……

#25


a[i]等价&a[i][0],*(a[i] + j)就是*(&a[i][0] + j)也就是a[i][j]

数组除了取地址和取长度运算都是自动转指针的

#26



a[i]就是数组名



引用 25 楼 bluewanderer 的回复:
a[i]等价&amp;a[i][0],*(a[i] + j)就是*(&amp;a[i][0] + j)也就是a[i][j]

数组除了取地址和取长度运算都是自动转指针的

#27


引用 26 楼 caddor 的回复:
a[i]就是数组名


那我得告诉你,下标运算只能作用于指针(因为是*的等价运算)... 如果a[i]不能给出一个指针右值a[i][0]就是非法的。

#28


不知道你想表达什么,大概猜测:你是说a[i] 不是数组名???


如果不是数组名,请问他是什么???


int a[3][4];


int(*p)[4]=&a[0];

你试试这代码,对不对,我的vs2010是对的


再比如:

int a[]={1,35,4356,90};

int(*p)[4]=&a;

a[0][1],它的值是35




引用 27 楼 bluewanderer 的回复:
引用 26 楼 caddor 的回复:
a[i]就是数组名


那我得告诉你,下标运算只能作用于指针(因为是*的等价运算)... 如果a[i]不能给出一个指针右值a[i][0]就是非法的。

#29


其实我很想吐槽“数组名到底是个什么玩意”... 表达式里其实只有左右值

#include <stdio.h>

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

int main()
{
int *p0 = a[0];
int (*p1)[2] = &a[0];

printf("%d %d\n", p0[3], p1[1][1]);
}

#30


注意上面的a[0]和&a[0]地址一样但是类型不一样,但参与运算的时候都是指针。

#31


a[i]不是数组名。它是一个表达式,这个表达式的类型是数组类型。


#32


表示学习了,C语言的指针已经有段时间没碰了。现在学C#的委托,这个类似于指针吧。

#33


比如用二维的字符数组存放多个字符串:
char s[3][40]={"Hello","How are you?","I'm fine, thank you."};

那么 cout<<s[0];  显示的是 Hello
     cout<<s[1];  显示的是 How are you?
     cout<<s[2];  显示的是 I'm fine, thank you.

说明 s[0]、s[1]、s[2]都是一个字符指针,即 char*,

s[0] 指向Hello中的字符“H”
s[1] 指向How中的字符“H”
s[2] 指向字符“I”

#34


引用 29 楼 bluewanderer 的回复:
其实我很想吐槽“数组名到底是个什么玩意”... 表达式里其实只有左右值

C/C++ code
#include <stdio.h>

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

int main()
{
    int *p0 = a[0];
    int (*p1)[2] = &amp;a[0];

    printf("%d %d\n", p0[3], p1[1][……

左值和右值的定义和区别,能解释一下吗?

#35


A *(*(p+i)+j) 
B *(p[i]+j)
C p[i*4+j] 
D *(a[i]+j)

因为 *(p+i) 相当于 p[i],所以A\B是一样的。因为*(p+i)+j的结果是整型数据,所以再运用取内容运算符*是错误的。A B 都是错误的。

因为数组在内存中按行连续存储的,所以也可以按一维数组的形式来访问,所以C是对的

把a[i]看成p,选项D就变成了*(p+j),可转换成p[j],再把p换成a[i],就是a[i][j],所以D也是对的。

#36


严格来说只有D是对的,详细可以参考《C陷阱与缺陷》第三章3.1数组与指针45页。
由于未声明是32位系统故严格来说C不正确,中的i*4,4是32位系统int所占字节。

#37


答案肯定选D

C这个答案严格来说有局限性,牵扯到系统和编译器。 其只对32位程序成立。

#38


CD  正解
无法解释  请调试

#39


引用 25 楼 bluewanderer 的回复:
a[i]等价&amp;a[i][0],*(a[i] + j)就是*(&amp;a[i][0] + j)也就是a[i][j]

数组除了取地址和取长度运算都是自动转指针的

++

#40


标准答案:D。
原因楼上的多说了。。

#41


答案c,不完全对。这里默认int 为4字节。

#42


答案C。不完全对int 这里默认是4字节。。。

#43


引用 36 楼 gdujian0119 的回复:
严格来说只有D是对的,详细可以参考《C陷阱与缺陷》第三章3.1数组与指针45页。
由于未声明是32位系统故严格来说C不正确,中的i*4,4是32位系统int所占字节。


C和D都是正确的,但是C中的i*4这个4并不是代表4字节int型,而是表示a[4][4]列元素大小。
int *p=a[0]是将a的第0行的首地址赋于指针p,等效于p = &a[0][0]和p = a;
p[i*4+j]是p指向的地址偏移i*4+j个元素的值,它和a[i*4+j]是一样的。
*(a[i]+j) 其中a[i]+j是第i行的首地址加上列偏移量j,也就是i*4+j的地址,*(a[i]+j)就是这个地址的值。

#44


小女子的问题很快就有人答了,哎,世道啊....

#45


引用 36 楼 gdujian0119 的回复:
严格来说只有D是对的,详细可以参考《C陷阱与缺陷》第三章3.1数组与指针45页。
由于未声明是32位系统故严格来说C不正确,中的i*4,4是32位系统int所占字节。

我不同意“中的i*4,4是32位系统int所占字节”,这里的4指列数的4,跟机器无关,只与数组的列有关,若数组a为5列,则a[i][j] = p[i*5 + j]

#46


引用 1 楼 qq120848369 的回复:
不做强转,题目编译能过?

我也是这么想的, p与a[0]的类型是不一样的, 用C++中的typeid可以明确知道的
但用code::blocks(gcc4.6.1)居然能编译通过, 真扯!
引用 35 楼 sxldfang 的回复:
A *(*(p+i)+j)  
B *(p[i]+j)
C p[i*4+j]  
D *(a[i]+j)
因为 *(p+i) 相当于 p[i],所以A\B是一样的。因为*(p+i)+j的结果是整型数据,所以再运用取内容运算符*是错误的。A B 都是错误的。
因为数组在内存中按行连续存储的,所以也可以按一维数组的形式来访问,所以C是对的
把a[i]看成p,选项D就变成了*(p+j),可转换成p[j],再把p换成a[i],就是a[i][j],所以D也是对的。

楼主请先看上面这个分析
引用 36 楼 gdujian0119 的回复:
严格来说只有D是对的,详细可以参考《C陷阱与缺陷》第三章3.1数组与指针45页。
由于未声明是32位系统故严格来说C不正确,中的i*4,4是32位系统int所占字节。

再看上面这个说明(其实我也才听说有这么回事)


如果题目是这样:
int a[4][4];
int (*p)[4] = a;
那么我和1楼就不会说题目编译不过了, 而且这时的答案为ABD

#47


如果你觉得我说得对
请给1楼, 35楼, 36楼多给点分, 也记得给我1分, 这是我帮你总结的苦劳分

#48


引用 36 楼 gdujian0119 的回复:
严格来说只有D是对的,详细可以参考《C陷阱与缺陷》第三章3.1数组与指针45页。
由于未声明是32位系统故严格来说C不正确, 中的i*4,4是32位系统int所占字节

zhao4zhong1:
VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
想要从本质上理解C指针,必须学习汇编以及C和汇编的对应关系。
从汇编的角度理解和学习C语言的指针,原本看似复杂的东西就会变得非常简单!
指针即地址。“地址又是啥?”“只能从汇编语言和计算机组成原理的角度去解释了。”

#49


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、编译器、调试器、运行结果。
并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。
任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!
1:    int a[4][4];
2:    int *p=a[0];
3:    int i=2;j=3,v;
4:    void main() {
//0401000 55                   push        ebp
//0401001 8B EC                mov         ebp,esp
//0401003 83 EC 40             sub         esp,40h
//0401006 53                   push        ebx
//0401007 56                   push        esi
//0401008 57                   push        edi
5:          v=a[i][j];
//0401009 A1 34 E0 40 00       mov         eax,[i (0040e034)]
//040100E C1 E0 04             shl         eax,4
//0401011 8B 0D 38 E0 40 00    mov         ecx,dword ptr [j (0040e038)]
//0401017 8B 94 88 00 EB 40 00 mov         edx,dword ptr [eax+ecx*4+40EB00h]
//040101E 89 15 40 EB 40 00    mov         dword ptr [v (0040eb40)],edx
6:        //v=*(*(p+i)+j);//A error C2100: illegal indirec
7:        //v=*(p[i]+j);  //B error C2100: illegal indirec
8:          v=p[i*4+j];   //C
//0401024 A1 34 E0 40 00       mov         eax,[i (0040e034)]
//0401029 8B 0D 38 E0 40 00    mov         ecx,dword ptr [j (0040e038)]
//040102F 8D 14 81             lea         edx,[ecx+eax*4]
//0401032 A1 30 E0 40 00       mov         eax,[p (0040e030)]
//0401037 8B 0C 90             mov         ecx,dword ptr [eax+edx*4]
//040103A 89 0D 40 EB 40 00    mov         dword ptr [v (0040eb40)],ecx
9:          v=*(a[i]+j);  //D
//0401040 8B 15 34 E0 40 00    mov         edx,dword ptr [i (0040e034)]
//0401046 C1 E2 04             shl         edx,4
//0401049 A1 38 E0 40 00       mov         eax,[j (0040e038)]
//040104E 8B 8C 82 00 EB 40 00 mov         ecx,dword ptr [edx+eax*4+40EB00h]
//0401055 89 0D 40 EB 40 00    mov         dword ptr [v (0040eb40)],ecx
10:   }

#50


现行国内教程中有大量这样的题目,难为了许多人。
现行国内教程又说不清数组与指针的关系,这样的题目真的就成为学生的拦路虎了。

我来说一下:
基本概念
1.数组名是&数组名[0]的缩写(不是所有场合下,此处不深入说),这转化成一种纸上演算:数组名====  &数组名 [0]
2.*与&是一对逆操作:&--取地址、*--按地址取值,所以有*&对象====对象,&*地址====地址
3.地址前的*与地址(数组名)后[0] 存在互换关系

下面使用这些概念解释LZ的问题:
int a[4][4],*p=a[0];
A *(*(p+i)+j) 
B *(p[i]+j)
C p[i*4+j] 
D *(a[i]+j)
==================
第一行:a是二维数组,四行四列; p指向了 &a[0]  [0]--是元素的地址
第二行A:p+i是地址+偏移--仍是地址,*(p+i)是取地址的值--即int数值,(*(p+i)+j)是数值,*(*(p+i)+j)则错了--不能*数值
第三行B:同A--p[i]是*(p+i)另一表述法(用上边的概念可以推演出来)
第四行C:i*4是指第 i行前共有的元素数量,j是指第i行中第j列元素(即a[i][j])前元素数量,则i*4+j是a[i][j]这个元素前的元素个数,p[i*4+j]即这个元素的值
!!!!LS有人将4理解为32位系统中sizeof(int)值,有误----这个4是“四列”
第五行D:*(a[i]+j)====*(&a[i][0]+j)====*(&a[i][j])====*&a[i][j]====a[i][j]

指针(地址)表达式是C语言的脊梁骨,立则C立




#1


不做强转,题目编译能过?

#2


你可以编个程序测试下。

#3


引用 1 楼 qq120848369 的回复:
不做强转,题目编译能过?

答案选C,这是为什么?还有D为什么不行呢?不明白

#4


还是用赵老师的话回答你.
VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
想要从本质上理解C指针,必须学习汇编以及C和汇编的对应关系。
从汇编的角度理解和学习C语言的指针,原本看似复杂的东西就会变得非常简单!
指针即地址。“地址又是啥?”“只能从汇编语言和计算机组成原理的角度去解释了。”

#5


楼上是啥?我水平太次,看不懂啊。。。。。。

#6


指针的东西  复杂的我一般画个图。。  各位帮帮忙,小女子谢谢啊~~~~~~~~~~~~~~~~~~~~

#7


有木有人正视一下俺的题啊。。。。太简单了么?你们都不屑回答??

#8


D也是对的,我认为啊

CD 都是对的






引用 3 楼 linnnn2012 的回复:
引用 1 楼 qq120848369 的回复:
不做强转,题目编译能过?

答案选C,这是为什么?还有D为什么不行呢?不明白

#9


a在内存中的状态是按顺序 a[0][0], a[0][1], a[0][2], a[0][3], a[1][0], ..., a[3][3],
a[0]获得的是指向a[0][0]的指针,把它赋值给p后,想得到指向a[i][j]的指针就可以写p + i * 4 + j,获得其内容就是*(p + i * 4 + j)等价于p[i * 4 + j]

#10


D是错的,a[i]+j,实际等于a[i+j]...是不是这样?

#11


引用 9 楼 bluewanderer 的回复:
a在内存中的状态是按顺序 a[0][0], a[0][1], a[0][2], a[0][3], a[1][0], ..., a[3][3],
a[0]获得的是指向a[0][0]的指针,把它赋值给p后,想得到指向a[i][j]的指针就可以写p + i * 4 + j,获得其内容就是*(p + i * 4 + j)等价于p[i * 4 + j]

那D呢,我觉得D也可以啊

#12


D是对的...
建议楼主下次这样的题目直接写程序来试验...

#13


我编译过,cd都可以

#14



a[i]是一个行指针,其值为:第i个一维数组的首元素的地址

然后+j, 即a[i][j]

a[i][j] 反汇编估计是这样的:两步骤:1,定位到第i个一维数组的首元素的地址,2:偏移

所以cd都是对的

不对请指出

另外:楼主可以自行测试。。我没有测试


引用 10 楼 czh3642210 的回复:
D是错的,a[i]+j,实际等于a[i+j]...是不是这样?

#15


我觉得对于这种问题,先在编译器上试下,然后自己有些思考,然后查下书,这样对于知识的理解就会更深

#16


好感动,我明白 了,刚自学C不久,有点迷糊,谢谢各位的指点啊!(*^__^*) 

#17


还是用赵老师的话回答你.
VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
想要从本质上理解C指针,必须学习汇编以及C和汇编的对应关系。
从汇编的角度理解和学习C语言的指针,原本看似复杂的东西就会变得非常简单!
指针即地址。“地址又是啥?”“只能从汇编语言和计算机组成原理的角度去解释了。”

#18


蛋疼的东西~~

#19


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

赵老师是谁呀

#20


引用 14 楼 caddor 的回复:
a[i]是一个行指针,其值为:第i个一维数组的首元素的地址

然后+j, 即a[i][j]

a[i][j] 反汇编估计是这样的:两步骤:1,定位到第i个一维数组的首元素的地址,2:偏移

所以cd都是对的

不对请指出

另外:楼主可以自行测试。。我没有测试


引用 10 楼 czh3642210 的回复:
D是错的,a[i]+j,实际等于a[i+j]...是不是这……

嗯……一时半会没反应过来

#21


学聪明了,,直接把题目写成小女子了。。。。


哈哈哈哈哈哈。。。

目前没看到详细且正确的答案。。。

答案是CD

#22


引用 21 楼 mingliang1212 的回复:
学聪明了,,直接把题目写成小女子了。。。。


哈哈哈哈哈哈。。。

目前没看到详细且正确的答案。。。

答案是CD

哈哈,那你有何高见呐?(*^__^*) 

#23


是否可以认为p=&a[0][0]?

#24


纠正一下,我犯了错误,还是基础不扎实

a[i]不是行指针,是数组名,

在表达式汇中,自动转位a[i]的首元素的地址

对于&a[i]才是获得行指针




引用 14 楼 caddor 的回复:
a[i]是一个行指针,其值为:第i个一维数组的首元素的地址

然后+j, 即a[i][j]

a[i][j] 反汇编估计是这样的:两步骤:1,定位到第i个一维数组的首元素的地址,2:偏移

所以cd都是对的

不对请指出

另外:楼主可以自行测试。。我没有测试


引用 10 楼 czh3642210 的回复:
D是错的,a[i]+j,实际等于a[i+j]...是不是这……

#25


a[i]等价&a[i][0],*(a[i] + j)就是*(&a[i][0] + j)也就是a[i][j]

数组除了取地址和取长度运算都是自动转指针的

#26



a[i]就是数组名



引用 25 楼 bluewanderer 的回复:
a[i]等价&amp;a[i][0],*(a[i] + j)就是*(&amp;a[i][0] + j)也就是a[i][j]

数组除了取地址和取长度运算都是自动转指针的

#27


引用 26 楼 caddor 的回复:
a[i]就是数组名


那我得告诉你,下标运算只能作用于指针(因为是*的等价运算)... 如果a[i]不能给出一个指针右值a[i][0]就是非法的。

#28


不知道你想表达什么,大概猜测:你是说a[i] 不是数组名???


如果不是数组名,请问他是什么???


int a[3][4];


int(*p)[4]=&a[0];

你试试这代码,对不对,我的vs2010是对的


再比如:

int a[]={1,35,4356,90};

int(*p)[4]=&a;

a[0][1],它的值是35




引用 27 楼 bluewanderer 的回复:
引用 26 楼 caddor 的回复:
a[i]就是数组名


那我得告诉你,下标运算只能作用于指针(因为是*的等价运算)... 如果a[i]不能给出一个指针右值a[i][0]就是非法的。

#29


其实我很想吐槽“数组名到底是个什么玩意”... 表达式里其实只有左右值

#include <stdio.h>

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

int main()
{
int *p0 = a[0];
int (*p1)[2] = &a[0];

printf("%d %d\n", p0[3], p1[1][1]);
}

#30


注意上面的a[0]和&a[0]地址一样但是类型不一样,但参与运算的时候都是指针。

#31


a[i]不是数组名。它是一个表达式,这个表达式的类型是数组类型。


#32


表示学习了,C语言的指针已经有段时间没碰了。现在学C#的委托,这个类似于指针吧。

#33


比如用二维的字符数组存放多个字符串:
char s[3][40]={"Hello","How are you?","I'm fine, thank you."};

那么 cout<<s[0];  显示的是 Hello
     cout<<s[1];  显示的是 How are you?
     cout<<s[2];  显示的是 I'm fine, thank you.

说明 s[0]、s[1]、s[2]都是一个字符指针,即 char*,

s[0] 指向Hello中的字符“H”
s[1] 指向How中的字符“H”
s[2] 指向字符“I”

#34


引用 29 楼 bluewanderer 的回复:
其实我很想吐槽“数组名到底是个什么玩意”... 表达式里其实只有左右值

C/C++ code
#include <stdio.h>

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

int main()
{
    int *p0 = a[0];
    int (*p1)[2] = &amp;a[0];

    printf("%d %d\n", p0[3], p1[1][……

左值和右值的定义和区别,能解释一下吗?

#35


A *(*(p+i)+j) 
B *(p[i]+j)
C p[i*4+j] 
D *(a[i]+j)

因为 *(p+i) 相当于 p[i],所以A\B是一样的。因为*(p+i)+j的结果是整型数据,所以再运用取内容运算符*是错误的。A B 都是错误的。

因为数组在内存中按行连续存储的,所以也可以按一维数组的形式来访问,所以C是对的

把a[i]看成p,选项D就变成了*(p+j),可转换成p[j],再把p换成a[i],就是a[i][j],所以D也是对的。

#36


严格来说只有D是对的,详细可以参考《C陷阱与缺陷》第三章3.1数组与指针45页。
由于未声明是32位系统故严格来说C不正确,中的i*4,4是32位系统int所占字节。

#37


答案肯定选D

C这个答案严格来说有局限性,牵扯到系统和编译器。 其只对32位程序成立。

#38


CD  正解
无法解释  请调试

#39


引用 25 楼 bluewanderer 的回复:
a[i]等价&amp;a[i][0],*(a[i] + j)就是*(&amp;a[i][0] + j)也就是a[i][j]

数组除了取地址和取长度运算都是自动转指针的

++

#40


标准答案:D。
原因楼上的多说了。。

#41


答案c,不完全对。这里默认int 为4字节。

#42


答案C。不完全对int 这里默认是4字节。。。

#43


引用 36 楼 gdujian0119 的回复:
严格来说只有D是对的,详细可以参考《C陷阱与缺陷》第三章3.1数组与指针45页。
由于未声明是32位系统故严格来说C不正确,中的i*4,4是32位系统int所占字节。


C和D都是正确的,但是C中的i*4这个4并不是代表4字节int型,而是表示a[4][4]列元素大小。
int *p=a[0]是将a的第0行的首地址赋于指针p,等效于p = &a[0][0]和p = a;
p[i*4+j]是p指向的地址偏移i*4+j个元素的值,它和a[i*4+j]是一样的。
*(a[i]+j) 其中a[i]+j是第i行的首地址加上列偏移量j,也就是i*4+j的地址,*(a[i]+j)就是这个地址的值。

#44


小女子的问题很快就有人答了,哎,世道啊....

#45


引用 36 楼 gdujian0119 的回复:
严格来说只有D是对的,详细可以参考《C陷阱与缺陷》第三章3.1数组与指针45页。
由于未声明是32位系统故严格来说C不正确,中的i*4,4是32位系统int所占字节。

我不同意“中的i*4,4是32位系统int所占字节”,这里的4指列数的4,跟机器无关,只与数组的列有关,若数组a为5列,则a[i][j] = p[i*5 + j]

#46


引用 1 楼 qq120848369 的回复:
不做强转,题目编译能过?

我也是这么想的, p与a[0]的类型是不一样的, 用C++中的typeid可以明确知道的
但用code::blocks(gcc4.6.1)居然能编译通过, 真扯!
引用 35 楼 sxldfang 的回复:
A *(*(p+i)+j)  
B *(p[i]+j)
C p[i*4+j]  
D *(a[i]+j)
因为 *(p+i) 相当于 p[i],所以A\B是一样的。因为*(p+i)+j的结果是整型数据,所以再运用取内容运算符*是错误的。A B 都是错误的。
因为数组在内存中按行连续存储的,所以也可以按一维数组的形式来访问,所以C是对的
把a[i]看成p,选项D就变成了*(p+j),可转换成p[j],再把p换成a[i],就是a[i][j],所以D也是对的。

楼主请先看上面这个分析
引用 36 楼 gdujian0119 的回复:
严格来说只有D是对的,详细可以参考《C陷阱与缺陷》第三章3.1数组与指针45页。
由于未声明是32位系统故严格来说C不正确,中的i*4,4是32位系统int所占字节。

再看上面这个说明(其实我也才听说有这么回事)


如果题目是这样:
int a[4][4];
int (*p)[4] = a;
那么我和1楼就不会说题目编译不过了, 而且这时的答案为ABD

#47


如果你觉得我说得对
请给1楼, 35楼, 36楼多给点分, 也记得给我1分, 这是我帮你总结的苦劳分

#48


引用 36 楼 gdujian0119 的回复:
严格来说只有D是对的,详细可以参考《C陷阱与缺陷》第三章3.1数组与指针45页。
由于未声明是32位系统故严格来说C不正确, 中的i*4,4是32位系统int所占字节

zhao4zhong1:
VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
想要从本质上理解C指针,必须学习汇编以及C和汇编的对应关系。
从汇编的角度理解和学习C语言的指针,原本看似复杂的东西就会变得非常简单!
指针即地址。“地址又是啥?”“只能从汇编语言和计算机组成原理的角度去解释了。”

#49


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、编译器、调试器、运行结果。
并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。
任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!
1:    int a[4][4];
2:    int *p=a[0];
3:    int i=2;j=3,v;
4:    void main() {
//0401000 55                   push        ebp
//0401001 8B EC                mov         ebp,esp
//0401003 83 EC 40             sub         esp,40h
//0401006 53                   push        ebx
//0401007 56                   push        esi
//0401008 57                   push        edi
5:          v=a[i][j];
//0401009 A1 34 E0 40 00       mov         eax,[i (0040e034)]
//040100E C1 E0 04             shl         eax,4
//0401011 8B 0D 38 E0 40 00    mov         ecx,dword ptr [j (0040e038)]
//0401017 8B 94 88 00 EB 40 00 mov         edx,dword ptr [eax+ecx*4+40EB00h]
//040101E 89 15 40 EB 40 00    mov         dword ptr [v (0040eb40)],edx
6:        //v=*(*(p+i)+j);//A error C2100: illegal indirec
7:        //v=*(p[i]+j);  //B error C2100: illegal indirec
8:          v=p[i*4+j];   //C
//0401024 A1 34 E0 40 00       mov         eax,[i (0040e034)]
//0401029 8B 0D 38 E0 40 00    mov         ecx,dword ptr [j (0040e038)]
//040102F 8D 14 81             lea         edx,[ecx+eax*4]
//0401032 A1 30 E0 40 00       mov         eax,[p (0040e030)]
//0401037 8B 0C 90             mov         ecx,dword ptr [eax+edx*4]
//040103A 89 0D 40 EB 40 00    mov         dword ptr [v (0040eb40)],ecx
9:          v=*(a[i]+j);  //D
//0401040 8B 15 34 E0 40 00    mov         edx,dword ptr [i (0040e034)]
//0401046 C1 E2 04             shl         edx,4
//0401049 A1 38 E0 40 00       mov         eax,[j (0040e038)]
//040104E 8B 8C 82 00 EB 40 00 mov         ecx,dword ptr [edx+eax*4+40EB00h]
//0401055 89 0D 40 EB 40 00    mov         dword ptr [v (0040eb40)],ecx
10:   }

#50


现行国内教程中有大量这样的题目,难为了许多人。
现行国内教程又说不清数组与指针的关系,这样的题目真的就成为学生的拦路虎了。

我来说一下:
基本概念
1.数组名是&数组名[0]的缩写(不是所有场合下,此处不深入说),这转化成一种纸上演算:数组名====  &数组名 [0]
2.*与&是一对逆操作:&--取地址、*--按地址取值,所以有*&对象====对象,&*地址====地址
3.地址前的*与地址(数组名)后[0] 存在互换关系

下面使用这些概念解释LZ的问题:
int a[4][4],*p=a[0];
A *(*(p+i)+j) 
B *(p[i]+j)
C p[i*4+j] 
D *(a[i]+j)
==================
第一行:a是二维数组,四行四列; p指向了 &a[0]  [0]--是元素的地址
第二行A:p+i是地址+偏移--仍是地址,*(p+i)是取地址的值--即int数值,(*(p+i)+j)是数值,*(*(p+i)+j)则错了--不能*数值
第三行B:同A--p[i]是*(p+i)另一表述法(用上边的概念可以推演出来)
第四行C:i*4是指第 i行前共有的元素数量,j是指第i行中第j列元素(即a[i][j])前元素数量,则i*4+j是a[i][j]这个元素前的元素个数,p[i*4+j]即这个元素的值
!!!!LS有人将4理解为32位系统中sizeof(int)值,有误----这个4是“四列”
第五行D:*(a[i]+j)====*(&a[i][0]+j)====*(&a[i][j])====*&a[i][j]====a[i][j]

指针(地址)表达式是C语言的脊梁骨,立则C立