学习 C 语言的问题,有个问题怎么想都想不通

2015-11-03 21:45:57 +08:00
 ru20150601
一个 2 维的指针, int array [4] [2],教材中说, array 的值和 array[0]的值是一样的,是同一个地址值。这个我不能理解。* array 得到的是一个内存地址值 long int ,而* array[0]则是一个 int 。怎么可能会相同呢?

我本来以为这个是教材的错误,但是这个教材言之凿凿,甚至还特别举例,输出了这两个值的地址证明是相同的。有人能解答下吗?
3790 次点击
所在节点    程序员
35 条回复
inevermore
2015-11-04 00:00:45 +08:00
array 和 array[0]都可以看做指针,指向的内存起始位置相同,所以他们的值是相同的。
但是他们的类型不同。
Reficul
2015-11-04 00:10:30 +08:00
把一个二维数组看成一个矩阵, array 指向整个矩阵, array[0]指向矩阵的第一行。指针指向元素起始位置,那么 array 和 array[0]自然都是指向同一个位置,即元素 array[0][0]的内存地址。
但是仅仅是数值相同,他们的类型是不一样的。指针每+1 不是简单的地址+1 ,而是会自动匹配对应类型大小相加的,所以 array[a][b] == *(*(array+a)+b)。
30 天自制操作系统那本书的作者甚至认为数组的写法 A[i]是*(A+i)的一个语法糖。
ru20150601
2015-11-04 00:32:55 +08:00
谢谢大家的回复,我明白怎么回事了。
应该是我理解有误,一开始,我是这样理解的:

1 : array 和 pointer 是同一回事,区别仅仅在于 array 是一个内存地址定值,而 pointer 是一个内存地址变量。类似于变量 char c 和定量 'c' 。

2 : 2 维 array 是一个 array 的 array 。也就是一个指针的指针 。(或者说是一个地址值的地址值)。所以 array 本身的值是一个地址,而在这个地址所存的是 array[0]的值。而 array[0]的值是一个地址,这个地址存储的是 array[0][0]的值。

3 :所以*array 和 *array[0]分别是一个是内存的地址而另一个是此处所存储的值,不可能相同。

我先把这个理解记录下来,希望将来再回来看一下,能看明白到底什么地方出了问题。
ru20150601
2015-11-04 00:35:51 +08:00
额,上面第三点写错了,是 array 和 array[0]。
mimzy
2015-11-04 00:55:36 +08:00
@CRVV 楼主看的是 C Primer Plus 那个帖子说的是 C++ Primer Plus 在此指出一下 不能冤枉前者……
CRVV
2015-11-04 01:06:22 +08:00
xufang
2015-11-04 01:09:20 +08:00
看完回帖,实在憋不住说一句,一聊到 C 遇见, V2EX 上小学生就像韭菜一样涌现出来。
Valyrian
2015-11-04 04:25:14 +08:00
都别他妈扯淡了, int[][]和**int 不是一回事

楼主运行一下这个:
int a[2][4];
printf("%p\n", a[0]);
printf("%p\n", a);

还有这个
int a[2][4];
printf("%p\n", a[1]);
printf("%p\n", &a[0][4]); //故意越界

这两个值打出来是一样的!!!!!!!!!!!!!

多维 array 在内存里一维的,比如 int a[2][4],在内存里就是长度为 8 的一整块,不存在中间那个保存每一行起点的数组!!!你用高维语法访问的时候会自动帮你算他在一维中的位置,比如 a[1][2],一维中的位置就是 1 * 4 + 2 = 6 ,访问时会自动帮你算!

如果你少访问一个或多个维度,比如 a[1],那么程序就帮你算成 1*4+0=4 ,(并且不 dereference ,返回地址)。越界也没有关系, a[0][4]会算成 0*4+0=4 ,(并且 dereference ,返回值)。这就是为什么上面 a[1]和&a[0][4]是相同的!

当然你的二维数组也可以用**int 来存,这样你访问的时候, a[1],会先访问一个中间数组,这个数组包含每一行开始的位置,元素的类型是*int ,然后 a[1][2]是去这一行找第二个元素

C 语言规定了指针可以用数组的语法来访问,导致很多人混淆 t[]和*t 。他们在一维的时候确实完完全全一样,高维就不一样了
yuchting
2015-11-04 09:09:45 +08:00
再学汇编吧,学汇编你就懂了。

其实汇编没有啥一维二维的, if else for 啥的那都是 C 语言抽象出来的,真的到了底层就只有 jump ( goto )啦, compare 啊什么的,全是指针,只不过偏移量不一样而已。

