V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ru20150601
V2EX  ›  程序员

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

  •  
  •   ru20150601 · 2015-11-03 21:45:57 +08:00 · 3906 次点击
    这是一个创建于 3309 天前的主题,其中的信息可能已经有所发展或是发生改变。
    一个 2 维的指针, int array [4] [2],教材中说, array 的值和 array[0]的值是一样的,是同一个地址值。这个我不能理解。* array 得到的是一个内存地址值 long int ,而* array[0]则是一个 int 。怎么可能会相同呢?

    我本来以为这个是教材的错误,但是这个教材言之凿凿,甚至还特别举例,输出了这两个值的地址证明是相同的。有人能解答下吗?
    35 条回复    2015-11-05 22:59:25 +08:00
    ProjectAmber
        1
    ProjectAmber  
       2015-11-03 21:51:15 +08:00
    虽然内容一样,但是类型不同啊。就和 int 和 float 二进制一样,表示的数可不一样。
    zackkson1991
        2
    zackkson1991  
       2015-11-03 21:52:47 +08:00
    一楼说的正确。这时候已经类型是不一样了。也就是每次你访问的时候,抓取内存的方式不一样了。
    colatin
        3
    colatin  
       2015-11-03 22:01:10 +08:00
    f(x)=x^2
    f(x)=x^3
    即使 x 的值相同, f(x)的值也会不同。不知道这个类比是否恰当。
    aholic
        4
    aholic  
       2015-11-03 22:11:36 +08:00
    int i = 1;
    int *p = (int*)(i);
    i 和 p 的“值”也是一样的。
    但是作为 int 来解读,它就是 1
    作为 int*来解读,他就是一个指向 0x1 的指针
    feiyuanqiu
        5
    feiyuanqiu  
       2015-11-03 22:14:18 +08:00
    *array 得到的是 array[0][0] 的地址
    *array[0] 得到的是 array[0][0] 的值
    ru20150601
        6
    ru20150601  
    OP
       2015-11-03 22:15:16 +08:00
    @ProjectAmber
    @zackkson1991
    @colatin
    不是,三位,可能我表达的不清楚。,是这样的,这个例子当中呢, 他们输出的是指针的值。我看的英文版的也不知道中文术语是不是这样说的,我直接说代码吧:
    printf("array =%p array[0]= %p",array,array[0] );

    书中的例子,这两个输出出来是一样的。这完全讲不通啊

    如果 array==array[0]

    那么 *array == *array [0]

    array[0][0]=13 //而 *array [0] ==array [0][0]

    那么 array==13 ?
    =====================================分割线============================

    我的意思是, array[0][0]是一个变量, array[0]是指向变量的指针, array 是指针的指针,这两个怎么可能相等?
    ru20150601
        7
    ru20150601  
    OP
       2015-11-03 22:17:33 +08:00
    @feiyuanqiu

    对呀,这就是我所理解的啊。这两个值不可能相等啊。书中却是一样的。
    c742435
        8
    c742435  
       2015-11-03 22:21:43 +08:00
    指针的值是其指向的内存地址,都是指向同一个内存地址,为啥不等?
    谁跟你说 array 是指针的指针。
    patrickstar
        9
    patrickstar  
       2015-11-03 22:28:45 +08:00
    你还是找本靠谱的书看看,看看 C 指针这本书,好像是这个名字
    array 是一个 int**,而 array[0]是一个 int*
    patrickstar
        10
    patrickstar  
       2015-11-03 22:36:36 +08:00
    你看看 "Pointers on C" 这本书,中文名叫啥子忘了!
    array 和 array[0]的指针值确实一样,都等于 &array[0][0],即第一个元素的地址,但是*array 和 *array[0]是两个概念了,*array 任然是一个指针,这时候任然=&array[0][0], 而*array[0]= array[0][0]
    patrickstar
        11
    patrickstar  
       2015-11-03 22:44:18 +08:00
    运行一下子程序就明白了:
    #include <stdio.h>
    #include <stdlib.h>

    int main(int argc,char* argv[])
    {
    int a[4][2];
    for(int *p = a[0],i = 0; i < 8; i++)
    *(p+i) = (i+1)*(i+1);

    printf("array = %p\n",a); // = &a[0][0]
    printf("array[0] = %p\n",a[0]); // = &a[0][0]

    printf("*array = %p\n",*a); //*a is a pointer=&a[0][0]
    printf("*array[0] = %d\n",*a[0]); //*a[0] is int

    return EXIT_SUCCESS;
    }
    harry890829
        12
    harry890829  
       2015-11-03 22:45:54 +08:00
    这是一个二维指针,所以 array[0]其实也是指针啊……
    如果 char sz[8]写成 sz ,你知道是指 sz 数组的第一个元素的地址
    那么 int array[4][2]写成, array[0]你也应该知道这是一个指针啊,
    array 和 array[0]这两个都是指向了这个二维数组的第一个元素的地址
    ru20150601
        13
    ru20150601  
    OP
       2015-11-03 22:46:52 +08:00
    @patrickstar
    好吧,我刚刚也实际运行代码测试过了,确认不是教材错了我就放心了。正在看的是 c primer plus ,这个例子也是这本书里的,这本书写的很细,猛的出来一个这么反直觉的东西,竟然没有解释,一句话就带过了,把我害苦了。
    htfy96
        14
    htfy96  
       2015-11-03 22:53:11 +08:00   ❤️ 1
    @patrickstar
    @harry890829
    @ru20150601
    array[0]并不是一个指针,而就是一个叫做 int[2]的类型
    printf("%lu", sizeof(a[0]));
    之所以常常能把 a[0]看作一个指针用,是因为这里发生了隐性的 decay
    canautumn
        15
    canautumn  
       2015-11-03 23:00:53 +08:00
    C 语言就是这么规定的,记住就行了。可以看看『征服 C 指针』,记得里边讲 C 语言指针的设计只是尽量合理(尽量符合直觉),但有些和直觉矛盾之处是很难解决的,只好记住。
    halfcoder
        16
    halfcoder  
       2015-11-03 23:06:20 +08:00
    楼主从 C 数组的内存模型上来思考就容易理解了。 int array[4][2]作为一个总共拥有 8 个 int 元素的数组,不管是 array 也好还是 array[0]也罢,都是指向的第一个元素,其地址当然是一致的。至于类型不一样,那只是外部表示而已,即使 array 变成更高维的数组,其类型发生巨大变化,结论也是一致的。
    Zhang
        17
    Zhang  
       2015-11-03 23:19:03 +08:00
    c 语言规定的,数组的名称就是第 0 个元素的地址!
    CRVV
        18
    CRVV  
       2015-11-03 23:23:35 +08:00   ❤️ 2
    array 是一个 2 维数组, array[0] 是一个 1 维数组,类型都不一样,值怎么个一样法?

    但是 C/C++里面的数组比较特殊,数组类型的变量会被编译器转成指向首元素的指针
    (注一下,这个问题上,有些人认为数组名就是一个指针(而不是被转成指针),比如楼上的回帖,也包括楼主的教材)

    所以下面是我认为的正确的理解方式(也就是 "C++ Primer" 的内容),要搞懂这个问题需要了解两件事:
    1 、在你使用数组名这个变量时,它通常被转换成了首元素的指针,所以 array 被转成了 &array[0]
    2 、数组只是一个连续的内存区域,用来存数组里的元素。数组的内存地址就是数组首元素的内存地址(但是,作为两个指针,它们的类型不同)
    用 printf 输出来的是内存地址,所以
    array -> &array[0] -> &array[0][0], array[0] -> &array[0][0]
    三个箭头分别是,数组被转成首元素地址,数组的地址和数组首元素的地址相等,数组被转成首元素地址
    结论是, 如果你用 printf 输出 array 和 array[0],输出的东西是相等的,但这两个变量的类型不同,不应该拿来比较

    以下和回答无关
    “教材中说, array 的值和 array[0]的值是一样的”
    “* array 得到的是一个内存地址值 long int ”
    看到这两行内容,我觉得你这本教材应该扔掉了
    ru20150601
        19
    ru20150601  
    OP
       2015-11-03 23:42:49 +08:00
    @CRVV 谢谢长文答复。我看的是 c primer plus ,这两句话不是原文,是我根据自己的理解描述的。因为不知道有这个背景。
    CRVV
        20
    CRVV  
       2015-11-03 23:57:47 +08:00
    @ru20150601
    http://www.v2ex.com/t/233379
    从这个帖子来看,这书的作者不会使用指针
    inevermore
        21
    inevermore  
       2015-11-04 00:00:45 +08:00
    array 和 array[0]都可以看做指针,指向的内存起始位置相同,所以他们的值是相同的。
    但是他们的类型不同。
    Reficul
        22
    Reficul  
       2015-11-04 00:10:30 +08:00 via Android
    把一个二维数组看成一个矩阵, array 指向整个矩阵, array[0]指向矩阵的第一行。指针指向元素起始位置,那么 array 和 array[0]自然都是指向同一个位置,即元素 array[0][0]的内存地址。
    但是仅仅是数值相同,他们的类型是不一样的。指针每+1 不是简单的地址+1 ,而是会自动匹配对应类型大小相加的,所以 array[a][b] == *(*(array+a)+b)。
    30 天自制操作系统那本书的作者甚至认为数组的写法 A[i]是*(A+i)的一个语法糖。
    ru20150601
        23
    ru20150601  
    OP
       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
        24
    ru20150601  
    OP
       2015-11-04 00:35:51 +08:00
    额,上面第三点写错了,是 array 和 array[0]。
    mimzy
        25
    mimzy  
       2015-11-04 00:55:36 +08:00
    @CRVV 楼主看的是 C Primer Plus 那个帖子说的是 C++ Primer Plus 在此指出一下 不能冤枉前者……
    CRVV
        26
    CRVV  
       2015-11-04 01:06:22 +08:00
    xufang
        27
    xufang  
       2015-11-04 01:09:20 +08:00
    看完回帖,实在憋不住说一句,一聊到 C 遇见, V2EX 上小学生就像韭菜一样涌现出来。
    Valyrian
        28
    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
        29
    yuchting  
       2015-11-04 09:09:45 +08:00
    再学汇编吧,学汇编你就懂了。

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

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

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

    你就基本上全明白了。
    bengol
        30
    bengol  
       2015-11-04 09:27:11 +08:00 via Android
    哪有什么二维指针
    firstway
        31
    firstway  
       2015-11-04 10:39:24 +08:00
    米尺见过吧,上面也有厘米刻度。
    那么,第一厘米和第一米的起始点是一样的。
    懂了吗?
    monkeymonkey
        32
    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
        33
    monkeymonkey  
       2015-11-04 12:40:08 +08:00
    C 语言里语法糖很多, 比如 for 就是 while 的语法糖, ptr->x 是 (*ptr).x 的语法糖, a[i]是 *(a + i)的语法糖。
    monkeymonkey
        34
    monkeymonkey  
       2015-11-04 12:52:43 +08:00
    http://biancheng.dnbcw.info/c/66493.html
    楼主你什么时候把这篇文章搞懂了,就会 C 语言了。
    但是一般正常人不会这么写 233 。
    就跟正常人不会写 ++i++这种语句一样。
    leechung
        35
    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]的内容。显然,它们的值是不一样的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4950 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 10:00 · PVG 18:00 · LAX 02:00 · JFK 05:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.