$(DDOC $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(SPEC_S Arrays, $(DDOC_BLANKLINE ) $(HEADERNAV_TOC $(HEADERNAV_SUBITEMS array-kinds, Kinds, $(HEADERNAV_ITEM static-arrays, Static Arrays) $(HEADERNAV_ITEM dynamic-arrays, Dynamic Arrays) $(HEADERNAV_ITEM pointers, Pointer Arrays) ) $(HEADERNAV_ITEM declarations, Array Declarations) $(HEADERNAV_ITEM literals, Array Literals) $(HEADERNAV_ITEM assignment, Array Assignment) $(HEADERNAV_SUBITEMS indexing, Indexing, $(HEADERNAV_ITEM pointer-arithmetic, Pointer Arithmetic) ) $(HEADERNAV_ITEM slicing, Slicing) $(HEADERNAV_ITEM array-length, Array Length) $(HEADERNAV_SUBITEMS array-copying, Array Copying, $(HEADERNAV_ITEM overlapping-copying, Overlapping Copying) ) $(HEADERNAV_ITEM array-setting, Array Setting) $(HEADERNAV_ITEM array-concatenation, Array Concatenation) $(HEADERNAV_ITEM array-appending, Array Appending) $(HEADERNAV_ITEM array-operations, Vector Operations) $(HEADERNAV_ITEM rectangular-arrays, Rectangular Arrays) $(HEADERNAV_SUBITEMS array-properties, Array Properties, $(HEADERNAV_ITEM resize, Setting Dynamic Array Length) $(HEADERNAV_ITEM capacity-reserve, capacity and reserve) $(HEADERNAV_ITEM func-as-property, Functions as Array Properties) ) $(HEADERNAV_SUBITEMS bounds, Array Bounds Checking, $(HEADERNAV_ITEM disable-bounds-check, Disabling Array Bounds Checking) ) $(HEADERNAV_SUBITEMS array-initialization, Array Initialization, $(HEADERNAV_ITEM default-initialization, Default Initialization) $(HEADERNAV_ITEM length-initialization, Length Initialization) $(HEADERNAV_ITEM void-initialization, Void Initialization) $(HEADERNAV_ITEM static-init-static, Static Initialization of Statically Allocated Arrays) ) $(HEADERNAV_SUBITEMS special-array, Special Array Types, $(HEADERNAV_ITEM strings, Strings) $(HEADERNAV_ITEM void_arrays, Void Arrays) ) $(HEADERNAV_ITEM implicit-conversions, Implicit Conversions) ) $(DDOC_BLANKLINE )

$(LNAME2 array-kinds, Kinds)

$(DDOC_BLANKLINE ) $(P There are four kinds of arrays:) $(DDOC_BLANKLINE ) $(TABLE2 Kinds of Arrays, Syntax, Description $(TROW $(ARGS $(I type)[$(I integer)]), $(ARGS $(RELATIVE_LINK2 static-arrays, Static arrays))) $(TROW $(ARGS $(I type)[]), $(ARGS $(RELATIVE_LINK2 dynamic-arrays, Dynamic arrays))) $(TROW $(ARGS $(I type)*), $(ARGS $(RELATIVE_LINK2 pointers, Pointer arrays))) $(TROW $(ARGS $(I type)[$(I type)]), $(ARGS $(DDLINK spec/hash-map, Associative Arrays, Associative arrays))) ) $(DDOC_BLANKLINE )

$(LNAME2 static-arrays, Static Arrays)

$(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int)[3] s; ) ) $(DDOC_BLANKLINE ) $(P Static arrays have a length fixed at compile time. ) $(DDOC_BLANKLINE ) $(P The total size of a static array cannot exceed 16Mb. ) $(DDOC_BLANKLINE ) $(P A static array with a dimension of 0 is allowed, but no space is allocated for it. ) $(DDOC_BLANKLINE ) $(P Static arrays are value types. They are passed to and returned by functions by value. ) $(DDOC_BLANKLINE ) $(BEST_PRACTICE $(OL $(LI Use dynamic arrays for larger arrays.) $(LI Static arrays with 0 elements are useful as the last member of a variable length struct, or as the degenerate case of a template expansion.) $(LI Because static arrays are passed to functions by value, a larger array can consume a lot of stack space. Use dynamic arrays instead.) )) $(DDOC_BLANKLINE )

$(LNAME2 dynamic-arrays, Dynamic Arrays)

$(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int)[] a; ) ) $(DDOC_BLANKLINE ) $(P Dynamic arrays consist of a length and a pointer to the array data. Multiple dynamic arrays can share all or parts of the array data. ) $(DDOC_BLANKLINE ) $(BEST_PRACTICE $(OL $(LI Use dynamic arrays instead of pointer arrays as much as practical. Indexing of dynamic arrays are bounds checked, avoiding buffer underflow and overflow problems.) )) $(DDOC_BLANKLINE )

$(LNAME2 pointers, Pointer Arrays)

$(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int)* p; ) $(DDOC_BLANKLINE ) $(P A $(DDSUBLINK spec/type, pointers, pointer) can manipulate a block of multiple contiguous values in memory. Accessing more than one value cannot be $(DDLINK spec/memory-safe-d, Memory-Safe-D-Spec, @safe) as it requires $(RELATIVE_LINK2 pointer-arithmetic, pointer arithmetic). This is supported for interfacing with C and for specialized systems work. A pointer has no length associated with it, so there is no way for the compiler or runtime to do bounds checking, etc., on it. ) $(DDOC_BLANKLINE ) $(BEST_PRACTICE Most conventional uses for pointers can be replaced with dynamic arrays, $(D ref) and $(D out) $(DDSUBLINK spec/function, parameters, parameters), and reference types. ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 declarations, Array Declarations)

