引用和指针类型

引用(类似于其他编程语言中的指针)是引入多对一关系的一种方式。 这意味着不同的引用可以指向并修改内存中的相同位置(也称为 别名 )。

Nim区分 追踪和 未追踪 引用。 未追踪引用也叫 指针 。 追踪引用指向垃圾回收堆中的对象,未追踪引用指向手动分配对象或内存中其它位置的对象。 因此,未追踪引用是 不安全 的。 然而,对于某些低级操作(访问硬件),未追踪引用是不可避免的。

使用 ref 关键字声明追踪引用,使用 ptr 关键字声明未追踪引用。 通常,ptr T 可以隐式转换为 pointer 类型。

空的下标 [] 表示法可以用来取代引用, addr 程序返回一个对象的地址。 地址始终是未经过引用的参考。 因此, addr 的使用是 不安全的 功能。

. (访问元组和对象字段运算符)和 [] (数组/字符串/序列索引运算符)运算符对引用类型执行隐式解引用操作:

  1. type
  2. Node = ref NodeObj
  3. NodeObj = object
  4. le, ri: Node
  5. data: int
  6.  
  7. var
  8. n: Node
  9. new(n)
  10. n.data = 9
  11. # 不必写n[].data; 实际上 n[].data是不推荐的!

还对过程调用的第一个参数执行自动解引用。 但是目前这个功能只能通过 {.experimental:"implicitDeref".} 来启用:

  1. {.experimental: "implicitDeref".}
  2.  
  3. proc depth(x: NodeObj): int = ...
  4.  
  5. var
  6. n: Node
  7. new(n)
  8. echo n.depth
  9. # 也不必写n[].depth

为了简化结构类型检查,递归元组无效:

  1. # 无效递归
  2. type MyTuple = tuple[a: ref MyTuple]

同样, T = ref T 是无效类型。

作为语法扩展 object 类型,如果在类型部分中通过 ref objectptr object 符号声明,则可以是匿名的。 如果对象只应获取引用语义,则此功能非常有用:

  1. type
  2. Node = ref object
  3. le, ri: Node
  4. data: int

要分配新的追踪对象,必须使用内置过程 new 。 为了处理未追踪的内存,可以使用过程 allocdeallocrealloc 。 系统模块的文档包含更多信息。

Nil —-

如果引用指向 nothing ,则它具有值 nilnil 也是所有 refptr 类型的默认值。 解除引用 nil 是一个不可恢复的致命运行时错误。 解除引用操作 p [] 意味着 p 不是nil。 这可以通过实现来利用,以优化代码,如:

  1. p[].field = 3
  2. if p != nil:
  3. # 如果p为nil,``p []`` 会导致崩溃,
  4. # 所以我们知道 ``p`` 总不是nil。
  5. action()

Into:

  1. p[].field = 3
  2. action()

注意 :这与用于解引用NULL指针的C的“未定义行为”不具有可比性。