这里没有定义数组指针,为什么会出现数组,只是仅仅定义一个指针而已,为什么可以当数组用? 这里没有定义数组指针,为什么会出现数组,只是仅仅定义一个指针而已,为什么可以当数组用?
@crystalzhiguo 的回答是胡说八道,是在误导初学者。数组和指针完全是两种不同的概念。 举个例子,有一个整数 i,一个浮点数 d。你可以将浮点数赋给整数 (i = d),同样地你也能把数组名赋给指针 (ptr = array);另外 i 跟 d 都支持 +-×/ 的语法呢 (数组跟指针都支持加一个整数或者通过下标运算得到一个偏移位置),你能说整数跟浮点数是一个玩意么? 指针和数组只是有相似的语法罢了。数组的类型中带有长度信息,而指针是没有的。参见:知乎用户:为何std::begin()的参数不能是一个指针? 数组的索引运算符,是一种语法糖,等于首地址+偏移量。就像这个函数一样: int offset(int *arr,int index) { return *(arr+index) } 也就是将下面的代码 arr[3] 理解为 *(arr+3) C 语言是很简洁的,只是很多人都喜欢往自己习惯的方向去理解,于是就不得不搞出很多特例来弥补自己理解上的漏洞,所以搞得好像很难一样。就好像你撒第一个谎之后,就注定要再撒无数的谎去圆它一样。 比如这里的方括号,并不是因为声明数组的时候要 ,所以方括号是数组专享,使用数组的时候模仿声明来一个 。 正确的理解是数组使用的时候经常 ,所以声明的时候就模仿使用时候的样子写成 。(指针、函数的声明都是同样的道理,没有特殊。) 这里 的性质和 是一样的:正常是 ,但是括号写起来太麻烦了,所以给你一个捷径写成 正常是 ,但是括号写起来太麻烦了,所以给你一个捷径写成 所以重点来了:方括号是指针的操作(的快捷方式),反而和数组无关。 — 所以这个问题的正确打开方式应该是「数组凭什么可以用方括号?」 故事发展到这里,我们就可以看到 @crystalzhiguo 解释说「指针就是数组,数组就是指针」。可惜实际的游戏规则并非如此,这是一个善意的谎言,可以方便你在初学 C 语言时的理解。但也正因为它是谎言,所以你会在评论区里发现被指出反例后的大型「圆谎」现场,以及尝试把你吸引去以谎言为真理的国度的操作(拉 sigusoft 群)。当然这不是讽刺,谎言重复一百次就会成为真理,我觉得可以理解。 指针和数组都是有良好定义的概念,并不会因为「我理解」「本质上」「编译器实际上」而发生改变。例如很早的 C99 标准中就已经是这么定义的:An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. Array types are characterized by their element type and by the number of elements in the array.大意:「数组类型」指连续分配的同一类型对象的非空集合。如果素数量或者素类型不同,就算是不同的数组类型。A pointer type may be derived from a function type, an object type, or an incomplete type, called the referenced type. A pointer type describes an object whose value provides a reference to an entity of the referenced type.大意:「指针类型」对象的值引用着一个实体。这个实体可以是函数、对象、甚至不完整类型。 小明有 A B C D 四个箱子,小红只有一个箱子。咱不能因为小红的箱子里有张纸条写着「A 箱子」,然后就说小明和小红的财产完全对等,是吧? 数组之所以可以用方括号,是因为数组在绝大多数情况下都会被自动转换成指针。Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.大意:除非被用作 sizeof 的操作数、单目 & 的操作数、用来初始化数组的字符串字面量,类型为「T 的数组」的表达式都会被转换成类型为「T 的指针」的表达式,表达式指向该数组的首素。 原因就是这么简单。 p.s. 我个人觉得这里用 遍历整个二维数组理论上是有问题的,见《多维数组指针p可以换行嘛?》 举个例子: 这里的 a 就是这个数组的起始地址,C语言相关的书里一般这么说:数组的地址就是这个数组的首素的地址,那么你可以通过 a[下标] 来指定位置的素的值;如果你这样写: 那么你可以这样取值: 这是我在 Clion上测试的结果:
看到没,结果是一样的。 你甚至还可以这样写:
说白了你对数组进行取值的时候,编译器是将首地址加上数组中素的数据类型所占的大小乘以需要偏移的量计算对应值的地址的,如果上面数组的起始地址是 0x07c00, 而这个数组中素的数据类型是 int,暂且将 int 所占的空间规定为四个字节, 那么第三个素的地址就是 0x07c00 + 4*4,这样就求得了第三个素的地址,至于为什么直接写 p[i] 就可以取到值,编译器会根据 p 中存放的地址和当前的数据类型(这里以4个字节为一个单位)偏移指定的 i 个单位,自然就取到值了,这些都是为了写程序的时候方便。所以 3[p] 这种写法也没问题,只不过”偏移”的值换成了 p 中的值(地址)而已。 不管是 a[i] 这种方式取值也好,p[i],*(p+i),i[p],也好,本质上都是根据数组起始地址加上偏移地址计算素位置的;这个你学汇编的时候应该就能更加清晰了。 看了一些回答想说个暴论,大学教汇编教的太早没什么吊用,很可能让对CS领域是怎么做抽象、为什么做抽象还没有正确认知时,就养成了自以为是喜欢从底层倒推上层设计的臭毛病。而唯一收获到的就是学会了一点点汇编的皮毛,然而他的职业生涯里除了玩票可能再也没有写汇编的机会了( 老生常谈的i = i++ + ++i问题也是,你跟他说是ub,他说他去看汇编了,就该这么理解。这种人都没得聊的。 至于持指针等于数组这种观点的人,无非就是“啊,一看汇编都是寻址嘛,那’本质’就是一种东西啦“。我寻思越来越高级的语言的类型系统就是为了帮废物程序员更好的写代码,没想到还真有人不领情( 上面这两段代码,你去编译吧,大概率出来的汇编代码像是一个模子里刻出来的(换成c也一样)。想必FuckAssemblyBoy和int [2]本质相同嗷。 当我们说C/C++里面两个类型”一样“的时候,不严格的讲,我们是在谈:这两个类型”占据“的”空间“相同,即sizeof(typeA) == sizeof(typeB)这两个类型对于所”占据“的空间里面的信息具有相同的解释(比如int把信息解释为补码,unsigned把信息解释为单纯二进制)这两个类型上面定义的操作完全相同这两个类型具有相同的语义信息 在我有限的认知里面,除了typeA是typeB的typedef之类的情况之外,恐怕没有两个类能满足上面的要求了。比如std::pair<int, int>和上文的FuckAssemblyBoy其实就很像,但也不是一种类型,因为FuckAssemblyBoy是专门用来骂人的,std::pair<int, int>罪不至此( 而指针和数组就更惨了,恐怕一条都不满足吧( sizeof(int [N]) == sizeof(int*)上来就不满足了;对信息的解释就更不要说了,一个编码的是”地址“,另一个编码的是一系列对象本身;定义的操作也不相同,关键在于数组好死不死的在大多数情况下会隐式转换成指针,才显得太像了;语义信息就更是天差地别了,数组的语义大体上是”编译期确定大小N的一块连续区域,大小N本身不能再改变,区域里放置着N个构造好的类型为T的对象,在正常情况下这N个对象的生命周期和数组本身是一致的“,指针的语义大体上是”可能为null,也可能指向一个位置;一般情况下会指向一个类型为T的对象,但在很多情况下甚至会指向一块“混沌”的内存区域,不保证这块区域的任何有效性;由于堆内存分配可以一次分配一大块区域,因此一个运行时的大小n和指针本身确实可以起到标识一块连续区域的作用;但这块区域里的对象有多少个是任意的,这些对象的生命周期也是任意的,可以在指针还存在的时候就被回收,也可以在指针的生命周期结束后依然存在“ 这语义差别的已经不是一点半点了,光凭生命周期这一点就可以说是截然不同了吧( “无非是为了方便”而已?说的倒是轻巧嗷,汇编不也是为了方便嘛,机器只认识0101,什么mov啥的不也是为了方便嘛?建议以后宁编程必写010101,汇编都不许写,写一个高级语言nmbsyc( 喜欢聊本质?那宁干脆把所有类型对应的内存都弄成void*指向的区域好了,这样就没有类型系统了,过于本质,建议给C++之父托梦( ps:根据我给大一新生答疑的经验,我觉得大一新生都能意识到数组和指针的不同了,道理也很简单嘛,写算法题,总会遇到需要把二维数组传进函数里的情况 结果一写这样的代码就错了,如果坚持“指针就是数组”,那怎么解释“二重指针不是二维数组”呢?接着打补丁啊?
2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/57757.html