$(DDOC_BLANKLINE ) $(P Declarations appear before the identifier being declared and read right to left, so: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int)[] a; $(D_COMMENT // dynamic array of ints )$(D_KEYWORD int)[4][3] b; $(D_COMMENT // array of 3 arrays of 4 ints each )$(D_KEYWORD int)[][5] c; $(D_COMMENT // array of 5 dynamic arrays of ints. )$(D_KEYWORD int)*[]*[3] d; $(D_COMMENT // array of 3 pointers to dynamic arrays of pointers to ints )$(D_KEYWORD int)[]* e; $(D_COMMENT // pointer to dynamic array of ints )) ) $(DDOC_BLANKLINE )

$(LNAME2 literals, Array Literals)

$(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD auto) a1 = [1,2,3]; $(D_COMMENT // type is int[], with elements 1, 2, and 3 )$(D_KEYWORD auto) a2 = [1u,2,3]; $(D_COMMENT // type is uint[], with elements 1u, 2u, and 3u )$(D_KEYWORD int)[2] a3 = [1,2]; $(D_COMMENT // type is int[2], with elements 1, and 2 )) $(P [] is an empty array literal.) $(DDOC_BLANKLINE ) $(P See $(DDSUBLINK spec/expression, array_literals, Array Literals).) $(DDOC_BLANKLINE ) $(LEGACY_LNAME2 usage)

$(LNAME2 assignment, Array Assignment)

$(DDOC_BLANKLINE ) $(P There are two broad kinds of operations to do on dynamic arrays and pointer arrays - those affecting the handle to the array, and those affecting the contents of the array. Assignment only affects the handle for these types. ) $(DDOC_BLANKLINE ) $(P The .ptr property for static and dynamic arrays will give the address of the first element in the array:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)* p; $(D_KEYWORD int)[3] s; $(D_KEYWORD int)[] a; p = s.ptr; $(D_COMMENT // p points to the first element of the array s. )p = a.ptr; $(D_COMMENT // p points to the first element of the array a. ) $(D_COMMENT // error, since the length of the array pointed to by p is unknown )$(D_COMMENT //s = p; ) $(D_COMMENT //a = p; // error, length unknown )a = s; $(D_COMMENT // a points to the elements of s )$(D_KEYWORD assert)(a.ptr == s.ptr); $(D_KEYWORD int)[] b; a = b; $(D_COMMENT // a points to the same array as b does )$(D_KEYWORD assert)(a.ptr == b.ptr); $(D_KEYWORD assert)(a == []); ) ) $(NOTE The two error lines above can be made to copy elements using pointer $(RELATIVE_LINK2 slicing, slicing), so that the number of elements to copy is then known.) $(DDOC_BLANKLINE ) $(P A static array can be assigned from a dynamic array - the data is copied. The lengths must match:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[3] s; $(D_KEYWORD int)[] a; $(D_COMMENT //s = [1, 2]; // error )s = [1, 2, 3]; $(D_COMMENT // OK )$(D_COMMENT //s = [1, 2, 3, 4]; // error ) a = [4, 5, 6]; s = a; $(D_COMMENT // OK )$(D_KEYWORD assert)(s.ptr != a.ptr); a = [1, 2]; $(D_COMMENT //s = a; // RangeError, length mismatch ) a = s; $(D_KEYWORD assert)(a.ptr == s.ptr); $(D_COMMENT //s = a; // RangeError, overlap )) ) $(P The dynamic array data must not $(RELATIVE_LINK2 overlapping-copying, overlap) with the static array memory. See also $(RELATIVE_LINK2 array-copying, Copying).) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 indexing, Indexing)

$(DDOC_BLANKLINE ) $(P Indexing allows access to an element of an array:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD auto) a = [1,2,3]; $(D_KEYWORD assert)(a[0] == 1); $(D_KEYWORD assert)(a[2] == 3); a[2] = 4; $(D_KEYWORD assert)(a[2] == 4); $(D_KEYWORD assert)(a == [1,2,4]); $(D_COMMENT //writeln$(LPAREN)a[3]$(RPAREN ); // runtime error $(LPAREN)unless bounds checks turned off$(RPAREN ) ) $(D_KEYWORD int)[2] b = [1,2]; $(D_KEYWORD assert)(b[1] == 2); $(D_COMMENT //writeln$(LPAREN)b[2]$(RPAREN ); // compile-time error, index out of bounds )) ) $(DDOC_BLANKLINE ) $(P See also $(GLINK2 expression, IndexOperation).) $(DDOC_BLANKLINE )

$(LNAME2 pointer-arithmetic, Pointer Arithmetic)

$(DDOC_BLANKLINE ) $(P A pointer can also be indexed, but no bounds checks are done. Unlike arrays, a pointer value can also be used in certain arithmetic expressions to produce another pointer:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[] a = [1,2,3]; $(D_KEYWORD int)* p = a.ptr; p[2] = 4; $(D_KEYWORD assert)(a[2] == 4); writeln(p[3]); $(D_COMMENT // undefined behaviour ) $(D_KEYWORD assert)(p == &a[0]); p++; $(D_COMMENT // point to a[1] )$(D_KEYWORD assert)(*p == 2); ) ) $(DDOC_BLANKLINE ) $(P See $(DDSUBLINK spec/expression, pointer_arithmetic, AddExpression) for details.) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 slicing, Slicing)

$(DDOC_BLANKLINE ) $(P $(I Slicing) an array means to specify a subarray of it. This is done by supplying two index expressions. The elements from the start index up until the end index are selected. Any item at the end index is not included. ) $(P An array slice does not copy the data, it is only another reference to it. Slicing produces a dynamic array. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[3] a = [4, 5, 6]; $(D_COMMENT // static array of 3 ints )$(D_KEYWORD int)[] b; b = a[1..3]; $(D_COMMENT // a[1..3] is a 2 element dynamic array consisting of ) $(D_COMMENT // a[1] and a[2] )$(D_KEYWORD assert)(b == [5, 6]); $(D_KEYWORD assert)(b.ptr == a.ptr + 1); a[2] = 3; $(D_KEYWORD assert)(b == [5, 3]); b = b[1..2]; $(D_KEYWORD assert)(b == [3]); ) ) $(DDOC_BLANKLINE ) $(P $(I Expression)[] is shorthand for a slice of the entire array. ) $(DDOC_BLANKLINE ) $(P Slicing is not only handy for referring to parts of other arrays, but for converting pointers into bounds-checked arrays: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[10] a = [ 1,2,3,4,5,6,7,8,9,10 ]; $(D_KEYWORD int)* p = &a[2]; writeln(p[7]); $(D_COMMENT // 10 )writeln(p[8]); $(D_COMMENT // undefined behaviour ) $(D_KEYWORD int)[] b = p[0..8]; $(D_COMMENT // convert pointer elements to dynamic array )$(D_KEYWORD assert)(b $(D_KEYWORD is) a[2..10]); writeln(b); writeln(b[7]); $(D_COMMENT // 10 )$(D_COMMENT //writeln$(LPAREN)b[8]$(RPAREN ); // runtime error $(LPAREN)unless bounds checks turned off$(RPAREN ) )) ) $(DDOC_BLANKLINE ) $(P See also $(GLINK2 expression, SliceOperation).) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 array-length, Array Length)

$(DDOC_BLANKLINE ) $(P When indexing or slicing a static or dynamic array, the symbol $(D $) represents the length of the array. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[4] foo; $(D_KEYWORD int)[] bar = foo; $(D_COMMENT // These expressions are equivalent: )bar = foo; bar = foo[]; bar = foo[0 .. 4]; bar = foo[0 .. $]; bar = foo[0 .. foo.length]; $(D_KEYWORD int)* p = foo.ptr; $(D_COMMENT //bar = p[0 .. $]; // error, '$' is not defined, since p is not an array ) $(D_KEYWORD int) i; $(D_COMMENT //i = foo[0]+$; // error, '$' is not defined, out of scope of [ ] )i = bar[$-1]; $(D_COMMENT // retrieves last element of the array )) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 array-copying, Array Copying)

$(DDOC_BLANKLINE ) $(P When the slice operator appears as the left-hand side of an assignment expression, it means that the contents of the array are the target of the assignment rather than a reference to the array. Array copying happens when the left-hand side is a slice, and the right-hand side is an array of or pointer to the same type. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[3] s, t; $(D_KEYWORD int)[] a; s = t; $(D_COMMENT // the 3 elements of t are copied into s )s[] = t; $(D_COMMENT // the 3 elements of t are copied into s )s[] = t[]; $(D_COMMENT // the 3 elements of t are copied into s )s[1..2] = t[0..1]; $(D_COMMENT // same as s[1] = t[0] )s[0..2] = t[1..3]; $(D_COMMENT // same as s[0] = t[1], s[1] = t[2] )$(D_COMMENT //s[0..4] = t[0..4]; // error, only 3 elements in s and t )$(D_COMMENT //s[0..2] = t; // error, operands have different lengths ) a = [1, 2]; s[0..2] = a; $(D_KEYWORD assert)(s == [1, 2, 0]); $(D_COMMENT //a[] = s; // RangeError, lengths don't match )a[0..2] = s[1..3]; $(D_KEYWORD assert)(a == [2, 0]); ) ) $(DDOC_BLANKLINE )

$(LNAME2 overlapping-copying, Overlapping Copying)

$(DDOC_BLANKLINE ) $(P Overlapping copies are an error:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) main() { $(D_KEYWORD int)[3] s; s[0..2] = s[1..3]; $(D_COMMENT // error, overlapping copy ) s[1..3] = s[0..2]; $(D_COMMENT // error, overlapping copy )} ) ) $(DDOC_BLANKLINE ) $(P Disallowing overlapping makes it possible for more aggressive parallel code optimizations than possible with the serial semantics of C. ) $(DDOC_BLANKLINE ) $(P If overlapping is required, use $(REF copy, std,algorithm,mutation): ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.algorithm; $(D_KEYWORD int)[] s = [1, 2, 3, 4]; copy(s[1..3], s[0..2]); $(D_KEYWORD assert)(s == [2, 3, 3, 4]); ) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 array-setting, Array Setting)

$(DDOC_BLANKLINE ) $(P If a slice operator appears as the left-hand side of an assignment expression, and the type of the right-hand side is the same as the element type of the left-hand side, then the array contents of the left-hand side are set to the right-hand side. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[3] s; $(D_KEYWORD int)[] a; $(D_KEYWORD int)* p; s[] = 3; $(D_KEYWORD assert)(s == [3, 3, 3]); a = s; a[] = 1; $(D_KEYWORD assert)(s == [1, 1, 1]); p = s.ptr; p[0..2] = 2; $(D_KEYWORD assert)(s == [2, 2, 1]); ) ) $(DDOC_BLANKLINE )

$(LNAME2 array-concatenation, Array Concatenation)

$(DDOC_BLANKLINE ) $(P The binary operator ~ is the $(I cat) operator. It is used to concatenate arrays: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[] a = [1, 2]; $(D_KEYWORD assert)(a ~ 3 == [1, 2, 3]); $(D_COMMENT // concatenate array with a single value ) $(D_KEYWORD int)[] b = a ~ [3, 4]; $(D_KEYWORD assert)(b == [1, 2, 3, 4]); $(D_COMMENT // concatenate two arrays )) ) $(DDOC_BLANKLINE ) $(P Many languages overload the + operator for concatenation. This confusingly leads to a dilemma - does: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_STRING "10") + 3 + 4 ) $(DDOC_BLANKLINE ) $(P produce the number 17, the string "1034" or the string "107" as the result? It isn't obvious, and the language designers wind up carefully writing rules to disambiguate it - rules that get incorrectly implemented, overlooked, forgotten, and ignored. It's much better to have + mean addition, and a separate operator to be array concatenation. ) $(DDOC_BLANKLINE ) $(P Concatenation always creates a copy of its operands, even if one of the operands is a 0 length array, so: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD auto) b = [7]; $(D_KEYWORD auto) a = b; $(D_COMMENT // a refers to b )$(D_KEYWORD assert)(a $(D_KEYWORD is) b); a = b ~ []; $(D_COMMENT // a refers to a copy of b )$(D_KEYWORD assert)(a !$(D_KEYWORD is) b); $(D_KEYWORD assert)(a == b); ) ) $(DDOC_BLANKLINE ) $(P See also: $(DDSUBLINK spec/expression, identity_expressions, is operator).) $(DDOC_BLANKLINE )

$(LNAME2 array-appending, Array Appending)

$(DDOC_BLANKLINE ) $(P Similarly, the ~= operator means append, as in: ) $(DDOC_BLANKLINE ) $(D_CODE a ~= b; $(D_COMMENT // a becomes the concatenation of a and b )) $(DDOC_BLANKLINE ) $(P Appending does not always create a copy, see $(RELATIVE_LINK2 resize, setting dynamic array length) for details. ) $(DDOC_BLANKLINE )

$(LNAME2 array-operations, Vector Operations)

$(DDOC_BLANKLINE ) $(P Many array operations can be expressed at a high level rather than as a loop. For example, the loop: ) $(DDOC_BLANKLINE ) $(D_CODE T[] a, b; ... $(D_KEYWORD for) (size_t i = 0; i < a.length; i++) a[i] = b[i] + 4; ) $(DDOC_BLANKLINE ) $(P assigns to the elements of $(CODE a) the elements of $(CODE b) with $(CODE 4) added to each. This can also be expressed in vector notation as: ) $(DDOC_BLANKLINE ) $(D_CODE T[] a, b; ... a[] = b[] + 4; ) $(DDOC_BLANKLINE ) $(P A vector operation is indicated by the slice operator appearing as the left-hand side of an assignment or an op-assignment expression. The right-hand side can be certain combinations of:) $(UL $(LI An array $(GLINK2 expression, SliceOperation) of the same length and type as the left-hand side) $(LI A scalar expression of the same element type as the left-hand side ) ) $(P The following operations are supported:) $(UL $(LI Unary: -, ~) $(LI Add: +, -) $(LI Mul: *, /, %,) $(LI Bitwise: ^, &, |) $(LI Pow: ^^ ) ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[3] a = 0; $(D_KEYWORD int)[] b = [1, 2, 3]; a[] += 10 - (b[] ^^ 2); $(D_KEYWORD assert)(a == [9, 6, 1]); ) ) $(DDOC_BLANKLINE ) $(NOTE In particular, an expression using $(GLINK2 expression, ConditionalExpression), $(DDSUBLINK spec/expression, logical_expressions, logical expressions), $(GLINK2 expression, CmpExpression), concatenation ~ or a function call is not a vector op.) $(DDOC_BLANKLINE ) $(P The slice on the left and any slices on the right must not overlap. All operands are evaluated exactly once, even if the array slice has zero elements in it. ) $(DDOC_BLANKLINE ) $(P If the element type defines matching overloaded operators, those methods must be pure nothrow @nogc.) $(DDOC_BLANKLINE ) $(P The order in which the array elements are computed is implementation defined, and may even occur in parallel. An application must not depend on this order. ) $(DDOC_BLANKLINE ) $(P $(B Implementation Note:) Many vector operations are expected to take advantage of any vector math instructions available on the target computer. ) $(DDOC_BLANKLINE )

$(LNAME2 rectangular-arrays, Rectangular Arrays)

$(DDOC_BLANKLINE ) $(P Experienced FORTRAN numerics programmers know that multidimensional "rectangular" arrays for things like matrix operations are much faster than trying to access them via pointers to pointers resulting from "array of pointers to array" semantics. For example, the D syntax: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD double)[][] matrix; ) ) $(DDOC_BLANKLINE ) $(P declares matrix as an array of pointers to arrays. (Dynamic arrays are implemented as pointers to the array data.) Since the arrays can have varying sizes (being dynamically sized), this is sometimes called "jagged" arrays. Even worse for optimizing the code, the array rows can sometimes point to each other! Fortunately, D static arrays, while using the same syntax, are implemented as a fixed rectangular layout in a contiguous block of memory: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD import) std.stdio : writeln; $(D_KEYWORD double)[6][3] matrix = 0; $(D_COMMENT // Sets all elements to 0. ) $(D_KEYWORD void) main() { writeln(matrix); $(D_COMMENT // [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]] )} ) ) $(DDOC_BLANKLINE ) $(P Note that dimensions and indices appear in opposite orders. Dimensions in the $(RELATIVE_LINK2 declarations, declaration) are read right to left whereas indices are read left to right: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD import) std.stdio : writeln; $(D_KEYWORD void) main() { $(D_KEYWORD double)[6][3] matrix = 0; matrix[2][5] = 3.14; $(D_COMMENT // Assignment to bottom right element. ) writeln(matrix); $(D_COMMENT // [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 3.14]] ) $(D_KEYWORD static) $(D_KEYWORD assert)(!$(D_KEYWORD __traits)(compiles, matrix[5][2])); $(D_COMMENT // Array index out of bounds. )} ) ) $(DDOC_BLANKLINE ) $(P More information can be found at $(LINK2 https://wiki.dlang.org/Dense_multidimensional_arrays, Dlang Wiki - Dense Multidimensional Arrays).) $(DDOC_BLANKLINE )

$(LNAME2 array-properties, Array Properties)

$(DDOC_BLANKLINE ) $(P Static array properties are:) $(DDOC_BLANKLINE ) $(TABLE_2COLS Static Array Properties, Property, Description $(TROW $(D .init), Returns an array literal with each element of the literal being the $(D .init) property of the array element type.) $(TROW $(D .sizeof), Returns the array length multiplied by the number of bytes per array element.) $(TROW $(D .length), Returns the number of elements in the array. This is a fixed quantity for static arrays. It is of type $(D size_t).) $(TROW $(D .ptr), Returns a pointer to the first element of the array.) $(TROW $(D .dup), Create a dynamic array of the same size and copy the contents of the array into it. The copy will have any immutability or const stripped. If this conversion is invalid the call will not compile.) $(TROW $(D .idup), Create a dynamic array of the same size and copy the contents of the array into it. The copy is typed as being immutable. If this conversion is invalid the call will not compile.) $(TROW $(D .tupleof), $(ARGS Returns an $(DDSUBLINK spec/template, homogeneous_sequences, lvalue sequence) of each element in the array: $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD void) foo($(D_KEYWORD int), $(D_KEYWORD int), $(D_KEYWORD int)) { $(D_COMMENT /* ... */) } $(D_KEYWORD int)[3] ia = [1, 2, 3]; foo(ia.tupleof); $(D_COMMENT // same as `foo$(LPAREN)1, 2, 3$(RPAREN );` ) $(D_KEYWORD float)[3] fa; $(D_COMMENT //fa = ia; // error )fa.tupleof = ia.tupleof; $(D_KEYWORD assert)(fa == [1F, 2F, 3F]); ) ) )) ) $(DDOC_BLANKLINE ) $(P Dynamic array properties are:) $(DDOC_BLANKLINE ) $(TABLE_2COLS Dynamic Array Properties, Property, Description $(TROW $(D .init), Returns $(D null).) $(TROW $(D .sizeof), $(ARGS Returns the size of the dynamic array reference, which is 8 in 32-bit builds and 16 on 64-bit builds.)) $(TROW $(D .length), Get/set number of elements in the array. It is of type $(D size_t).) $(TROW $(D .capacity), Returns the number of elements that can be appended to the array without reallocating. See $(RELATIVE_LINK2 capacity-reserve, here) for details.) $(TROW $(D .ptr), Returns a pointer to the first element of the array.) $(TROW $(D .dup), Create a dynamic array of the same size and copy the contents of the array into it. The copy will have any immutability or const stripped. If this conversion is invalid the call will not compile.) $(TROW $(D .idup), Create a dynamic array of the same size and copy the contents of the array into it. The copy is typed as being immutable. If this conversion is invalid the call will not compile.) ) $(DDOC_BLANKLINE ) $(P Examples:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD int)* p; $(D_KEYWORD int)[3] s; $(D_KEYWORD int)[] a; p.length; $(D_COMMENT // error, length not known for pointer )s.length; $(D_COMMENT // compile time constant 3 )a.length; $(D_COMMENT // runtime value ) p.dup; $(D_COMMENT // error, length not known )s.dup; $(D_COMMENT // creates an array of 3 elements, copies ) $(D_COMMENT // elements of s into it )a.dup; $(D_COMMENT // creates an array of a.length elements, copies ) $(D_COMMENT // elements of a into it )) ) $(DDOC_BLANKLINE )

$(LNAME2 resize, Setting Dynamic Array Length)

$(DDOC_BLANKLINE ) $(P The $(D .length) property of a dynamic array can be set as the left-hand side of an = operator: ) $(DDOC_BLANKLINE ) $(D_CODE array.length = 7; ) $(DDOC_BLANKLINE ) $(P This causes the array to be reallocated in place, and the existing contents copied over to the new array. If the new array length is shorter, the array is not reallocated, and no data is copied. It is equivalent to slicing the array:) $(DDOC_BLANKLINE ) $(D_CODE array = array[0..7]; ) $(DDOC_BLANKLINE ) $(P If the new array length is longer, the array is reallocated if necessary, preserving the existing elements. The new elements are filled out with the default initializer. ) $(DDOC_BLANKLINE )

$(LNAME2 growing, Growing an Array)

$(DDOC_BLANKLINE ) $(P To maximize efficiency, the runtime always tries to resize the array in place to avoid extra copying. It will do a copy if the new size is larger and either:) $(UL $(LI The array was not $(DDSUBLINK spec/garbage, op_involving_gc, allocated by the GC).) $(LI There is no spare $(RELATIVE_LINK2 capacity-reserve, capacity) for the array.) $(LI Resizing in place would overwrite valid data still accessible in another slice. ) ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD char)[] a = $(D_KEYWORD new) $(D_KEYWORD char)[20]; $(D_KEYWORD char)[] b = a[0..10]; $(D_KEYWORD char)[] c = a[10..20]; $(D_KEYWORD char)[] d = a; b.length = 15; $(D_COMMENT // always reallocates because extending in place would ) $(D_COMMENT // overwrite other data in a. )b[11] = 'x'; $(D_COMMENT // a[11] and c[1] are not affected )$(D_KEYWORD assert)(a[11] == $(D_KEYWORD char).init); d.length = 1; $(D_KEYWORD assert)(d.ptr == a.ptr); $(D_COMMENT // unchanged ) d.length = 20; $(D_COMMENT // also reallocates, because doing this will overwrite a and c )$(D_KEYWORD assert)(d.ptr != a.ptr); c.length = 12; $(D_COMMENT // may reallocate in place if space allows, because nothing ) $(D_COMMENT // was allocated after c. )c[5] = 'y'; $(D_COMMENT // may affect contents of a, but not b or d because those ) $(D_COMMENT // were reallocated. ) a.length = 25; $(D_COMMENT // This always reallocates because if c extended in place, ) $(D_COMMENT // then extending a would overwrite c. If c didn't ) $(D_COMMENT // reallocate in place, it means there was not enough space, ) $(D_COMMENT // which will still be true for a. )a[15] = 'z'; $(D_COMMENT // does not affect c, because either a or c has reallocated. )) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(P To guarantee copying behavior, use the .dup property to ensure a unique array that can be resized. ) $(DDOC_BLANKLINE ) $(NOTE These issues also apply to $(RELATIVE_LINK2 array-appending, appending arrays) with the ~= operator. Concatenation using the ~ operator is not affected since it always reallocates. ) $(DDOC_BLANKLINE ) $(P Resizing a dynamic array is a relatively expensive operation. So, while the following method of filling an array: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) fun() { $(D_KEYWORD int)[] array; $(D_KEYWORD while) (1) { $(D_KEYWORD import) core.stdc.stdio : getchar; $(D_KEYWORD auto) c = getchar; $(D_KEYWORD if) (!c) $(D_KEYWORD break); ++array.length; array[array.length - 1] = c; } } ) ) $(DDOC_BLANKLINE ) will work, it will be inefficient. A more practical approach would be to minimize the number of resizes: $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) fun() { $(D_KEYWORD int)[] array; array.length = 100; $(D_COMMENT // guess ) $(D_KEYWORD int) i; $(D_KEYWORD for) (i = 0; ; i++) { $(D_KEYWORD import) core.stdc.stdio : getchar; $(D_KEYWORD auto) c = getchar; $(D_KEYWORD if) (!c) $(D_KEYWORD break); $(D_KEYWORD if) (i == array.length) array.length *= 2; array[i] = c; } array.length = i; } ) ) $(DDOC_BLANKLINE ) $(P Base selection of the initial size on expected common use cases, which can be determined by instrumenting the code, or simply using good judgement. For example, when gathering user input from the console - it's unlikely to be longer than 80. ) $(DDOC_BLANKLINE )

$(LNAME2 capacity-reserve, capacity and reserve)

$(DDOC_BLANKLINE ) $(P The $(D capacity) property gives the maximum length a dynamic array can grow to without reallocating. If the array does not point to GC-allocated memory, the capacity will be zero. The spare capacity for an array a is a.capacity - a.length.) $(DDOC_BLANKLINE ) $(P By default, capacity will be zero if an element has been stored after the slice.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[] a; $(D_KEYWORD assert)(a.capacity == 0); a.length = 3; $(D_COMMENT // may allocate spare capacity too )$(D_KEYWORD assert)(a.capacity >= 3); $(D_KEYWORD auto) b = a[1..3]; $(D_KEYWORD assert)(b.capacity >= 2); $(D_COMMENT // either a or b can append into any spare capacity )b = a[0..2]; $(D_KEYWORD assert)(b.capacity == 0); ) ) $(DDOC_BLANKLINE ) $(RATIONALE This behaviour helps prevent accidental overwriting of elements in another slice. It is also necessary to protect immutable elements from being overwritten.) $(DDOC_BLANKLINE ) $(P The $(D reserve) function expands an array's capacity for use by the $(RELATIVE_LINK2 array-appending, append operator) or .length assignment.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[] array; $(D_KEYWORD const) size_t cap = array.reserve(10); $(D_COMMENT // request )$(D_KEYWORD assert)(cap >= 10); $(D_COMMENT // allocated may be more than request )$(D_KEYWORD assert)(array.ptr != $(D_KEYWORD null)); $(D_KEYWORD int)[] copy = array; $(D_KEYWORD assert)(copy.capacity == cap); $(D_COMMENT // array and copy have same capacity )array ~= [1, 2, 3, 4, 5]; $(D_COMMENT // grow in place )$(D_KEYWORD assert)(cap == array.capacity); $(D_COMMENT // array memory was not reallocated )$(D_KEYWORD assert)(copy.ptr == array.ptr); $(D_KEYWORD assert)(copy.capacity == 0); copy ~= 0; $(D_COMMENT // new allocation )$(D_KEYWORD assert)(copy.ptr != array.ptr); ) ) $(P Above, copy's length remains zero but it points to the same memory allocated by the reserve call. Because array is then appended to, copy.ptr + 0 no longer points to unused memory - instead that is the address of array[0]. So copy.capacity will be zero to prevent any appending to copy from overwriting elements in array.) $(DDOC_BLANKLINE ) $(NOTE The runtime uses the number of appended elements to track the start of the spare capacity for the memory allocation.) $(DDOC_BLANKLINE ) $(P When an array with spare capacity has its length reduced, or is assigned a slice of itself that ends before the previous last element, the capacity will be zero.) $(DDOC_BLANKLINE ) $(P The @system function $(REF1 assumeSafeAppend, object) allows the capacity to be regained, but care must be taken not to overwrite immutable elements that may exist in a longer slice.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[] a = [1, 2, 3]; a.length--; $(D_KEYWORD assert)(a.capacity == 0); a.assumeSafeAppend(); $(D_KEYWORD assert)(a.capacity >= 3); ) ) $(DDOC_BLANKLINE ) $(NOTE Accessing .capacity may require the runtime to acquire a global lock and perform a cache lookup.) $(DDOC_BLANKLINE ) $(BEST_PRACTICE Avoid intensive use of .capacity in performance-sensitive code. Instead, track the capacity locally when building an array via a unique reference.) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 func-as-property, Functions as Array Properties)

$(DDOC_BLANKLINE ) $(P See $(DDSUBLINK spec/function, pseudo-member, Uniform Function Call Syntax (UFCS)).) $(DDOC_BLANKLINE )

$(LNAME2 bounds, Array Bounds Checking)

$(DDOC_BLANKLINE ) $(P It is an error to index an array with an index that is less than 0 or greater than or equal to the array length. If an index is out of bounds, an ArrayIndexError is thrown if detected at runtime, and an error is raised if detected at compile time. A program may not rely on array bounds checking happening, for example, the following program is incorrect: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) main() { $(D_KEYWORD import) core.exception; $(D_KEYWORD try) { $(D_KEYWORD auto) array = [1, 2]; $(D_KEYWORD for) ($(D_KEYWORD auto) i = 0; ; i++) { array[i] = 5; } } $(D_KEYWORD catch) (ArrayIndexError) { $(D_COMMENT // terminate loop ) } } ) ) $(DDOC_BLANKLINE ) The loop is correctly written: $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) main() { $(D_KEYWORD auto) array = [1, 2]; $(D_KEYWORD for) ($(D_KEYWORD auto) i = 0; i < array.length; i++) { array[i] = 5; } } ) ) $(DDOC_BLANKLINE ) $(P $(B Implementation Note:) Compilers should attempt to detect array bounds errors at compile time, for example: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD int)[3] foo; $(D_KEYWORD int) x = foo[3]; $(D_COMMENT // error, out of bounds )) ) $(DDOC_BLANKLINE ) $(P Insertion of array bounds checking code at runtime should be turned on and off with a compile time switch. ) $(DDOC_BLANKLINE ) $(UNDEFINED_BEHAVIOR An out of bounds memory access will cause undefined behavior, therefore array bounds check is normally enabled in @safe functions. The runtime behavior is part of the language semantics. ) $(DDOC_BLANKLINE ) $(P See also $(DDSUBLINK spec/function, safe-functions, Safe Functions).) $(DDOC_BLANKLINE )

$(LNAME2 disable-bounds-check, Disabling Array Bounds Checking)

$(DDOC_BLANKLINE ) $(P Insertion of array bounds checking code at runtime may be turned off with a compiler switch $(LINK2 $(ROOT_DIR )dmd.html#switch-boundscheck, -boundscheck). ) $(DDOC_BLANKLINE ) $(P If the bounds check in @system or @trusted code is disabled, the code correctness must still be guaranteed by the code author. ) $(DDOC_BLANKLINE ) $(P On the other hand, disabling the bounds check in @safe code will break the guaranteed memory safety by compiler. It's not recommended unless motivated by speed measurements. ) $(DDOC_BLANKLINE )

$(LNAME2 array-initialization, Array Initialization)

$(DDOC_BLANKLINE )

$(LNAME2 default-initialization, Default Initialization)

$(DDOC_BLANKLINE ) $(UL $(LI Pointers are initialized to $(D null).) $(LI Static array contents are initialized to the default initializer for the array element type.) $(LI Dynamic arrays are initialized to having 0 elements.) $(LI Associative arrays are initialized to having 0 elements.) ) $(DDOC_BLANKLINE )

$(LNAME2 length-initialization, Length Initialization)

$(P The $(D new) expression can be used to start a dynamic array with a specified length by specifying its type and then using the (size) syntax: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD int)[] i = $(D_KEYWORD new) $(D_KEYWORD int)[](5); $(D_COMMENT //i.length == 5 )$(D_KEYWORD int)[][] j = $(D_KEYWORD new) $(D_KEYWORD int)[][](10, 5); $(D_COMMENT //j.length == 10, j[0].length == 5 )) ) $(DDOC_BLANKLINE )

$(LNAME2 void-initialization, Void Initialization)

$(DDOC_BLANKLINE ) $(P Void initialization happens when the $(I Initializer) for an array is $(D void). What it means is that no initialization is done, i.e. the contents of the array will be undefined. This is most useful as an efficiency optimization. Void initializations are an advanced technique and should only be used when profiling indicates that it matters. ) $(P To void initialise the elements of a dynamic array use $(REF uninitializedArray, std,array). ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 static-init-static, Static Initialization of Statically Allocated Arrays)

$(DDOC_BLANKLINE ) $(P Static initalizations are supplied by a list of array element values enclosed in [ ]. The values can be optionally preceded by an index and a :. If an index is not supplied, it is set to the previous index plus 1, or 0 if it is the first value. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[3] a = [ 1:2, 3 ]; $(D_COMMENT // a[0] = 0, a[1] = 2, a[2] = 3 ) $(D_KEYWORD assert)(a == [0, 2, 3]); ) ) $(DDOC_BLANKLINE ) $(P This is most handy when the array indices are given by $(DDLINK spec/enum, Enums, enums):) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD enum) Color { red, blue, green } $(D_KEYWORD int)[Color.max + 1] value = [ Color.blue :6, Color.green:2, Color.red :5 ]; $(D_KEYWORD assert)(value == [5, 6, 2]); ) ) $(DDOC_BLANKLINE ) $(P All elements of a static array can be initialized to a specific value with:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[4] a = 42; $(D_COMMENT // set all elements of a to 42 ) $(D_KEYWORD assert)(a == [42, 42, 42, 42]); ) ) $(DDOC_BLANKLINE ) $(P These arrays are statically allocated when they appear in global scope. Otherwise, they need to be marked with $(D const) or $(D static) storage classes to make them statically allocated arrays.) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 special-array, Special Array Types)

$(DDOC_BLANKLINE )

$(LNAME2 strings, Strings)

$(DDOC_BLANKLINE ) $(P A string is an array of characters. String literals are just an easy way to write character arrays. String literals are immutable (read only). ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD char)[] str1 = $(D_STRING "abc"); $(D_COMMENT // error, cannot implicitly convert expression `"abc"` of type `string` to `char[]` )$(D_KEYWORD char)[] str2 = $(D_STRING "abc").dup; $(D_COMMENT // ok, makes mutable copy )$(D_KEYWORD immutable)($(D_KEYWORD char))[] str3 = $(D_STRING "abc"); $(D_COMMENT // ok )$(D_KEYWORD immutable)($(D_KEYWORD char))[] str4 = str2; $(D_COMMENT // error, cannot implicitly convert expression `str2` of type `char[]` to `string` )$(D_KEYWORD immutable)($(D_KEYWORD char))[] str5 = str3; $(D_COMMENT // ok, makes a mutable str5 with immutable aray contents )$(D_KEYWORD immutable)($(D_KEYWORD char))[] str6 = str2.idup; $(D_COMMENT // ok, makes immutable copy )) ) $(DDOC_BLANKLINE ) $(P The name $(CODE string) is aliased to $(CODE immutable(char)[]), so the above declarations could be equivalently written as: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD char)[] str1 = $(D_STRING "abc"); $(D_COMMENT // error, cannot implicitly convert expression `"abc"` of type `string` to `char[]` )$(D_KEYWORD char)[] str2 = $(D_STRING "abc").dup; $(D_COMMENT // ok, makes mutable copy )string str3 = $(D_STRING "abc"); $(D_COMMENT // ok )string str4 = str2; $(D_COMMENT // error, cannot implicitly convert expression `str2` of type `char[]` to `string` )string str5 = str3; $(D_COMMENT // ok, makes a mutable str5 with immutable aray contents )string str6 = str2.idup; $(D_COMMENT // ok, makes immutable copy )) ) $(DDOC_BLANKLINE ) $(P The type $(D immutable(char)[]) represents an array of $(D immutable char)s. However, the reference to the string is mutable. If the the reference to the string needs to be immutable as well it can be declared $(D immutable char[]) or $(D immutable string): ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD immutable) $(D_KEYWORD char)[] s = $(D_STRING "foo"); s[0] = 'a'; $(D_COMMENT // error, s refers to immutable data )s = $(D_STRING "bar"); $(D_COMMENT // error, s is immutable ) $(D_KEYWORD immutable)($(D_KEYWORD char))[] s = $(D_STRING "hello"); s[0] = 'b'; $(D_COMMENT // error, s[] is immutable )s = $(D_KEYWORD null); $(D_COMMENT // ok, s itself is not immutable )) $(DDOC_BLANKLINE ) $(P $(CODE char[]) strings are in UTF-8 format. $(CODE wchar[]) strings are in UTF-16 format. $(CODE dchar[]) strings are in UTF-32 format. ) $(DDOC_BLANKLINE ) $(P Strings can be copied, compared, concatenated, and appended:) $(DDOC_BLANKLINE ) $(D_CODE str1 = str2; $(D_KEYWORD if) (str1 < str3) { ... } func(str3 ~ str4); str4 ~= str1; ) $(DDOC_BLANKLINE ) $(P with the obvious semantics. Any generated temporaries get cleaned up by the garbage collector (or by using $(CODE alloca())). Not only that, this works with any array not just a special String array. ) $(DDOC_BLANKLINE ) $(P A pointer to a char can be generated: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD char)* p = &str[3]; $(D_COMMENT // pointer to 4th element )$(D_KEYWORD char)* p = str; $(D_COMMENT // pointer to 1st element )) $(DDOC_BLANKLINE ) $(P Since strings, however, are not 0 terminated in D, when transferring a pointer to a string to C, add a terminating 0: ) $(DDOC_BLANKLINE ) $(D_CODE str ~= $(D_STRING "\0"); ) $(DDOC_BLANKLINE ) or use the function $(D std.string.toStringz). $(DDOC_BLANKLINE ) $(P The type of a string is determined by the semantic phase of compilation. The type is one of: char[], wchar[], dchar[], and is determined by implicit conversion rules. If there are two equally applicable implicit conversions, the result is an error. To disambiguate these cases, a cast or a postfix of $(D c), $(D w) or $(D d) can be used: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD cast)($(D_KEYWORD immutable)($(D_KEYWORD wchar)) [])$(D_STRING "abc") $(D_COMMENT // this is an array of wchar characters )$(D_STRING "abc"w) $(D_COMMENT // so is this )) $(DDOC_BLANKLINE ) $(P String literals that do not have a postfix character and that have not been cast can be implicitly converted between string, wstring, and dstring as necessary. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) fun() { $(D_KEYWORD char) c; $(D_KEYWORD wchar) w; $(D_KEYWORD dchar) d; c = 'b'; $(D_COMMENT // c is assigned the character 'b' ) w = 'b'; $(D_COMMENT // w is assigned the wchar character 'b' ) $(D_COMMENT //w = 'bc'; // error - only one wchar character at a time ) w = $(D_STRING "b")[0]; $(D_COMMENT // w is assigned the wchar character 'b' ) w = $(D_STRING "\r")[0]; $(D_COMMENT // w is assigned the carriage return wchar character ) d = 'd'; $(D_COMMENT // d is assigned the character 'd' )} ) ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 strings_unicode, strings-unicode, Strings and Unicode)

$(P Note that built-in comparison operators operate on a $(LINK2 http://www.unicode.org/glossary/#code_unit, code unit) basis. The end result for valid strings is the same as that of $(LINK2 http://www.unicode.org/glossary/#code_point, code point) for $(LINK2 http://www.unicode.org/glossary/#code_point, code point) comparison as long as both strings are in the same $(LINK2 http://www.unicode.org/glossary/#normalization_form, normalization form). Since normalization is a costly operation not suitable for language primitives it's assumed to be enforced by the user. ) $(P The standard library lends a hand for comparing strings with mixed encodings (by transparently decoding, see $(REF cmp, std,algorithm)), $(REF_ALTTEXT case-insensitive comparison, icmp, std,uni) and $(REF_ALTTEXT normalization, normalize, std,uni). ) $(P Last but not least, a desired string sorting order differs by culture and language and is usually nothing like code point for code point comparison. The natural order of strings is obtained by applying $(HTTP www.unicode.org/reports/tr10/, the Unicode collation algorithm) that should be implemented in the standard library. ) $(DDOC_BLANKLINE )

$(LNAME2 printf, C's printf() and Strings)

$(DDOC_BLANKLINE ) $(P $(D printf()) is a C function and is not part of D. $(D printf()) will print C strings, which are 0 terminated. There are two ways to use $(D printf()) with D strings. The first is to add a terminating 0, and cast the result to a char*: ) $(DDOC_BLANKLINE ) $(D_CODE str ~= $(D_STRING "\0"); printf($(D_STRING "the string is '%s'\n"), str.ptr); ) $(DDOC_BLANKLINE ) or: $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD import) std.string; printf($(D_STRING "the string is '%s'\n"), std.string.toStringz(str)); ) $(DDOC_BLANKLINE ) $(P String literals already have a 0 appended to them, so can be used directly:) $(DDOC_BLANKLINE ) $(D_CODE printf($(D_STRING "the string is '%s'\n"), $(D_STRING "string literal").ptr); ) $(DDOC_BLANKLINE ) $(P So, why does the first string literal to printf not need the .ptr? The first parameter is prototyped as a const(char)*, and a string literal can be implicitly cast to a const(char)*. The rest of the arguments to printf, however, are variadic (specified by ...), and a string literal typed immutable(char)[] cannot pass to variadic parameters.) $(DDOC_BLANKLINE ) $(P The second way is to use the precision specifier. The length comes first, followed by the pointer:) $(DDOC_BLANKLINE ) $(D_CODE printf($(D_STRING "the string is '%.*s'\n"), $(D_KEYWORD cast)($(D_KEYWORD int))str.length, str.ptr); ) $(DDOC_BLANKLINE ) $(P The best way is to use std.stdio.writefln, which can handle D strings:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD import) std.stdio; writefln($(D_STRING "the string is '%s'"), str); ) $(DDOC_BLANKLINE )

$(LNAME2 void_arrays, Void Arrays)

$(DDOC_BLANKLINE ) $(P There is a special type of array which acts as a wildcard that can hold arrays of any kind, declared as $(D void[]). Void arrays are used for low-level operations where some kind of array data is being handled, but the exact type of the array elements are unimportant. The $(D .length) of a void array is the length of the data in bytes, rather than the number of elements in its original type. Array indices in indexing and slicing operations are interpreted as byte indices.) $(DDOC_BLANKLINE ) $(P Arrays of any type can be implicitly converted to a void array; the compiler inserts the appropriate calculations so that the $(D .length) of the resulting array's size is in bytes rather than number of elements. Void arrays cannot be converted back to the original type without using a cast, and it is an error to convert to an array type whose element size does not evenly divide the length of the void array.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) main() { $(D_KEYWORD int)[] data1 = [1,2,3]; $(D_KEYWORD long)[] data2; $(D_KEYWORD void)[] arr = data1; $(D_COMMENT // OK, int[] implicit converts to void[]. ) $(D_KEYWORD assert)(data1.length == 3); $(D_KEYWORD assert)(arr.length == 12); $(D_COMMENT // length is implicitly converted to bytes. ) $(D_COMMENT //data1 = arr; // Illegal: void[] does not implicitly ) $(D_COMMENT // convert to int[]. ) $(D_KEYWORD int)[] data3 = $(D_KEYWORD cast)($(D_KEYWORD int)[]) arr; $(D_COMMENT // OK, can convert with explicit cast. ) data2 = $(D_KEYWORD cast)($(D_KEYWORD long)[]) arr; $(D_COMMENT // Runtime error: long.sizeof == 8, which ) $(D_COMMENT // does not divide arr.length, which is 12 ) $(D_COMMENT // bytes. )} ) ) $(DDOC_BLANKLINE ) $(P Void arrays can also be static if their length is known at compile-time. The length is specified in bytes:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) main() { $(D_KEYWORD byte)[2] x; $(D_KEYWORD int)[2] y; $(D_KEYWORD void)[2] a = x; $(D_COMMENT // OK, lengths match ) $(D_KEYWORD void)[2] b = y; $(D_COMMENT // Error: int[2] is 8 bytes long, doesn't fit in 2 bytes. )} ) ) $(DDOC_BLANKLINE ) $(P While it may seem that void arrays are just fancy syntax for $(D ubyte[]), there is a subtle distinction. The garbage collector generally will not scan $(D ubyte[]) arrays for pointers, $(D ubyte[]) being presumed to contain only pure byte data, not pointers. However, it $(I will) scan $(D void[]) arrays for pointers, since such an array may have been implicitly converted from an array of pointers or an array of elements that contain pointers. Allocating an array that contains pointers as $(D ubyte[]) may run the risk of the GC collecting live memory if these pointers are the only remaining references to their targets.) $(DDOC_BLANKLINE )

$(LNAME2 implicit-conversions, Implicit Conversions)

$(DDOC_BLANKLINE ) $(P A pointer $(D T*) can be implicitly converted to one of the following:) $(DDOC_BLANKLINE ) $(UL $(LI $(D void*)) ) $(DDOC_BLANKLINE ) $(P A static array $(D T[dim]) can be implicitly converted to one of the following ($(D U) is a base class of $(D T)): ) $(DDOC_BLANKLINE ) $(UL $(LI $(D T[])) $(DDOC_BLANKLINE ) $(LI $(D const(U)[])) $(LI $(D const(U[]))) $(LI $(D void[])) ) $(DDOC_BLANKLINE ) $(P A dynamic array $(D T[]) can be implicitly converted to one of the following ($(D U) is a base class of $(D T)):) $(DDOC_BLANKLINE ) $(UL $(LI $(D const(U)[])) $(LI $(D const(U[]))) $(LI $(D void[])) ) $(DDOC_BLANKLINE ) $(P Array literals can also be implicitly converted to static array types. See $(DDSUBLINK spec/expression, array_literals, Array Literals) for details.) $(DDOC_BLANKLINE ) $(P String literals can also be implicitly converted to static array types and character pointer types. See $(DDSUBLINK spec/expression, string_literals, String Literals) for details.) $(DDOC_BLANKLINE ) $(SPEC_SUBNAV_PREV_NEXT statement, Statements, hash-map, Associative Arrays) ) )