如何在D中的堆上创建一个字符串?

时间:2013-02-22 00:07:08

标签: memory-management d

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

这要求我最终在堆上创建我的数据,此时trie节点可以指向它。在试验中,似乎new执行此任务,就像在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
*/

是什么给出的?如何在堆上创建字符串?

P.S。如果有人写D编译器正在读这个,那么“string”中的撇号是一个语法错误。

4 个答案:

答案 0 :(得分:5)

字符串总是在堆上分配。对于任何其他动态数组(T[]string只是类型immutable(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 = &"";

请记住,您无法更改字符串中任何单个字符的值(因为它们是immutable)。如果你想对字符串中的字符进行操作,请使用:

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,如果来自指针和8B是从引用到数组,因为数组引用是由两个指针组成的。数组中的一对一和一对一元素。

答案 1 :(得分:2)

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

至于创建它们,您只需new char[X],而不是new string

答案 2 :(得分:0)

字符串内容已经在堆上,因为字符串是动态数组。但是,在您的情况下,最好使用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
}

请注意,实际上并没有保存数组的内容,只是它的一部分。该数组由运行时处理,并在堆上分配。 关于这个主题的一个很好的解读是这篇文章http://dlang.org/d-array-article.html

答案 3 :(得分:0)

如果你只能使用一个指针并且你不想在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);
}