D(编程语言)/d2/字符串和动态数组
在本章中,您将深入了解字符串。同时,您还将学习另一种类型:动态数组。
import std.stdio;
void main()
{
int[] a = [1,2,3,4];
int[] b = [5,6];
auto c = a ~ b;
writeln(c); // [1,2,3,4,5,6]
writeln(c.length); // 6
int* ptr_c = c.ptr;
ptr_c[0] = 3;
writeln(c); // [3,2,3,4,5,6]
}
import std.stdio;
void main()
{
// Concept: Immutable
immutable(int) a = 10;
// a = 11; Error: cannot modify immutable
immutable a = 10;
// Concept: Strings as Arrays
immutable(char)[] str = "Hello";
auto str1 = str ~ "1";
writeln(str1); // Hello1
writeln(str1.length); // 6
// str1[] = 'z'; error! str1's elements are not mutable
char[] mutablestr1 = str1.dup;
// Concept: Char Literals
mutablestr1[] = 'z'; // 'z' is not a string, but a char
str1 = mutablestr1.idup; // The str1 itself is mutable
// only its elements are not mutable
writeln(str1); // zzzzzz
}
在上一课中,您学习了静态数组。动态数组与静态数组不同,因为它们没有固定长度。本质上,动态数组是一个具有以下信息的结构:
- 指向第一个元素的指针
- 整个数组的长度
您可以像这样创建动态数组
int[] a;
您可以创建一个以特定长度初始化的动态数组
int[] a = new int[](5);
您将在后面学习有关 new
关键字的更多内容。
使用单个值填充数组的语法对于静态和动态数组都相同
int[] a = [1,2,3];
a[] = 3;
writeln(a); // [3, 3, 3]
动态数组的索引方式与静态数组相同。访问特定索引处元素的语法也相同
a[2];
但是,由于动态数组在编译时没有已知的长度,因此编译器无法检查您访问的索引是否确实在该长度内。类似这样的代码将编译,但也会导致运行时范围违规错误
int[] a = [1,2,3];
writeln(a[100]); //Runtime error, Range violation
动态数组可以使用 ~
运算符与其他动态数组甚至静态数组组合。将新元素追加到数组使用相同的运算符。
int[] a = [1,2];
auto b = a ~ [3,4];
writeln(b); //[1, 2, 3, 4]
b ~= 5; // same as b = b ~ 5;
writeln(b); //[1, 2, 3, 4, 5]
除非您确定 my_dynamic_arr.length
等于 my_static_arr.length
,否则您不应该将动态数组分配给静态数组 (my_static_arr = my_dynamic_arr;
)。否则,将发生运行时错误。但是,您可以始终将静态数组分配给动态数组变量,因为如果静态数组的长度太大,动态数组将自动调整大小。
int[] a = [9,9,9,9,9,9];
int[3] b = [1,2,3];
int[200] c;
a = b;
writeln(a); // [1, 2, 3]
a = c;
writeln(a.length); // 200
int[] d = [5,4,3];
b = d; // OK: lengths are both 3
// c = d; runtime error! lengths don't match.
动态数组按值传递给函数。这意味着,当您将动态数组传递给函数时,包含指向第一个元素的指针和长度的结构会被复制并传递。
void tryToChangeLength(int[] arr)
{
arr.length = 100;
}
void main()
{
int[] a = [1,2];
tryToChangeLength(a);
writeln(a.length); // still 2
}
您在上章中了解到,您可以通过添加 ref
修饰符使事物按引用而不是按值传递。
这些属性适用于静态和动态数组
属性 | 描述 |
---|---|
.init | 对于静态数组,它返回一个数组,其中每个元素都初始化为其默认值。 |
.sizeof | 返回数组在内存中的大小。 |
.length | 返回数组中元素的数量。这在静态数组中是固定的。 |
.ptr | 返回指向数组第一个元素的指针。 |
.dup | 创建一个动态数组,它是数组的副本,并返回它。 |
.idup | 类似于 .dup ,但副本的元素是 immutable 。 |
.reverse | 以相反顺序返回数组的元素。 |
.sort | 对数组中的元素进行就地排序,并返回结果。 |
还有其他属性,这些属性是所有对象或表达式的共有属性。其中两个属性是 .init
和 .sizeof
属性。
在 D 中,immutable
就像 auto
一样是一个存储类。它将类型转换为不可修改的类型。
immutable(int) fixed_value = 37;
immutable int another_value = 46;
请注意,immutable(type)
对编译器而言与 immutable type
相同。
当您有一个像 immutable
这样的存储类时,您可以省略 auto
进行类型推断。
immutable fixed_value = 55;
编译器会推断出不可变类型的具体类型。以下代码示例无效,因为编译器无法推断出类型。
immutable fixed_value; //Error!
这是允许的,并且是完全有效的代码
immutable(int) a = 300;
int b = a;
它只是将 b
设置为 a
的值。 b
不必是 immutable
。如果你正在获取引用,情况就会有所不同。
immutable(int) a = 13;
immutable(int)* b = &a;
// int* c = &a; Error.
你可以将 immutable
强制转换为可变类型,但如果你使用这种技巧修改了不可变值,结果是未定义的。
immutable(int) a = 7;
int* b = cast(int*)&a;
// Just make sure you do not modify a
// through b, or else!
从第一课起你就在使用字符串。string
与 immutable(char)[]
完全相同,即不可变字符元素的动态数组。类似地,wstring
等同于 immutable(wchar)[]
,而 dstring
等同于 immutable(dchar)[]
。
字符串具有与动态数组相同的内置属性。一个有用的属性是 .dup
属性,用于创建字符串的可变 char[]
副本,以便修改字符串的各个字符。
string a = "phobos";
char[] b = a.dup;
b[1] = 'r';
b[4] = 'e';
writeln(b); // probes
.idup
属性用于创建现有字符串的副本,或创建 char[]
的字符串副本。
string a = "phobos";
string copy_a = a.idup;
char[] mutable_a = copy_a.dup;
mutable_a[3] = 't';
copy_a = mutable_a.idup;
writeln(mutable_a); // photos
writeln(a); // phobos
char
字面量用单引号括起来。也有 wchar
和 dchar
字面量。
auto a = "a"; // string
auto b = 'b'; // char
auto c = 'c'c; // char
auto d = 'd'w; // wchar
auto e = 'e'd; // dchar