0

我正在用 D 编写一个 trie,我希望每个 trie 对象都有一个指向某些数据的指针,如果节点是 trie 中的终端节点,则该指针具有非 NULL 值,否则为 NULL。在创建 trie 之前,数据的类型是不确定的(在 C 中,这将使用 a 来完成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
*/

是什么赋予了?如何在堆上创建字符串?

PS 如果编写 D 编译器的人正在阅读此内容,则“字符串”中的撇号是语法错误。

4

4 回答 4

5

字符串总是在堆上分配。这对于任何其他动态数组都是相同的(T[],string只是 type 的别名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)内存,但如果您使用指向数组的指针,那么如果指针不为空,则使用 12B(+ 数组中的数据)内存。4B 如果来自指针和 8B 来自对数组的引用,因为数组引用是由两个指针组成的。数组中的一个到第一个和一个到最后一个元素。

于 2013-02-22T12:12:05.013 回答
2

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

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

于 2013-02-22T01:00:20.970 回答
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

于 2013-02-22T07:00:00.167 回答
0

If you can only use exactly one pointer and you don't want to use the suggestions in Marmyst's answer (&str in his example creates a reference to the stack which you might not want, str.ptr loses information about the strings length as D strings are not always zero terminated) you can do this:

Remeber that you can think of D arrays (and therefore strings) as a struct with a data pointer and length member:

struct ArraySlice(T)
{
    T* ptr;
    size_t length;
}

So when dealing with an array the array's content is always on the heap, but the ptr/length combined type is a value type and therefore usually kept on the stack. I don't know why the compiler doesn't allow you to create that value type on the heap using new, but you can always do it manually:

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);
}
于 2013-02-24T10:06:08.383 回答