3

What is the correct syntax for initializing a dynamically allocated array in Ada? I have tried this:

type Short_Array is array (Natural range <>) of Short;
Items : access Short_Array;
Items := new Short_Array(1..UpperBound)'(others => 0);

which results in a compiler error - "binary operator expected". And this:

type Short_Array is array (Natural range <>) of Short;
Items : access Short_Array;
Items := new Short_Array(1..UpperBound);
Items.all := (others => 0);

which seems to raise a SEGFAULT surprisingly. Not sure what's going on there but wanted to get the syntax right before I start chasing my tail.

Scott L.
  • 265
  • 2
  • 9
  • Why are you dynamically allocating an array? – Jim Rogers Dec 02 '16 at 02:27
  • Your second version works fine here, macOS, GNAT GPL 2015/2016. What OS/compiler are you using? – Simon Wright Dec 02 '16 at 08:57
  • @JimRogers It is part of a record type that I do not know the size of at compile time. – Scott L. Dec 02 '16 at 16:05
  • @SimonWright Yeah, I just tried it in a separate test file vs. as part of my larger program and it worked fine. Not sure what the deal was. GNATMAKE GPL 2016 (20160515-49) distributed with GPS GPL from AdaCore – Scott L. Dec 02 '16 at 16:07

3 Answers3

7

If you are using Ada2012 you can do the following:

type Short_Array is array(Natural range <>) of Short with
   Default_Component_Value => 0;
Items : access Short_Array := new Short_Array(1..UpperBound);

The use of default initial values for arrays is explained in section 2.6 of the Ada 2012 Rationale http://www.ada-auth.org/standards/12rat/html/Rat12-2-6.html

Jim Rogers
  • 4,822
  • 1
  • 11
  • 24
5

Another approach in Ada is to define the record as a discriminant record, with the discriminant determining the size of the array field.

type Items_Record (Size : Natural) is record
   -- Some non-array fields of your record
   Items : Short_Array(1..Size);
end record;

An instance of the record can then be allocated in an inner block

Get(Items_Size);
declare
   My_Record : Items_Record(Size => Items_Size);
begin
   -- Process the instance of Items_Record
end;

The record is dynamically allocated on the stack. If the record size is very large you will encounter a stack overflow issue. If not, this works very well. One advantage of this approach is the instance is automatically de-allocated when the end of the block is reached.

Jim Rogers
  • 4,822
  • 1
  • 11
  • 24
1

The SEGFAULT in your second example comes most certainly from the initialization.

Compare

type Short_Array is array (Natural range <>) of Short;
Items : access Short_Array;
Items := new Short_Array(1..UpperBound);
Items.all := (others => 0);

And this:

type Short_Array is array (Natural range <>) of Short;
Items : access Short_Array;
Items := new Short_Array(1..UpperBound);
for I in 1..UpperBound loop
   Items(I) := 0;
end loop;

And play with different values of ulimit -Ss which sets the allowed size of the stack.

The point is that

Items.all := (others => 0);

allocates an array on the stack and copy it in your heap-allocated array. So you think you are working on the heap but still needs a lot of stack. If your array is too big for your ulimit -Ss (or considering both soft and hard limits ulimit -s), you will segfault (or get a STORAGE_ERROR) though you think you are all on the heap.

To mitigate this problem, (though it might not work in every situation, eg if UpperBound is dynamic…), you can compile your code either with:

  • "-fstack-usage" (to get usage information for each unit)
  • "-Wstack-usage=2000" (or any limit more accurate to your case, see gnat's documentation for more infos) to yield warnings for functions using too much stack (or having unbounded stack usage).

The second option could have issued a warning and pointed you to your stack overflow.

Vser
  • 578
  • 4
  • 18
  • 1
    BTW, if anyone knows of a static analysis tool that could detect this error, I'm eager to hear! AFAICT neither gnatcheck nor codepeer (level 4) could detect this. – Vser Jul 06 '20 at 11:23