使用模板将D字符串转换为char *

时间:2017-08-30 04:44:01

标签: arrays d c-strings

学习D并彻底享受这个过程,但是将D字符串转换为char*的这段代码令我感到困惑。我只是盲目地直观地了解模板是如何工作的,但我想知道它是如何工作的,我偶然发现了它

import core.stdc.stdio;
import core.stdc.string;
import core.stdc.stdlib;

extern (C):

/// Convert a string to a char array
template charify(const string str, const size_t length) {
    void charify(char* arr) {
        foreach(i; 0 .. str.length) {
            if (i >= length) {
                break;
            }
            arr[i] = str[i];
        }
    }
}

int main() {
    auto k = cast(char*)malloc(4 * char.sizeof);
    charify!("abcdef", 3)(k);
    printf("%s %d\n", k, strlen(k));
    return 0;
}

我期望的输出是abc 3,但我不知道为什么。谁能解释一下?

2 个答案:

答案 0 :(得分:2)

D标准库已经拥有您需要的一切 - toStringzfromStringz功能。

答案 1 :(得分:1)

我不完全确定这里有什么令人困惑的,但我会试一试。如果我没有覆盖某些东西,请随意提问。 :)

首先,存在一个错误 - 您的结果字符串并不总是以空值终止。 malloc提供随机数据,未初始化为0.您的函数应将最后一个字符设置为'\0'以解决此问题。

extern(C)行是不必要的 - 您不会从C调用charify,尤其是因为它是模板化函数。

在Phobos这个D标准库中,有toStringz函数执行你的函数应该做的事情,但使用GC。

无论如何,关于你的功能实际在做什么:

template charify(...) { void charify(...){...} }模式称为eponymous template。等效且较短的签名为void charify(const string str, const size_t length)(char* arr)

由于length是值类型(不涉及指针),D中的字符串是不可变的,因此两个模板参数都不需要const

可以做的其他改进是使用数组操作而不是foreach,将函数的整个主体转换为arr[0..min(str.length, length)] = str[];,当然还有空终止。

清理后的版本将是:

void charify(string str, size_t length)(char* arr) {
    arr[0..min(str.length, length)] = str[];
    arr[length-1] = '\0';
}

在你的情况下,模板并不是真正的保证 - 以str和length作为常规参数的函数基本上都是等价的:

void charify(string str, size_t length, char* arr) {
    arr[0..min(str.length, length)] = str[];
    arr[length-1] = '\0';
}

唯一的区别是,现在只有一个参数列表,并且它被调用charify("abcdef", 3, k)

此外,D中的字符串文字始终以空值终止,因此可以通过.ptr property安全地传递给C函数。请注意,只有当您的代码看起来像printf("%s", "foo".ptr),而不是printf("%s", functionThatReturnsString().ptr)或甚至printf("%s", "foo"[0..1].ptr)时才会出现这种情况。在"foo"[0..1]案例" foo"将打印,因为切片后没有插入空终止符。在functionThatReturnsString的情况下,返回的字符串可能会或可能不会以空值终止,如果没有,可能会在最终遇到'\0'之前打印很大一部分内存。