通过System.Address传递的变量能否被修改?Ada代码编译错误排查与解决方案
传递
My_Var'Address给in out System.Address参数报错,如何通过地址修改变量? 我尝试通过将变量以
System.Address的形式传递给过程来修改变量,以下是测试代码:with Ada.Text_IO; with System; with System.Storage_Elements; procedure Main is procedure Modify ( Var : in out System.Address) is use System.Storage_Elements; begin Var := Var + 10; end Modify; My_Var : Integer := 10; begin -- Insert code here. Modify (My_Var'Address); Ada.Text_IO.Put_Line("My_Var is:" & Integer(My_Var)'Image ); end Main;编译器返回错误:
17:17 actual for "Var" must be a variable
我无法理解报错原因,因为作为实参的My_Var显然是一个变量。请问我需要如何修改代码,才能通过System.Address来修改My_Var?
注:本次测试的背景是我正在理解一个现有遗留项目中的接口模块,尽管可能有更好的方法实现需求,但我希望了解是否可以通过上述方式修改变量。
为什么会报错?
你误解了in out参数的要求和'Address属性的本质:
Modify过程的参数Var是in out System.Address,这意味着它需要一个可以被赋值修改的变量作为实参——因为过程内部会修改这个参数本身的值(你代码里写了Var := Var +10)。- 但
My_Var'Address并不是一个变量,它是My_Var的内存地址的常量表达式。哪怕My_Var是变量,它的地址在程序运行时(通常)是固定的,'Address属性返回的是一个只读的右值,不能被赋值修改,所以编译器会报错。
更关键的是:你的真实需求是通过地址修改My_Var的值,而不是修改My_Var的地址本身——原代码的逻辑方向完全错了。
正确的修改方式
我们需要调整Modify过程的设计:让它接收System.Address作为输入参数(不需要in out),然后通过这个地址访问并修改对应的Integer变量。在Ada中,最直接的方式是用Ada.Unchecked_Conversion将地址转换为对应类型的访问指针,然后通过指针修改变量值。
修改后的完整代码:
with Ada.Text_IO; with System; with Ada.Unchecked_Conversion; procedure Main is -- 定义Integer类型的访问指针 type Integer_Access is access all Integer; -- 定义地址到访问指针的转换函数 function To_Integer_Access is new Ada.Unchecked_Conversion (Source => System.Address, Target => Integer_Access); -- 修改后的Modify过程:接收变量的地址,修改地址指向的内容 procedure Modify (Var_Addr : in System.Address) is -- 将地址转换为可以操作Integer的指针 Var_Ptr : Integer_Access := To_Integer_Access(Var_Addr); begin -- 通过指针修改原变量的值(这里示例为加10) Var_Ptr.all := Var_Ptr.all + 10; end Modify; My_Var : Integer := 10; begin Modify (My_Var'Address); Ada.Text_IO.Put_Line("My_Var is:" & Integer(My_Var)'Image ); end Main;
代码解释
- 访问指针与转换:
Integer_Access是指向Integer的访问类型,To_Integer_Access函数把System.Address转换成这个指针类型,让我们可以通过地址直接操作对应的变量。 - 过程逻辑调整:
Modify现在接收地址作为输入,不再试图修改地址本身,而是通过指针访问原变量的内存,修改其值。 - 类型安全注意:使用
Unchecked_Conversion会绕过Ada的类型检查,所以你必须确保传入的地址确实指向一个Integer类型的变量——这也是遗留代码中这种写法需要小心的原因,但符合你当前理解遗留接口的需求。
另一种用System.Storage_Elements的实现(如果坚持用这个包)
如果你想基于原代码里的System.Storage_Elements包来实现,也可以直接操作内存字节,但写法更繁琐:
with Ada.Text_IO; with System; with System.Storage_Elements; with Ada.Unchecked_Conversion; procedure Main is use System.Storage_Elements; -- 定义与Integer大小匹配的字节数组类型 type Integer_Storage is array (1 .. Integer'Size / Storage_Element'Size) of Storage_Element; -- 定义Integer和字节数组的双向转换函数 function To_Storage is new Ada.Unchecked_Conversion(Integer, Integer_Storage); function From_Storage is new Ada.Unchecked_Conversion(Integer_Storage, Integer); procedure Modify (Var_Addr : in System.Address) is -- 将地址绑定到字节数组,直接操作内存 Var_Bytes : Integer_Storage with Address => Var_Addr; Current_Value : Integer := From_Storage(Var_Bytes); begin Current_Value := Current_Value + 10; Var_Bytes := To_Storage(Current_Value); end Modify; My_Var : Integer := 10; begin Modify (My_Var'Address); Ada.Text_IO.Put_Line("My_Var is:" & Integer(My_Var)'Image ); end Main;
这种方式本质是把变量的内存当作字节数组来读写,和指针方式的效果一致,但可读性稍差。
内容的提问来源于stack exchange,提问作者MVSR
相关产品推荐
相关产品推荐

