digitalmars.D.learn - Why is dynamic array length required here?
- Samir (41/41) Oct 18 2018 I am working my way through the exercises in the "Programming in
- Stanislav Blinov (17/25) Oct 18 2018 Even though the array is dynamic, it doesn't have infinite
- =?UTF-8?Q?Ali_=c3=87ehreli?= (16/21) Oct 18 2018 It's because the expression that reads the elements below is readf,
- Mike Parker (50/53) Oct 18 2018 Just to expand on the previous answers, a dynamic array
- Samir (7/7) Oct 21 2018 Stanislav, Ali, Mike -- Thank you all for your thoughtful and
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
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
On 10/18/2018 07:04 PM, Samir wrote:myArray.length = noValues; // I get a run-time error if I comment this outIt'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
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. ThanksJust 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
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