存储字符串引用的方法有多种,那么如何在示例代码中执行此操作?目前问题是存储对字符串的访问,因为它导致non-local pointer cannot point to local object
。存储'First
和'Last
是否更适合引用字符串?
此记录存储对字符串的引用。 First
和Last
应该指向一个字符串。我认为Name
应该是相同的,但是当为其分配本地字符串时,这将导致non-local pointer cannot point to local object
。因此,当前解决方案的解决方案是使用First
和Last
。
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
答案 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设计做出相同的批评:您可以使用指针识别子字符串的开头,但是您不能在子字符串的末尾粘贴空终结符而不会破坏原始字符串。
鉴于更大的图景......你需要什么,而不是你想要如何实现它的低级细节,可能有更好的解决方案。