幻の上帝吧 关注:328贴子:3,165
  • 9回复贴,共1

[科普]为什么指针是个糟糕的语言特性

只看楼主收藏回复

喂熊。插楼删。


IP属地:北京1楼2015-08-23 14:41回复
    0 引言
    本文主要是写给自信知道“指针”是什么玩意儿的读者看的。最好看完再评论。(我大致上能确定 99% 以上的并没有足够重视这里的坑,不够了解现实。)
    不知道指针是啥的虽然看了基本是浪费时间,不过至少务必记住:不是所有叫“指针”的东西都是一回事。
    引用:
    http://tieba.baidu.com/p/3605563618 24L
    http://tieba.baidu.com/p/3615289496?pn=2
    1 什么是指针
    本文所谓的指针(pointer) ,是指C和C++等语言中的内建的语言特性。
    在不同范畴中指针这个概念有所不同。在体系结构规范中,指针指称特定的整数字节地址或者两个地址的差(地址偏移量),是整数数值;而在C和C++中,作为核心语言特性支持的指针是一类类型的统称。这两种完全不同的概念经常被混淆,造成一些稀里糊涂的问题(和数组混在一起的时候尤甚)。除非另行说明,本文总是指后者,并不对此进一步展开论述。
    C和C++中,指针(右)值是具有指针类型的(右)值。指针值有时也会被和指针混淆,但在健全的理解下通常能消歧义,因此问题不大(数组也有类似的情况,但涉及转换,问题相对严重)。为清晰起见,在这里不会不加区分地使用。
    注意,C++的成员指针(pointer-to-member)明确地不是指针。尽管它的数值表示在一些情况下可能被实现为地址的偏移量,但语言中并不存在这种保证,实际也通常不那么简单。重复:成员指针不是这里讨论的指针。
    此外,C++中,除了作为语言特性支持的内建(builtin) 指针,也有所谓的智能指针(smart pointer) 。后者在概念上也被 ISO C++11 以来的标准库正式支持。这里讨论的指针不包括这些智能指针,尽管后者和主题相关,并且会在下文重点讨论。
    2 什么是设计
    这里讨论的设计,是指语言的设计,也就是语言规则的作者决定语言特性中应该存在什么和不存在什么的决策之下的抽象结果。
    用户如何使用指针即语用问题是和本文主题相关的问题,会一并讨论,但和这里的设计是两个不同的话题。
    3 什么是糟糕
    糟糕是一个形容词。
    形容设计的糟糕从两个递进的视点得出:对照设计要解决的问题,即需求;对照同类解决方案,即语言中的其它特性或应用领域有交集的其它程序设计语言中的特性。
    通俗地,糟糕以“不好用”和“并非不得不好用”来表现。
    注意因为语言规则之间的相互作用,是否“好用”或者说要解决的问题,须结合使用场景下的其它问题一并讨论:一项特性即便能很好地解决某些问题,但若几乎总是引起其它难以回避的问题,那么至少是“不够好用”的;而要造成的问题麻烦到一定程度时就显然“不好用”了。


    IP属地:北京2楼2015-08-23 14:41
    回复
      4 指针有什么用
      在说明不好用之前,首先需要了解有什么用。
      这是一个发散的语用问题,但大多数用法都很浅显,清楚语言规则就并不难归纳。
      4.1 指针和地址
      C/C++的指针值和体系结构中的所说的指针(地址或地址偏移量)的基本作用类似,它用来指示数据存储的位置。
      以体系结构的接口实现C/C++,可以轻易保证相同类型的指针值到地址的映射是单射,即相同指针类型的指针值的不同的数值表示可以总是找到不同的地址对应,这样就可以在整数算术和关系操作的基础上毫无额外代价地定义指针算术和关系操作;而指针上的操作符 * 抽象的正是间接寻址操作。这就是一些用户口中的所谓“接近底层”。这种简单直接实现的最大好处就是容易以非常小的代价生成针对特定体系结构的代码。
      一个需要注意的关键不同点在于,C/C++作为强类型语言(这里的用法也比较乱,指的是原本意义——默认具有静态类型检查),其中的值(value) 脱离类型讨论并没有意义,指针值也不例外。对象指针可以进行算术操作,但和整数地址算术的含义并不相同,这受到具体指针类型的影响——例如,T*和整数的+操作和sizeof(T)相关;而函数指针并没有类似的意义。此外,需要不同间接操作层次的值如 T* 和 T** 也可被明确地静态地区分,光靠地址并不能做到这点。
      然而,因为体系结构(硬件)实现的惯例,这个差异在往往能被利用(典型地,基址变址寻址),生成相对高效的代码。这是语言中保留指针算术的用途之一。另一方面,把地址相关的整数数值明确和一般的整数值区分,也明确了目的,使静态检查非预期的混用成为可能,有限地提供了类型安全性(例如,指针和指针不能相加)。
      通过两个地址,或一个地址和表示字节大小的一个非负整数就可以标识出地址空间的区间范围。把地址替换为对象指针、字节大小替换为长度(指针值指向的连续元素的个数)同时限制取得指针的手段,能保留这种标记连续存储区域的功效,同时提供一定的可移植性。这种连续的存储在类型系统上被抽象为数组。不过应当注意,在可移植的要求下,实际上指针的语义依赖于数组。完全绕过数组的存在任意地构造一个指针值不能保证指向有效的对象或函数,进行间接操作基本上总会导致未定义行为。
      另一个关键不同是空指针值(null pointer value) 并不保证有特定的地址对应,见下文。
      4.2 存储资源管理
      因为一个对象指针和长度可以用于表示连续的内存,而对象(排除VLA)的大小能在翻译时静态确定,所以在已知大小的存储区域可以用一个对象指针值直接表示。
      ISO C标准库的malloc和calloc以及ISO C++标准库的::operator new和::operator new[]等的返回值是典型的实例。
      这里大小是由存储分配另外保存,这样释放时仍然只需要传递一个指针值即可。ISO C++14提供了sized deallocation,不过这并非强制。
      4.3 参数传递
      因为从分配函数中取得的指针表示的存储并不会如自动变量一样会被自动回收,同时指针有间接操作,而指针值作为对象类型的值可以作为参数传递,因此传递指向对象的指针值配合间接操作就可以模拟传递对象的引用。
      4.4 基于存储的迭代
      因为对象指针能表示存储位置,连续存储的布局由存储模型(以及基于数组的语义)规则限定,适当类型的指针值进行算术操作可以双向顺序迭代乃至随机访问连续存储的对象。
      4.5 空指针值
      指针类型是可空类型(nullable) 类型,约定特殊的空指针值表示不指向任何对象或函数,但可以进行有限的比较。
      可空类型很容易用来表示可选(optional) 值:约定空指针表示值不存在,非空指针指向的对象或函数即存在的可选值。
      空指针值还可以表示哨位(sentinal) 即迭代终止的标识。相对于具体存储区间结束的指针相比,空指针值是通用的,并不需要根据特定的区间使用不同的值。
      注意空指针值的存储表示不一定是整数零值(这再次体现了人为预选的数值和地址的无关性),尽管使用零值一般能有更好的初始化性能。


      IP属地:北京3楼2015-08-23 14:41
      回复
        EOF


        IP属地:北京6楼2015-08-23 14:42
        回复
          现在还看不懂 已收藏 明年的今天再看


          来自Android客户端9楼2016-01-29 10:22
          回复
            Outdated.
            cf. pl-docs: zh-CN/why-is-pointer-awful.md.


            IP属地:北京10楼2020-04-01 14:38
            回复
              太专业了。。。看不懂


              IP属地:上海11楼2023-11-20 15:26
              回复