引用和指针类型
引用(类似于其他编程语言中的指针)是引入多对一关系的一种方式。 这意味着不同的引用可以指向并修改内存中的相同位置(也称为 别名 )。
Nim区分 追踪和 未追踪 引用。 未追踪引用也叫 指针 。 追踪引用指向垃圾回收堆中的对象,未追踪引用指向手动分配对象或内存中其它位置的对象。 因此,未追踪引用是 不安全 的。 然而对于某些访问硬件的低级操作,未追踪引用是不可避免的。
使用 ref 关键字声明追踪引用,使用 ptr 关键字声明未追踪引用。 通常, ptr T 可以隐式转换为 pointer 类型。
空的下标 [] 表示法可以用来取代引用, addr 程序返回一个对象的地址。 地址始终是未经过引用的参考。 因此, addr 的使用是 不安全的 功能。
. (访问元组和对象字段运算符)和 [] (数组/字符串/序列索引运算符)运算符对引用类型执行隐式解引用操作:
- type
- Node = ref NodeObj
- NodeObj = object
- le, ri: Node
- data: int
- var
- n: Node
- new(n)
- n.data = 9
- # 不必写n[].data; 实际上 n[].data是不鼓励的。
还对过程调用的第一个参数执行自动解引用。 但是目前这个功能只能通过 {.experimental:"implicitDeref".} 来启用:
- {.experimental: "implicitDeref".}
- proc depth(x: NodeObj): int = ...
- var
- n: Node
- new(n)
- echo n.depth
- # 也不必写n[].depth
为了简化结构类型检查,递归元组无效:
- # 无效递归
- type MyTuple = tuple[a: ref MyTuple]
同样, T = ref T 是无效类型。
作为语法扩展 object 类型,如果在类型部分中通过 ref object 或 ptr object 符号声明,则可以是匿名的。 如果对象只应获取引用语义,则此功能非常有用:
- type
- Node = ref object
- le, ri: Node
- data: int
要分配新的追踪对象,必须使用内置过程 new 。 为了处理未追踪的内存,可以使用过程 alloc , dealloc 和 realloc 。 系统模块的文档包含更多信息。
Nil —-
如果引用指向 nothing ,则它具有值 nil 。 nil 也是所有 ref 和 ptr 类型的默认值。 解除引用 nil 是一个不可恢复的致命运行时错误。 解除引用操作 p [] 意味着 p 不是nil。 这可以通过实现来利用,以优化代码,如:
- p[].field = 3
- if p != nil:
- # 如果p为nil,``p []`` 会导致崩溃,
- # 所以我们知道 ``p`` 总不是nil。
- action()
Into:
- p[].field = 3
- action()
注意 :这与用于解引用NULL指针的C的“未定义行为”不具有可比性。