这是一个看起来更复杂的主题。部分原因是它没有在 MATLAB 文档中完整记录,部分原因是幕后的共享机制多年来发生了变化。首先,我将简要描述什么是 MATLAB 变量。然后我将描述 MATLAB 使用的各种共享机制。最后,我将描述这些共享机制是如何在 MATLAB 后台使用的。
一个 MATLAB 变量:
MATLAB 变量基本上是一个称为 mxArray 的 C 结构体,其中包含用于保存大小、类、存储类和数据指针等信息的各种字段。此 C 结构的地址通常称为变量的“结构地址”,而数据指针通常称为“Pr”、“Pi”、“Ir”、“Jc”等。对于更高版本的 MATLAB 复数数据是交错的,没有 Pi 指针。对于固有的数字、逻辑和字符类,数据直接位于 Pr 和 Pi 数据指针(以及用于稀疏变量索引的 Ir 和 Jc 指针)的后面。对于 OOP classdef 类变量,在实际数据所在的数据指针后面有一个专有结构,用户无法直接访问它(IMO 的一个基本缺陷限制了 mex 例程中 OOP classdef 变量的有用性)。
变量共享:
MATLAB 以下列方式共享变量:
深拷贝:有问题的变量不与任何其他变量共享任何内容。
共享数据复制:多个变量可以有不同的结构地址但具有相同的数据指针。例如,这通常是由直接的整个变量赋值或完整变量的重塑所产生的。 mxArray (CrossRef) 中曾经有一个字段,它是所有这些变量的链表的一部分。更高版本的 MATLAB 只有一个计数器来告诉您有多少变量是列表的一部分,但用户不再可以访问列表本身。
参考副本:多个变量可以具有完全相同的结构地址。 mxArray (refcount) 中的一个字段表示有多少变量共享相同的结构地址。这通常用于单元格或结构变量元素。
父副本:不是像上面那样真正的副本,而是在嵌套结构和元胞数组中,由于上游共享,变量最终可能与变量其他部分或其他变量中的变量共享。 mxArray 本身没有任何迹象。即,CrossRef 和 refcount 看似未共享,但实际上正在共享。
句柄复制:如果 OOP classdef 变量是从句柄派生的,那么多个变量本质上是共享的。在 mxArray 本身中不会有这方面的指示,并且这些变量不遵循正常的“写时复制”或“延迟复制”规则。
什么时候使用共享?
这就是它变得粘稠的地方。这些规则没有公布,多年来一直在变化。我能做的最好的就是举例:
-- 共享数据复制示例--
A = B; % direct whole variable assignment (earlier versions of MATLAB)
A{1} = B; % assigning from workspace into cell or struct (earlier versions of MATLAB)
A = reshape(B,whatever); % reshape of full variable
B{1} % cell or struct element in expression or assignment
fun(B); % function arguments are passed as shared data copies of original
A = typecast(B,'whatever'); % later versions of MATLAB only. Early versions did deep copy.
-- 参考复制示例--
A = B; % direct whole variable assignment (later versions of MATLAB)
A{1} = B{1}; % assignment among cell or struct elements
A = 1:5; % literal assignment of small variable can result in background reference copy
-- 父拷贝示例--
A.x = 5; B = A; % A.x is sharing with B.x through the parent A and B sharing.
原问题:
非墨西哥函数参数通过某种类型的复制机制传递给函数。无论是文字变量还是 varargin,通常都会使用共享数据副本(用于显式参数或作为构建 varargin 元胞数组的结果)。我看到的唯一例外是有时嵌套函数可以传递标量变量的深层副本,而不是共享数据副本。因此,“写时复制”或“延迟复制”机制适用于函数内部的文字参数和可变参数,因为在这两种情况下,您实际上都在使用共享数据副本(或者可能在更高版本的MATLAB)的函数内的原件。需要注意的是,如果您在函数调用中使用特殊语法,您可以让 MATLAB 解析器识别您正在尝试“就地”修改变量并避免否则会发生的深层复制。
Mex 函数参数有所不同。旧版本的 MATLAB 始终用于传入原始变量结构地址,但更高版本的 MATLAB 使用与非 Mex 函数相同的规则并传入共享数据副本(尽管标量可能作为深层副本传入)。
所以函数中的“copy-on-write”或“lazy-copy”机制真的没什么特别的。传入了原始变量的共享数据副本或引用副本。因此,如果未对其进行更改,则不会在函数内部进行深度复制。如果您确实更改了参数变量的元素,则将首先进行深层复制(即非共享)。但这是在任何级别的 MATLAB 中发生的行为……如果您更改共享变量的元素,则必须首先进行深层复制。无论您是否在函数内部,都适用相同的规则......如果变量是共享的并且您更改了一个元素,那么将首先进行深层复制。