Elisp函数参数传递机制解析:与C++传值/传引用对比
嘿,作为从C转Elisp的开发者,我当初也纠结过这个问题,其实Elisp的参数传递机制不能直接等同于C的传值或传引用,得拆开来理解~
Elisp 参数传递:介于传值与传引用之间的逻辑
首先明确核心:Elisp采用的是传值调用,但这里的“值”是「对象的引用(指针)」,这就导致它表现出和C++两种机制都相似的地方,关键看你怎么操作参数。
1. 操作不可变对象:行为类似C++传值
Elisp里的数字、字符串、符号都是不可变对象——你没法直接修改它们的内容,只能让变量指向新的不可变对象。
比如这个例子:
(defun modify-number (x) ; 这里的setq是让参数x指向新的数字100,不是修改原数字 (setq x 100) x) (setq my-num 20) (modify-number my-num) ; 返回100 my-num ; 仍然是20,完全不受函数内操作影响
这和C++传值的效果一模一样:函数拿到的是实参的“拷贝”,修改这个拷贝不会影响原变量。
2. 操作可变对象:修改内容会影响实参
列表、哈希表这类是可变对象,你可以直接修改它们的内部内容。这时候因为函数参数拿到的是对象的引用,修改对象本身就会影响外部的实参——这看起来很像C++的传引用,但本质不同。
比如修改列表的第一个元素:
(defun modify-list (lst) ; setcar是修改列表本身的第一个元素,不是让lst指向新列表 (setcar lst "modified") lst) (setq my-list '("original")) (modify-list my-list) ; 返回("modified") my-list ; 现在也是("modified"),外部实参被改变了
但注意:如果你是给参数重新赋值(比如让lst指向一个新列表),那外部实参不会受影响:
(defun reassign-list (lst) ; 让参数lst指向新的列表,和原实参的列表没关系了 (setq lst '("new list")) lst) (setq my-list '("original")) (reassign-list my-list) ; 返回("new list") my-list ; 还是("original"),完全没变化
这一点和C的传引用不同——C里如果是引用参数,直接赋值会修改原变量,但Elisp里参数是独立的变量,只是初始值是实参的引用,重新赋值只会改变参数自己的指向。
总结:和C++机制的区别
- 和C++传值:Elisp不会拷贝整个对象(只是拷贝引用),所以对大对象更高效;但对于不可变对象,表现出来的效果和传值一致。
- 和C++传引用:Elisp参数是独立变量,不是原变量的别名,所以给参数重新赋值不会影响原变量;但修改可变对象的内容时,效果和传引用一致。
简单来说:Elisp传的是引用的拷贝——修改参数的指向不影响实参,修改参数指向的可变对象内容会影响实参。
内容的提问来源于stack exchange,提问作者JosephTLyons
相关产品推荐
相关产品推荐

