Memory management 如何在D中的堆上创建字符串?

Memory management 如何在D中的堆上创建字符串?,memory-management,d,Memory Management,D,我在D中编写一个trie,我希望每个trie对象都有一个指向某个数据的指针,如果该节点是trie中的终端节点,则该数据具有非NULL值,否则为NULL。在创建trie之前,数据的类型是不确定的(在C中,这将通过void*完成,但我计划通过模板来完成),这就是为什么需要指向堆对象的指针的原因之一 这需要我最终在堆上创建数据,此时trie节点可以指向它。实验中,似乎执行这个任务,就像C++中那样。但是,由于某些原因,这在字符串中失败。以下代码起作用: import std.stdio; void

我在D中编写一个trie,我希望每个trie对象都有一个指向某个数据的指针,如果该节点是trie中的终端节点,则该数据具有非NULL值,否则为NULL。在创建trie之前,数据的类型是不确定的(在C中,这将通过
void*
完成,但我计划通过模板来完成),这就是为什么需要指向堆对象的指针的原因之一

这需要我最终在堆上创建数据,此时trie节点可以指向它。实验中,似乎<<代码>新< /COD>执行这个任务,就像C++中那样。但是,由于某些原因,这在字符串中失败。以下代码起作用:

import std.stdio;

void main() {
    string *a;
    string b = "hello";
    a = &b;
    writefln("b = %s, a = %s, *a = %s", b, a, *a);
}
/* OUTPUT:
b = hello, a = 7FFF5C60D8B0, *a = hello
*/
但是,这一点失败了:

import std.stdio;

void main() {
    string *a;
    a = new string();
    writefln("a = %s, *a = %s", a, *a);
}
/* COMPILER FAILS WITH:
test.d(5): Error: new can only create structs, dynamic arrays or class objects, not string's
*/
有什么好处?如何在堆上创建字符串


另外,如果编写D编译器的人正在阅读此文件,则“string”中的撇号是一个语法错误。

请记住
string
只是
不可变(char)[
。所以您不需要指针,因为
string
已经是一个动态数组


至于创建它们,只需执行
newchar[X]
,而不是
newstring

因为字符串是动态数组,所以字符串内容已经在堆上了。但是,在您的情况下,最好使用char动态数组,因为您需要可变性

import std.stdio;

void main() {
  char[] a = null; // redundant as dynamic arrays are initialized to null
  writefln("a = \"%s\", a.ptr = %s", a, a.ptr); // prints: a = "", a.ptr = null
  a = "hello".dup; // dup is required because a is mutable
  writefln("a = \"%s\", a.ptr = %s", a, a.ptr); // prints: a = "hello", a.ptr = 7F3146469FF0
}
请注意,您实际上并没有保存数组的内容,而是保存数组的一部分。数组由运行时处理,并在堆上分配。
这篇文章是关于这个主题的一篇好文章

字符串总是在堆上分配。这与任何其他动态数组相同(
T[]
string
只是type
不可变(char)[
的别名)

如果只需要一个指针,有两种方法:

auto str = "some immutable(char) array";
auto ptr1 = &str; // return pointer to reference to string (immutable(char)[]*)
auto ptr2 = str.ptr; // return pointer to first element in string (char*)
如果需要指向空字符串的指针,请使用以下命令:

auto ptr = &"";
auto mutableString1 = cast(char[])"Convert to mutable."; // shouldn't be used
// or
auto mutableString2 = "Convert to mutable.".dup; // T[].dup returns mutable duplicate of array
请记住,您不能更改字符串中任何单个字符的值(因为它们是不可变的)。如果要对字符串中的字符进行操作,请使用以下命令:

auto ptr = &"";
auto mutableString1 = cast(char[])"Convert to mutable."; // shouldn't be used
// or
auto mutableString2 = "Convert to mutable.".dup; // T[].dup returns mutable duplicate of array
通常你应该避免使用指针,除非你完全知道自己在做什么


从内存的角度来看,任何指针都占用4B(对于x64机器为8B)内存,但如果您使用指向数组的指针,那么,如果指针不为null,则有12B(+数组中的数据)内存在使用。4B if from pointer和8B是从引用到数组的,因为数组引用由两个指针组成。数组中一对一和一对最后的元素。

如果您只能使用一个指针,并且不想使用Marmyst答案中的建议(
&str
在他的示例中创建了一个对堆栈的引用,您可能不需要,
str.ptr
会丢失有关字符串长度的信息,因为D个字符串并不总是以零结尾)您可以这样做:

请记住,您可以将D数组(以及字符串)视为具有数据指针和长度成员的结构:

struct ArraySlice(T)
{
    T* ptr;
    size_t length;
}
因此,在处理数组时,数组的内容始终在堆上,但ptr/length组合类型是一种值类型,因此通常保留在堆栈上。我不知道为什么编译器不允许您使用new在堆上创建该值类型,但您始终可以手动创建:

import core.memory;
import std.stdio;

string* ptr;

void alloc()
{
    ptr = cast(string*)GC.malloc(string.sizeof);
    *ptr = "Hello World!";
}

void main()
{
    alloc();
    writefln("ptr=%s, ptr.ptr=%s, ptr.length=%s, *ptr=%s", ptr, ptr.ptr, ptr.length, *ptr);
}

您不应该从
string
强制转换为
char[]
,而是直接使用
char[]
(然后可以使用
assumeUnique更好地上转换)您是对的。从字符串强制转换有潜在危险,因为字符串的数据在引用之间共享。安全方法应该使用
dup
属性(添加到原始答案中)。您可能还想提到返回不可变副本的idup。我没有提到
idup
,因为我不确定这是如何工作的。我知道,当调用可变副本时,此调用返回数组的不可变副本,但当调用不可变数组时,会返回什么?根据我对D的了解(我大约一周前才开始学习D)此调用不应返回数组的副本,而应返回一个新引用,因为immutable不能在其生存期内更改其状态(根据immutable的定义,尤其是在字符串的情况下)。两个不可变副本浪费内存,编译器不应允许这样做,但在这种情况下,第二个副本不再重复。另外,如果编写D编译器的人正在阅读此文件,“string'S”中的撇号是一个语法错误。“不,这是故意的。如果需要,应该使用原始字符串。auto-mystring=`string'S`;