存储字符串引用

时间:2016-01-09 16:24:08

标签: string reference ada

问题

存储字符串引用的方法有多种,那么如何在示例代码中执行此操作?目前问题是存储对字符串的访问,因为它导致non-local pointer cannot point to local object。存储'First'Last是否更适合引用字符串?

字符串引用存储

此记录存储对字符串的引用。 FirstLast应该指向一个字符串。我认为Name应该是相同的,但是当为其分配本地字符串时,这将导致non-local pointer cannot point to local object。因此,当前解决方案的解决方案是使用FirstLast

type Segment is record
   First : Positive;
   Last : Positive;
   Length : Natural := 0;
   Name : access String;
end record;

分配子字符串引用

注释行导致non-local pointer cannot point to local object。这是因为Item是本地的。 Source不是本地的,这是我希望子字符串引用的字符串。

procedure Find (Source : aliased String; Separator : Character; Last : out Natural; Item_Array : out Segment_Array) is
   P : Positive := Source'First;
begin
   for I in Item_Array'Range loop
      declare
         Item : aliased String := Separated_String_Next (Source, Separator, P);
      begin
         exit when Item'Length = 0;
         Item_Array (I).Length := Item'Length;
         Item_Array (I).First := Item'First;
         Item_Array (I).Last := Item'Last;
         --Item_Array (I).Name := Item'Access;
         Last := I;
      end;
   end loop;
end;

实施例

with Ada.Text_IO;
with Ada.Integer_Text_IO;

procedure Main is

   use Ada.Text_IO;
   use Ada.Integer_Text_IO;

   function Separated_String_Next (Source : String; Separator : Character; P : in out Positive) return String is
      A : Positive := P;
      B : Positive;
   begin
      while A <= Source'Last and then Source(A) = Separator loop
         A := A + 1;
      end loop;
      P := A;
      while P <= Source'Last and then Source(P) /= Separator loop
         P := P + 1;
      end loop;
      B := P - 1;
      while P <= Source'Last and then Source(P) = Separator loop
         P := P + 1;
      end loop;
      return Source (A .. B);
   end;

   type Segment is record
      First : Positive;
      Last : Positive;
      Length : Natural := 0;
      Name : access String;
   end record;

   type Segment_Array is array (Integer range <>) of Segment;

   procedure Find (Source : String; Separator : Character; Last : out Natural; Item_Array : out Segment_Array) is
      P : Positive := Source'First;
   begin
      for I in Item_Array'Range loop
         declare
            Item : aliased String := Separated_String_Next (Source, Separator, P);
         begin
            exit when Item'Length = 0;
            Item_Array (I).Length := Item'Length;
            Item_Array (I).First := Item'First;
            Item_Array (I).Last := Item'Last;
            --Item_Array (I).Name := Item'Access;
            Last := I;
         end;
      end loop;
   end;

   Source : String := ",,Item1,,,Item2,,Item3,,,,,,";
   Item_Array : Segment_Array (1 .. 100);
   Last : Natural;

begin

   Find (Source, ',', Last, Item_Array);

   Put_Line (Source);
   Put_Line ("Index First Last Name");
   for I in Item_Array (Item_Array'First .. Last)'Range loop
      Put (I, 5);
      Put (Item_Array (I).First, 6);
      Put (Item_Array (I).Last, 5);
      Put (" ");
      Put (Source (Item_Array (I).First .. Item_Array (I).Last));
      New_Line;
   end loop;

end;

输出

,,Item1,,,Item2,,Item3,,,,,,
Index First Last Name
    1     3    7 Item1
    2    11   15 Item2
    3    18   22 Item3

1 个答案:

答案 0 :(得分:2)

错误消息告诉您确切的错误:Item是在本地声明的字符串,即在堆栈上,并且您将其地址分配给访问类型(指针)。我希望我不需要解释为什么那样做不起作用。

即时答案 - 这不是错误的,但也不是最佳实践,是为存储池或堆上的新字符串分配空间 - 这是通过new完成的。

Item : access String := new String'(Separated_String_Next (Source, Separator, P));
...
Item_Array (I).Name := Item;

请注意,其他一些记录成员,至少Length似乎都是完全多余的,因为它只是其同名属性的副本,所以应该被删除(除非我有一部分图片可以不见了。

有更好的答案。有时您需要使用访问类型,并处理它们的对象生命周期以及它们可能出错的所有方式。但更常见的是,它们的出现暗示了设计中的某些东西可以得到改善:例如:

  • Unbounded_String可以更简单地管理您的字符串
  • 您可以将长度用作Segment记录的判别式,并将实际字符串(不是Access)存储在记录本身中
  • Ada.Containers是一个标准的容器库,用于抽象自己处理存储(就像在C ++中使用STL一样)。
  • 如果您确定需要访问类型,最好使用命名访问类型type Str_Access is access String; - 然后您可以创建特定于Str_Acc类型的存储池,并在一次操作中释放整个池,以简化对象生命周期管理并消除内存泄漏。

注意上面基本上“深度复制”源字符串的切片。如果特定需要“浅拷贝”它 - 即参考特定的子串 - 并且你可以保证它的对象寿命,这个答案不是你想要的。如果是,请澄清问题的意图。

对于“浅层副本”,问题中的方法基本上失败了,因为Item已经是堆栈上的深层副本。

我能看到的最接近的方法是使源字符串变为别名...你必须按照你希望每个Segment引用它...并将其访问权限传递给Find过程。

然后每个Segment成为First,Last,(冗余长度)的元组,并访问整个字符串(而不是子字符串)。

   procedure Find (Source : access String; Separator : Character; 
                   Last : out Natural; Item_Array : out Segment_Array) is
      P : Positive := Source'First;
   begin
      for I in Item_Array'Range loop
         declare
            Item : String := Separated_String_Next (Source.all, Separator, P);
         begin
            exit when Item'Length = 0;
...
            Item_Array (I).Name := Source;
            Last := I;
         end;
      end loop;
   end;

   Source : aliased String := ",,Item1,,,Item2,,Item3,,,,,,";
...
   Find (Source'access, ',', Last, Item_Array);

   for I in Item_Array (Item_Array'First .. Last)'Range loop
...
      Put (Item_Array (I).Name(Item_Array (I).First .. Item_Array (I).Last));
      New_Line;
   end loop;

Segment中提取字符串的助手可能会有用:

function get(S : Segment) return String is
begin
   return S.Name(S.First .. S.Last);
end get;
...
Put (get(Item_Array (I));

我可以看到这样一个设计的唯一理由是,要解析或解析的字符串集几乎不适合内存,因此必须避免重复。也许还有嵌入式编程或一些不鼓励甚至非法动态(堆)分配的纪律。

我看不到字符串中涉及地址算术的解决方案,因为数组不仅仅是它的内容 - 如果你指向它,你就会丢失属性。您可以对等效的C设计做出相同的批评:您可以使用指针识别子字符串的开头,但是您不能在子字符串的末尾粘贴空终结符而不会破坏原始字符串。

鉴于更大的图景......你需要什么,而不是你想要如何实现它的低级细节,可能有更好的解决方案。