Ada 编程/错误
一些语言特性经常被误解,导致常见的编程错误、性能下降和可移植性问题。以下这些 Ada 语言的错误用法,经常出现在 Ada 初学者编写的代码中。
使用 原子 或 易变 变量进行 任务处理 几乎总是错误的。[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] 因此,在使用原子对象进行任务数据共享或同步时,尤其是在多处理器中,应格外小心。
- ↑ Arch Robison (2007-11-30). "易变:对于多线程编程几乎无用". Intel 软件网络. 检索于 2008-05-30.
有一种普遍的观点认为,关键字易变对于多线程编程很有用 (...) 易变对于多线程编程几乎无用。
- ↑ a b Ian Lance Taylor (2008-03-05). "易变". 检索于 2008-05-28.
使用 [C/C++ 限定符] 易变并不意味着变量以原子方式访问;不使用锁。使用易变并不意味着多核系统中的其他内核将看到内存访问;不使用缓存刷新。 (...) 使用易变不意味着任何类型的内存屏障;处理器可以并且将会重新排列易变内存访问。 (...) 你不应该使用多个此类变量在任何一对线程之间进行通信,因为不能保证不同的线程会以相同的顺序看到访问。
- ↑ Laurent Guerby (1995). "C.5 共享变量控制". Ada 95 Rationale. Intermetrics.
有时需要访问特定的机器指令 (...)。例如,对共享内存执行复合操作的原子指令,例如测试并设置和比较并交换 (...)
{{cite book}}
:|access-date=
requires|url=
(帮助); External link in
(帮助); Unknown parameter|chapter=
|month=
ignored (帮助) - ↑ Sarita V. Adve, Kourosh Gharachorloo (1996). "共享内存一致性模型:教程" (PDF). IEEE Computer. 29 (12): 66–76. 检索于 2008-05-28.
{{cite journal}}
: Unknown parameter|month=
ignored (帮助)
重要的是要认识到,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]- ↑ Adam Beneschan (2008-01-09). "Pragma Pack vs. Convention C, portability issue?". comp.lang.ada. (Web link). Retrieved on 2008-05-27.
- ↑ 软件生产力联盟 (1995 年 10 月)。Ada 95 质量和风格指南,"10.5.7 打包布尔数组移位"
- ↑ 软件生产力联盟 (1995 年 10 月)。Ada 95 质量和风格指南,"10.6.3 模块类型上的位操作"
- ↑ AI95-00133-01 (1996-05-07). "控制位排序". Class: binding interpretation. Ada Rapporteur Group.
Bit_Order 子句与位的编号有关,与数据翻转互操作性无关。
- ↑ 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) - ↑ 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]