我认为这非常简单,基本上,我在 C 代码中创建的任何 SEXP
类型的对象都必须受到保护,但在使用链表和 CAR
/CDR
等。我从 Writing R Extensions 中的这条评论开始:
Protecting an R object automatically protects all the R objects pointed to in the corresponding SEXPREC, for example all elements of a protected list are automatically protected.
这来自 R Internals :
A SEXPREC is a C structure containing the 32-bit header as described above, three pointers (to the attributes, previous and next node) and the node data ...
LISTSXP: Pointers to the CAR, CDR (usually a LISTSXP or NULL) and TAG (a SYMSXP or NULL).
所以我将其解释为,如果我做类似的事情:
SEXP s, t, u;
PROTECT(s = allocList(2));
SETCAR(s, ScalarLogical(1));
SETCADR(s, ScalarLogical(0));
t = CAR(s);
u = CADR(s);
然后 t
和 u
受到保护,因为它们是指向 protected 列表 s
中的对象的指针(推论问题:是否有一种获取对象的 PROTECTED 状态的方法?在 Rinternals.h 中看不到任何符合要求的东西)。然而我看到了类似的东西(来自 src/main/unique.c
):
// Starting on line 1274 (R 3.0.2), note `args` protected by virtue of being
// a function argument
SEXP attribute_hidden do_matchcall(SEXP call, SEXP op, SEXP args, SEXP env)
{
// ommitting a bunch of lines, and then, on line 1347:
PROTECT(b = CAR(args));
// ...
}
这表明 args
中的所有对象都没有受到保护,但这看起来很奇怪,因为从那时起任何 args
对象都可能在任何时候被 GC 处理。由于 CAR
只是返回一个指向可能已经 protected 对象的指针,为什么我们需要在这里保护它?
最佳答案
这样想:PROTECT
实际上并没有对对象做任何事情。相反,它添加了一个临时的 GC 根,以便该对象被收集器认为是事件的。它包含的任何对象也是事件的,不是因为 C 应用了某些保护,而是因为它们被另一个对象指向,该对象本身已经被认为是事件的 - 与任何其他正常的事件对象相同。因此,将汽车设置为 protected 列表不仅可以使该对象保持事件状态,它还可能释放汽车中先前用于 GC 的所有内容,将其从该特定事件树中删除(保护列表不是递归的影响元素)。
所以一般来说,你不会有一种简单的方法来判断一个对象是否在这个更广泛的意义上“ protected ”,因为它实际上只是遵循与 GC 在其他地方所做的相同的规则,并且没有什么特别之处目的。您可以潜在地跟踪整个 PROTECT
列表并查看是否找到它,但这将是......至少可以说是低效的(也没有什么可说的是通向对象的所有权树PROTECT
列表中的问题是能够使其存活时间最长的那个)。
do_matchcall
中的行实际上是出于完全不相关的原因:保护 CAR(args)
仅发生在条件的一个分支中 - 在另一个分支中,它是 protected 新创建的对象。冗余地保护来自该分支的值也意味着无论采用哪个分支,PROTECT
堆栈上都保证有相同数量的对象,这简化了相应的UNPROTECT
在函数的末尾对固定数量的槽进行操作(无需复制检查以改变它)。
关于c - 编写用于 R 的 C 函数时究竟需要保护什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26610158/