学习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
,但我不知道为什么。谁能解释一下?
答案 0 :(得分:2)
D标准库已经拥有您需要的一切 - toStringz和fromStringz功能。
答案 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'
之前打印很大一部分内存。