我想用 Common Lisp 编写一个函数,它会破坏性地修改它的参数。在 C 或 Rust 中,我将使用指向对象的指针/引用,该对象可以在函数体内取消引用。在 CL 中我写道:
(defun foo (lst)
(setf lst NIL))
Run Code Online (Sandbox Code Playgroud)
但在评估此表格后:
(let ((obj (list "a" "b")))
(foo obj)
obj) => ("a" "b")
Run Code Online (Sandbox Code Playgroud)
可以看到该函数foo没有任何效果。我可以解释按值传递语义,即我们修改推入函数堆栈的参数的本地副本。如果我们定义另一个函数:
(defun bar (lst)
(setf (car lst) NIL))
Run Code Online (Sandbox Code Playgroud)
并评估类似的形式
(let ((obj (list "a" "b")))
(bar obj)
obj) => (NIL "b")
Run Code Online (Sandbox Code Playgroud)
我们将清楚地看到 已lst被修改,就好像我们将使用按引用传递语义一样。所以,(setf lst NIL)没有工作,但(setf (car lst) NIL)做了。您能解释一下原因吗?
我知道 Common Lisp 不鼓励程序员接触原始内存,但我想知道是否可以查看对象如何在字节级别存储。当然,垃圾收集器在内存空间中移动对象,并且函数的两次后续调用(obj-as-bytes obj)可能会产生不同的结果,但让我们假设我们只需要内存快照。你会如何实现这样的功能?
我对 SBCL 的尝试如下:
(defun obj-as-bytes (obj)
(let* ((addr (sb-kernel:get-lisp-obj-address obj)) ;; get obj address in memory
(ptr (sb-sys:int-sap addr)) ;; make pointer to this area
(size (sb-ext:primitive-object-size obj)) ;; get object size
(output))
(dotimes (idx size)
(push (sb-sys:sap-ref-64 ptr idx) output)) ;; collect raw bytes into list
(nreverse output))) ;; return bytes in the reversed order
Run Code Online (Sandbox Code Playgroud)
咱们试试吧:
(obj-as-bytes #(1)) =>
(0 2 0 0 0 0 0 0 0 0 0 0 0 …Run Code Online (Sandbox Code Playgroud)