www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why is dynamic array length required here?

reply Samir <samir aol.com> writes:
I am working my way through the exercises in the "Programming in 
D" tutorial (http://ddili.org/ders/d.en/arrays.html).  Why is the 
line assigning the length of the dynamic array required?

/*
Write a program that asks the user how many values will be 
entered and then
reads all of them. Have the program sort the elements using 
sort() and then
reverse the sorted elements using reverse().
*/

import std.stdio;
import std.algorithm;

void main() {
     int noValues, i;
     int[] myArray;

     write("how many values would you like to enter? ");
     readf(" %s", &noValues);
     myArray.length = noValues; // I get a run-time error if I 
comment this out

     while (i < noValues) {

         readf(" %s", &myArray[i]);
         ++i;
     }

     sort(myArray);
     writeln(myArray);
     reverse(myArray);
     writeln(myArray);

}

Without the line:

myArray.length = noValues;

I get the run-time error:

$ ./exArrays1_1
how many values would you like to enter? 5
core.exception.RangeError exArrays1_1.d(12): Range violation
----------------
??:? _d_arrayboundsp [0x461772]
??:? _Dmain [0x44c284]

I would have thought that since this is a dynamic array, I don't 
need to pre-assign its length.

Thanks
Oct 18 2018
next sibling parent Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Friday, 19 October 2018 at 02:04:37 UTC, Samir wrote:
 I am working my way through the exercises in the "Programming 
 in D" tutorial (http://ddili.org/ders/d.en/arrays.html).  Why 
 is the line assigning the length of the dynamic array required?
[...]
 Without the line:

 myArray.length = noValues;

 I get the run-time error...
 I would have thought that since this is a dynamic array, I 
 don't need to pre-assign its length.
Even though the array is dynamic, it doesn't have infinite storage. So you either: - preallocate required storage, like in that example code (note that it's not the same as making a fixed-size ('static') array: in your case you only learn what the required length is at run-time, as opposed to static arrays where you must know it at compile time) - don't preallocate, but instead append new elements like this: myArray ~= newValue; The latter isn't a universally "good" advice: it may cause reallocation every time you do that. That's why it's almost always best to preallocate in advance if you do know the length, or use something like std.array.appender that attempts to reduce memory reallocation. I'm not familiar with Ali's tutorials, maybe he talks about the appender in some future examples.
Oct 18 2018
prev sibling next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 10/18/2018 07:04 PM, Samir wrote:

      myArray.length = noValues; // I get a run-time error if I comment
 this out
It's because the expression that reads the elements below is readf, which reads on top of an existing element.
      while (i < noValues) {

          readf(" %s", &myArray[i]);
Aside: It may be possible to read like the following without taking the address, as readf has been improved to take references since that code was written: readf(" %s", myArray[i]); // (not tested) So, the answer to your question is, we need to set the length of the array so that there are elements to read on top of. If D had function templates like getResponse() that I use in the Templates chapter, http://ddili.org/ders/d.en/templates.html then the best thing to do would be to append the elements directly to the array: // No need to set .length beforehand myArray ~= getResponse!int(/* ... */); Ali
Oct 18 2018
prev sibling next sibling parent Mike Parker <aldacron gmail.com> writes:
On Friday, 19 October 2018 at 02:04:37 UTC, Samir wrote:
 I would have thought that since this is a dynamic array, I 
 don't need to pre-assign its length.

 Thanks
Just to expand on the previous answers, a dynamic array declaration with no initializer is an empty array: int[] arr; assert(arr.length == 0); No memory has been allocated to store any elements. Appending elements will allocate memory. In fact, the runtime will allocate more than needed so that it doesn't have to allocate on each append. arr ~= 1; assert(arr.length == 1); writeln(arr.capacity); When the capacity is 0, the next append will trigger another allocation. The reserve function can be used to allocate enough memory for a number of elements, but the array will still be empty: int[] arr; arr.reserve(100); assert(arr.length == 0); writeln(arr.capacity); Now 100+ elements can be appended without triggering an allocation. Setting the length directly as Ali's example does means that not only is the required memory allocated, but also that the array is not empty. int[] arr; arr.length = 100; assert(arr.length == 100); writeln(arr.capacity); And now the array can elements can be written to directly via the index operator, and a pointer to each element can be referenced as it is in the call to readf. Note that it's still possible to write elements directly to a dynamic array without the append operator after memory has been reserved, but it completely bypasses the runtime. void main() { int[] myArray; myArray.reserve(10); foreach(i; 0 .. 10) { *(myArray.ptr + i) = i; } assert(myArray == []); assert(myArray.length == 0); foreach(i; 0 .. 10) { writeln(*(myArray.ptr + i)); } } This is essentially treating myArray.ptr as a C array, with no bounds checking and no automatic memory management. Here be dragons.
Oct 18 2018
prev sibling parent Samir <samir aol.com> writes:
Stanislav, Ali, Mike -- Thank you all for your thoughtful and 
helpful replies to my queries.  Apologies that it has taken this 
long to reply to you.  I still haven't been able to find time to 
go through all of the code examples provided but hope to do so 
later this week.  If I have additional questions, I know where to 
go!

Thanks again!
Oct 21 2018