跳转到内容

Ada 编程/错误

来自维基教科书,开放的书籍,开放的世界

Ada. Time-tested, safe and secure.
Ada。经久耐用、安全可靠。


一些语言特性经常被误解,导致常见的编程错误、性能下降和可移植性问题。以下这些 Ada 语言的错误用法,经常出现在 Ada 初学者编写的代码中。

pragma Atomic & Volatile

[编辑 | 编辑源代码]

使用 原子易变 变量进行 任务处理 几乎总是错误的。[1] 当一个对象是原子的,它仅仅意味着它将以原子方式从内存中读取或写入。编译器不会在访问该对象时生成原子指令或内存屏障,它只会

  • 检查体系结构是否保证原子内存加载和存储,
  • 禁止一些编译器优化,例如对对象的重新排序或抑制冗余访问。

例如,以下代码,其中A是一个原子对象,可能会被误解

A := A + 1;  -- Not an atomic increment!

编译器不会(并且根据标准不允许)生成原子增量指令来直接从内存中增量并更新变量A[2] 这是编译器生成的代码

  A := A + 1;
804969f:	a1 04 95 05 08       	mov    0x8059504,%eax
80496a4:	40                   	inc    %eax
80496a5:	a3 04 95 05 08       	mov    %eax,0x8059504

可以看到,不会生成任何原子增量指令或测试并设置操作码。与其他编程语言一样,如果程序中需要这些特定指令,则必须使用机器代码插入显式编写。[3]

上面的代码片段等同于以下代码(两个代码序列生成完全相同的目标代码),其中T是一个(非原子)临时变量

T := A;      -- A is copied atomically to local variable T
T := T + 1;  -- local variable T is incremented
A := T;      -- A is stored atomically

因此,从多个任务同时修改原子变量是错误的。例如,两个任务并行递增一个计数器。即使在单处理器上,也应该使用其他 Ada 任务处理特性,例如保护对象。在多处理器中,根据内存一致性模型,使用各种原子或易变变量进行任务通信可能会产生意想不到的结果。[2][4] 因此,在使用原子对象进行任务数据共享或同步时,尤其是在多处理器中,应格外小心。

参考文献

[编辑 | 编辑源代码]
  1. Arch Robison (2007-11-30). "易变:对于多线程编程几乎无用". Intel 软件网络. 检索于 2008-05-30. 有一种普遍的观点认为,关键字易变对于多线程编程很有用 (...) 易变对于多线程编程几乎无用。
  2. a b Ian Lance Taylor (2008-03-05). "易变". 检索于 2008-05-28. 使用 [C/C++ 限定符] 易变并不意味着变量以原子方式访问;不使用锁。使用易变并不意味着多核系统中的其他内核将看到内存访问;不使用缓存刷新。 (...) 使用易变不意味着任何类型的内存屏障;处理器可以并且将会重新排列易变内存访问。 (...) 你不应该使用多个此类变量在任何一对线程之间进行通信,因为不能保证不同的线程会以相同的顺序看到访问。
  3. Laurent Guerby (1995). "C.5 共享变量控制". Ada 95 Rationale. Intermetrics. 有时需要访问特定的机器指令 (...)。例如,对共享内存执行复合操作的原子指令,例如测试并设置和比较并交换 (...) {{cite book}}: |access-date= requires |url= (帮助); External link in |chapter= (帮助); Unknown parameter |month= ignored (帮助)
  4. Sarita V. Adve, Kourosh Gharachorloo (1996). "共享内存一致性模型:教程" (PDF). IEEE Computer. 29 (12): 66–76. 检索于 2008-05-28. {{cite journal}}: Unknown parameter |month= ignored (帮助)

pragma Pack

[编辑 | 编辑源代码]

精确的数据表示

[编辑 | 编辑源代码]

重要的是要认识到,pragma Pack 不应该用来指定数据类型的精确表示,而是用来帮助编译器提高生成代码的效率。[1] 编译器可以自由地忽略该 pragma,因此,如果需要类型的特定表示,则应该使用表示子句(记录表示子句,和/或属性 'Size'Component_Size)。

按位操作

[编辑 | 编辑源代码]

尽管在 Ada 83 中,打包布尔数组被用来进行按位操作,[2] 但是从 Ada 95 开始,模类型 更适合这些操作。 [3] 可以权衡使用命名布尔数组索引的优势,例如Traffic_Lights'(Red => True,others => False), 取决于用例。

'Bit_Order 属性

[edit | edit source]

'Bit_Order 属性并非旨在将数据在大小端机器之间进行转换(它影响的是位编号,而不是字节顺序)。当指定非本机位顺序时,编译器不会生成代码来重新排序多字节字段。[4][5][6]

参考文献

[edit | edit source]
  1. Adam Beneschan (2008-01-09). "Pragma Pack vs. Convention C, portability issue?". comp.lang.ada. (Web link). Retrieved on 2008-05-27.
  2. 软件生产力联盟 (1995 年 10 月)。Ada 95 质量和风格指南,"10.5.7 打包布尔数组移位"
  3. 软件生产力联盟 (1995 年 10 月)。Ada 95 质量和风格指南,"10.6.3 模块类型上的位操作"
  4. AI95-00133-01 (1996-05-07). "控制位排序". Class: binding interpretation. Ada Rapporteur Group. Bit_Order 子句与位的编号有关,与数据翻转互操作性无关。
  5. ISO/IEC 8652:2007. "13.5.3 位排序 (9/2)". Ada 2005 参考手册. Retrieved 2008-06-02. Bit_Order 子句使编写可以在具有不同位排序的机器之间移植的 record_representation_clauses 成为可能。它们不保证在这些机器之间透明地交换数据。 {{cite book}}: Unknown parameter |chapterurl= ignored (|chapter-url= suggested) (help)
  6. Thomas Quinot (2013). "Gem #140: Bridging the Endianness Gap". AdaCore. Retrieved 2013-01-31. 构成机器标量的字节写入内存的顺序不会被 Bit_Order 属性更改——只有机器标量中位的索引会更改。 {{cite web}}: Unknown parameter |month= ignored (help)


'Size 属性

[edit | edit source]

Ada 编程中的一个常见错误是假设指定类型 T 的 'Size 会强制编译器为该类型对象的分配正好是这个数量的位。这不是真的。指定的 T'Size 将强制编译器在打包数组和记录以及 Unchecked_Conversion 中使用此大小,但编译器仍然可以自由地为独立对象分配更多位。

对对象本身使用 'Size 来强制对象为指定的值。

另请参阅

[edit | edit source]

维基教科书

[edit | edit source]

参考文献

[edit | edit source]


华夏公益教科书