2013-02-22 4 views
0

Я пишу trie в D и хочу, чтобы каждый объект trie имел указатель на некоторые данные, у которого есть значение, отличное от NULL, если узел является терминальным узлом в trie, а NULL - в противном случае. Тип данных не определен до тех пор, пока не будет создано trie (в C это будет сделано с void *, но я планирую сделать это с помощью шаблона), что является одной из причин того, что указатели на объекты кучи желательны.Как создать строку в куче в D?

Это требует от меня, в конечном счете, создания моих данных в куче, и в этот момент на нее может указывать узел 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» является грамматической ошибкой.

+0

«PS Если кто-нибудь писать D компилятор читает это, апостроф в «строке» является грамматической ошибкой ». Нет, это намеренно. Если бы вы этого хотели, использовала бы необработанную строку. auto mystring = \ 'string's \'; – 0b1100110

ответ

2

Помните, что string всего лишь immutable(char)[]. Поэтому вам не нужны указатели, так как string уже является динамическим массивом.

Что касается их создания, вы делаете new char[X], а не new string.

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

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 (8B для x64 машин) памяти, но если вы используете указатели на массивы, тогда, если указатель не является нулевым, в памяти находится 12B (+ data in array) использовать. 4B, если из указателя и 8B относятся к массиву, потому что ссылки массива имеют два указателя. Один на первый и один на последний элемент в массиве.

+0

, вы не должны отбрасывать из 'string' в' char [] ', но используйте' char [] 'напрямую, (вы можете повысить качество с помощью' takeUnique') –

+0

Вы правы. Кастинг из строки потенциально опасен из-за того, что данные строк разделяются между ссылками. Безопасный метод должен быть с использованием свойства 'dup' (добавлен в исходный ответ). – Marmyst

+0

, вы также можете указать idup, который возвращает неизменяемую копию –

0

Если вы можете использовать только один указатель, и вы не хотите использовать предложения в ответе Мартиста (&str в своем примере создает ссылку на стек, который вам может не понадобиться, str.ptr теряет информацию о длине строк как D строки не всегда равна нулю прекращается), вы можете сделать это:

Remeber, что вы можете думать о D массивов (и, следовательно строк) в качестве структуры с элементом указателя данных и длина:

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

Итак, когда имея дело с массивом, содержимое массива всегда находится в куче, но ptr/lengt h комбинированный тип является типом значения и поэтому обычно хранится в стеке. Я не знаю, почему компилятор не позволяет создать этот тип значения в куче с использованием нового, но вы всегда можете сделать это вручную:

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); 
} 
Смежные вопросы