r - 为什么对同一向量的两个引用会为向量的每个元素返回不同的内存地址?
问题描述
我正在学习 R,目前正在阅读这本书。为了确保我理解这个概念,我进行了以下测试,结果证明我很困惑,如果你能澄清一下,我将不胜感激。这是我从终端直接在 R shell 中运行的测试(不使用 RStudio 或 Emacs ESS)。
> library(lobstr)
>
> x <- c(1500,2400,8800)
> y <- x
> ### So the following two lines must return the same memory address
> obj_addr(x)
[1] "0xb23bc50"
> obj_addr(y)
[1] "0xb23bc50"
> ### So as I expected, indeed both x and y point to the same memory
> ### location: 0xb23bc50
>
>
>
> ### Now let's check that each element can be referenced by the same
> ### memory address either by using x or y
> x[1]
[1] 1500
> y[1]
[1] 1500
> obj_addr(x[1])
[1] "0xc194858"
> obj_addr(y[1])
[1] "0xc17db88"
> ### And here is exactly what I don't understand: x and y point
> ### to the same memory address, so the same must be true for
> ### x[1] and y[1]. So how come I obtain two different memory
> ### addresses for the same element of the same vector?
>
>
>
> x[2]
[1] 2400
> y[2]
[1] 2400
> obj_addr(x[2])
[1] "0xc15eca0"
> obj_addr(y[2])
[1] "0xc145d30"
> ### Same problem!
>
>
>
> x[3]
[1] 8800
> y[3]
[1] 8800
> obj_addr(x[3])
[1] "0xc10e9b0"
> obj_addr(y[3])
[1] "0xc0f78e8"
> ### Again the same problem: different memory addresses
你能告诉我我的错误在哪里以及我在这个问题中误解了什么吗?
解决方案
任何 R 对象都是 C(称为 - 的指针SEXP
)“多对象”(struct
)。这包括关于 R 对象的信息(R 需要操作,例如length
,引用的数量 - 知道何时复制对象 - 等等),以及我们有权访问的 R 对象的实际数据。
lobstr::obj_addr
,大概是返回 aSEXP
指向的内存地址。这部分内存包含有关 R 对象的信息和数据。在 R 环境中,我们不能/不需要访问每个 R 对象中实际数据的(指向)内存。
正如亚当在他的回答中指出的那样,该函数将 C 对象中包含的数据的第 n 个元素[
复制到一个新的 C 对象中,并将其SEXP
指针返回给 R。每次[
调用时,都会创建一个新的 C 对象并返回给 R。
我们无法通过 R 访问我们对象的实际数据的每个元素的内存地址。但是玩了一下,我们可以使用 C api 跟踪各自的地址:
获取地址的函数:
ff = inline::cfunction(sig = c(x = "integer"), body = '
Rprintf("SEXP @ %p\\n", x);
Rprintf("first element of SEXP actual data @ %p\\n", INTEGER(x));
for(int i = 0; i < LENGTH(x); i++)
Rprintf("<%d> @ %p\\n", INTEGER(x)[i], INTEGER(x) + i);
return(R_NilValue);
')
并应用于我们的数据:
x = c(1500L, 2400L, 8800L) #converted to "integer" for convenience
y = x
lobstr::obj_addr(x)
#[1] "0x1d1c0598"
lobstr::obj_addr(y)
#[1] "0x1d1c0598"
ff(x)
#SEXP @ 0x1d1c0598
#first element of SEXP actual data @ 0x1d1c05c8
#<1500> @ 0x1d1c05c8
#<2400> @ 0x1d1c05cc
#<8800> @ 0x1d1c05d0
#NULL
ff(y)
#SEXP @ 0x1d1c0598
#first element of SEXP actual data @ 0x1d1c05c8
#<1500> @ 0x1d1c05c8
#<2400> @ 0x1d1c05cc
#<8800> @ 0x1d1c05d0
#NULL
我们对象的数据元素之间的连续内存差异等于int
类型的大小:
diff(c(strtoi("0x1d1c05c8", 16),
strtoi("0x1d1c05cc", 16),
strtoi("0x1d1c05d0", 16)))
#[1] 4 4
使用[
功能:
ff(x[1])
#SEXP @ 0x22998358
#first element of SEXP actual data @ 0x22998388
#<1500> @ 0x22998388
#NULL
ff(x[1])
#SEXP @ 0x22998438
#first element of SEXP actual data @ 0x22998468
#<1500> @ 0x22998468
#NULL
这可能是一个超出需要的广泛答案,并且在实际技术上过于简单,但希望能提供更清晰的“大”图。
推荐阅读
- react-native - formik 在后台使用 formData 吗?
- c# - 当对象被序列化/反序列化时,委托/事件中的方法引用会做什么?
- javascript - 子元素未出现在网页中
- excel - VBA选择所有数据并根据列标题“名称”排序
- amazon-web-services - 如何使用 terraform 创建允许来自任何地方的 RDP 端口的 aws 安全组规则?
- php - PHP - 如何退出 if
- java - 如何提取部分录制的音频字节
- javascript - reduce() 返回值不能在 HTML 中显示在使用 map 函数的反应中
- c++ - 如何在一个 64 位整数中存储和使用两个 32 位带符号整数?
- arrays - 如何在 React/Redux 代码中进行分组(前端新手)?