你从 C 语言去理解指针的本质,还是有些角度不对,就像你从细菌的角度去理解病毒、从中医角度理解细胞结构一样。

学汇编可能也有不明白的,比如你要是问为啥 CPU 会判断?为啥会懂 1+3=4 ?继续学吧,学计算机结构和数字电路外加 EDA 什么的,操作完可编程芯片的那个啥手动输入指令,手动拨 CPU 频率运算的实验。

你就基本上全明白了。
bengol
2015-11-04 09:27:11 +08:00
哪有什么二维指针
firstway
2015-11-04 10:39:24 +08:00
米尺见过吧,上面也有厘米刻度。
那么,第一厘米和第一米的起始点是一样的。
懂了吗?
monkeymonkey
2015-11-04 12:34:49 +08:00
a[i]其实是 syntactic sugar, 语法糖, 等价于 *(a + i)。
在二维数组中
array 是[指针]的指针,这个[指针]可以是[数组指针],也可以是[指针数组]的头地址。
array[0] 是指针,等价于 *(array + 0)
array[0][0] 是数值,等价于 *( *(array + 0) + 0)

1. 在用一维数组模拟的二维数组里( 即在栈上声明 array[3][4]这种,空间连续)
array 的值是等于 array[0], 并等于 &array[0][0]的值,都是同一个地址,但是含义不同。
第一个指向一个二维数组,第二个指向一个一维数组,第三个指向一个元素的地址。
*array 等于 *(array +0) 等价于 array[0] ,其实是对一个指针指针进行取内容操作,得到一个指针,这个指针指向一个一维数组。
*array[0] 等于 *(*(array+0)) 等于*(*(array+0)+0) 等价于 array[0][0],对一个指针进行取内容操作,得到一个元素的内容。

2. 假如你在堆上申请一个指针数组表示的二维数组
不是一维模拟二维,空间可以不连续。而是申请很多一维的数组,再把这些数组的头地址放在一个指针数组里。
那么 array 的值,**一定**不等于 array[0],一个是指向[指针数组]的指针,一个是指向[数组]的指针。
但是 array[0]的值是等于 &array[0][0]的,但是依旧,它们的含义不同,后者是指向元素的地址。

3. 假如你用 int (*array) [m] = (int (*)[m])malloc(m * n * sizeof(int)) 这种,是第三种情况。
和情况 1 有点像,一维模拟二维,空间连续,但是是在堆上。情况 1 在栈上模拟。
此时 array 的值等于 array[0]等于 &array[0][0]的值,但是含义不同。
array 此时的类型是 [数组指针]数组的开始地址,有点绕。 是一个类型为 int [m] 的指针。
对比之下, array[0]则是一个 类型为 int 的指针。
array + 1 其实在一维空间里走了 m 步,每步 4 字节。
array[0] 指向一个数组,&array[0][0]指向元素的地址。
*array 等于 *(array + 0) 等价于 array[0] 会得到第一个数组的指针。
*(array + 1)等价于 array[1] 会得到第二个数组的指针。

一句话总结,地址相同,但含义不同,因此取内容得到的类型也不同。
monkeymonkey
2015-11-04 12:40:08 +08:00
C 语言里语法糖很多, 比如 for 就是 while 的语法糖, ptr->x 是 (*ptr).x 的语法糖, a[i]是 *(a + i)的语法糖。
monkeymonkey
2015-11-04 12:52:43 +08:00
http://biancheng.dnbcw.info/c/66493.html
楼主你什么时候把这篇文章搞懂了,就会 C 语言了。
但是一般正常人不会这么写 233 。
就跟正常人不会写 ++i++这种语句一样。
leechung
2015-11-05 22:59:25 +08:00
一个 2 维的指针, int array [4] [2],教材中说, array 的值和 array[0]的值是一样的,是同一个地址值。这个我不能理解。* array 得到的是一个内存地址值 long int ,而* array[0]则是一个 int 。怎么可能会相同呢?
-------------------------------------------------------------------------------------------------------------

首先, C 中不存在 2 维指针。维度是针对数组而言的;
其次,“值”这个术语在 C 标准文档中有明确的含义,粗略地说,值是对象的内容。值得注意的是, C 标准文档不使用“变量”一词,而是使用“对象”,但这个对象和面向对象设计语言中的对象不是一回事。
再次,基于以上概念的界定,那么,因为标识符 array 指示一个数组类型的对象,所以,“ array 的值”实际上指的是数组对象 array 的值,也就是整个数组的内容;“ array[0]的值”指的是数组 array 的元素,也就是子对象 array[0]的内容。显然,它们的值是不一样的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/233381

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX