在拆分函数的外部分配内存并将指向字符串数组的指针传递给拆分函数是否安全(并且可能?)?
不,因为在阅读之前您不知道每个令牌有多大,也不知道有多少令牌。因此,您编写的代码是不安全的。它假设每个令牌不超过 29 个字符,并且不超过 20 个令牌。
使用strdup、as Chris Dodd explains 可以解决为每个令牌分配恰到好处的内存量。但这仍然会让你分配一堆小块内存。
为了最大限度地减少分配,您可以将所有令牌存储在单个内存块中。这就是strtok 所做的;它修改输入字符串,用空值替换分隔符。 tokens 不会复制每个标记,而是简单地指向原始字符串中每个标记的开头。
这意味着tokens 指向in,而你的函数正在修改in。我会选择是否将输入字符串复制给调用者。
// Don't modify the original, tokens points at a copy.
char **tokens = split(strdup(input), &num_tokens, "\t");
// Modify the original, tokens points at input.
char **tokens = split(input, &num_tokens, "\t");
这就留下了分配tokens 的问题。为此,您要么需要知道有多少令牌,要么即时重新分配。
要最小化 CPU,您可以根据需要将 realloc 增加到 tokens。
size_t tokens_size = 1;
char **tokens = malloc(tokens_size * sizeof(char*));
*num_tokens = 0;
for(
char *token = strtok(in, delimiter);
token;
token = strtok(NULL, delimiter)
) {
if( *num_tokens >= tokens_size ) {
tokens_size *= 2;
tokens = realloc(tokens, tokens_size * sizeof(char*));
}
tokens[*num_tokens] = token;
(*num_tokens)++;
}
作为效率和内存使用之间的折衷方案,我没有为每个令牌重新分配,而是将每个重新分配的令牌大小加倍。这将使用额外的内存,但会最大限度地减少重新分配。
为了最大限度地减少分配,您需要对字符串进行标记并记住有多少标记。然后分配足够的空间。然后再次读取存储指向标记的指针的字符串。
// tokenize in and remember how many there are.
*num_tokens = 0;
for(
char *token = strtok(in, delimiter);
token;
token = strtok(NULL, delimiter)
) {
(*num_tokens)++;
}
// Allocate exactly enough space.
char **tokens = malloc(*num_tokens * sizeof(char*));
// Iterate through the tokens and store them.
char *token = in;
for(size_t i = 0; i < *num_tokens; i++) {
// Store a pointer to the token
tokens[i] = token;
// Jump ahead the length of the token plus the null.
token += strlen(token) + 1;
}
对于扫描输入两次的成本,您可以只执行一次 malloc。
注意:如果结构体包含所有信息,这段代码会更简单、更安全。
typedef struct {
char *original;
char **tokens;
size_t num_tokens;
size_t tokens_size;
} Tokens;
这将所有内容组合在一起,您可以将其分解为 Tokens_init 和 Tokens_free 等函数。