学习 C 语言的问题,有个问题怎么想都想不通 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
ru20150601
V2EX    程序员

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

  •  
  •   ru20150601 2015-11-03 21:45:57 +08:00 4365 次点击
    这是一个创建于 3632 天前的主题,其中的信息可能已经有所发展或是发生改变。
    一个 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     Solana     965 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 22:32 PVG 06:32 LAX 15:32 JFK 18:32
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86