0

在 Ada95 中,我们有两个相同类型的数组。如果我们像这样将一个分配给另一个:

Array_A := Array_B;  

这究竟会做什么?

它会遍历 Array_B 并将每个元素依次分配给 Array_A 吗?还是只是将 Array_A 的地址更改为 Array_B 的地址?

询问的原因是我们需要知道这个赋值是否是原子操作。

4

4 回答 4

2

假设

type My_Array is array (1 .. 42) of Integer;
Array_A : My_Array;
Array_B : My_Array;

thenArray_AArray_B是不同的内存区域,并且在分配之后的字节Array_A包含 的字节的副本Array_B。传输是通过迭代元素还是通过某些等效的memcpy(3)来完成的,取决于编译器编写者(pragma Atomic_Components影响这一点)。

另一方面,如果你有

type My_Array is array (1 .. 42) of Integer;
type My_Array_P is access My_Array;
Array_A : My_Array_P;
Array_B : My_Array_P;

然后赋值将复制指针,并且可能是原子的(您可以添加pragma Atomic (My_Array_P);pragma Atomic (Array_A);告诉编译器使访问值成为原子的,或者如果不能编译代码则无法编译)。

于 2013-10-04T12:35:02.687 回答
2

Array_A 的内容最终成为 Array_B 元素的副本,即赋值遍历数组,为每个元素赋值。与 C 不同,Ada 具有真正的数组对象。

因此,除非您在受保护的对象之类的东西中保护操作,否则分配不是原子的。(在受保护对象中包装数组赋值不会使其成为原子,它只是从应用程序的其余部分的角度来看。)

于 2013-10-04T12:29:17.347 回答
1

赋值语句复制数据。Ada 区分了保存数据的对象和仅“引用”数据的对象;作为引用的对象具有作为类型的access类型(例如My_Array_P在 Simon 的示例中)。分配访问类型会导致引用指向同一个对象;但是分配一个不是访问类型的对象总是会复制数据。

说数组赋值“在 Array_B 上迭代 [s] 并依次将每个元素分配给 Array_A”并不完全准确。如果数组元素类型是受控类型或具有受控子组件,则Array_A := Array_B;必须最终确定 的元素,然后Array_A必须调整它们,但它的发生顺序如下:首先,每个元素都Array_A被最终确定(以任意顺序)。然后将数据从复制Array_B到复制Array_A(这可以一次完成一个元素,但在许多情况下,编译器可以将其优化为块复制)。然后调整 的每个元素Array_A(同样,以任意顺序)。因此, 的语义与Array_A := Array_B循环不同(我假设数组的上限和下限相同):

for I in Array_A'range loop
    Array_A (I) := Array_B (I);
end loop;

因为FinalizeandAdjust调用将以不同的顺序完成。通常这不应该对程序产生任何影响,如果FinalizeAdjust编写正确(如果效果不同,则程序设计可能有些奇怪)。但是,在Finalize释放内存并Adjust分配新内存的情况下(例如在典型Unbounded_String实现中),以不同的顺序进行FinalizeandAdjust调用可能会对内存碎片产生影响。

于 2013-10-04T16:11:17.943 回答
1

您的问题的答案是,如果您希望编译器对它们进行原子分配,您应该将对象声明为“原子”。

如果您的目标 CPU 没有适合对象类型的原子指令,它会抱怨。在这种情况下,您(至少)有一种替代解决方案:

  1. 将数组封装在受保护的对象中,以确保对它们的操作是原子的。

使用访问类型和复制引用当然是一种选择,但它实际上是一种完全不同的操作,因此您应该仔细考虑这是否真的是您想要的。

(如果有问题的数组是短的、打包的布尔数组,您的编译器可能能够使对象“原子”,否则我不会期望它。)

于 2013-10-05T11:45:58.147 回答