我对C
有点陌生,想知道有关内存分配的某些事情。我的功能如下:
size_t count_nwords(const char* str) {
//char* copied_str = strdup(str); // because 'strtok()' alters the string it passes through
char copied_str[strlen(str)];
strcpy(copied_str, str);
size_t count = 1;
strtok(copied_str, " ");
while(strtok(NULL, " ") != 0) {
count++;
}
//free(copied_str);
return count;
}
此函数计算字符串中的单词数量(定界符为空格,即“ ”)。我不希望修改传入参数中的字符串。
我有两个问题:
strdup()
相比,strcpy()
方式(代码中的注释部分)应该更受青睐吗?我的理解是strcpy()
足够且更快,但我不确定。size_t
值分配任何内存(它是一个局部变量),是否应该这样做才能确保函数健壮?还是使用size_t nwords = count_nwords(copied_input);
完全安全并且总是可以正确获取返回值?谢谢!
编辑:我接受了唯一一个与我的问题完全相关的答案,但是我建议您阅读其他答案,因为它们可以很好地了解我在代码中所犯的错误。
答案 0 :(得分:4)
无法解释空字符
// char copied_str[strlen(str)];
char copied_str[strlen(str) + 1];
strcpy(copied_str, str);
错误的算法
即使进行了上述修复,代码仍返回count_nwords(" ")
1
不必要的字符串复制
strtok()
此处不需要。不需要该字符串的副本。
替代方法:遍历字符串。
size_t count_nwords(const char* str) {
size_t count = 0;
while (*str) {
while (isspace((unsigned char) *str)) {
str++;
}
if (*str) {
count++;
while (!isspace((unsigned char) *str) && *str) {
str++;
}
}
}
return count;
}
答案 1 :(得分:4)
另一种选择是 state-loop 方法,您可以使用一个简单的标志不断循环遍历每个字符,以跟踪计数状态。 (您要么用单词阅读字符,要么正在阅读空格)。好处是您只涉及一个循环。一个简短的例子是:
size_t count_words (const char *str)
{
size_t words = 0;
int in_word = 0;
while (*str) {
if (isspace ((unsigned char)*str))
in_word = 0;
else {
if (!in_word)
words++;
in_word = 1;
}
str++;
}
return words;
}
值得了解所有技术。 isspace
需要包含ctype.h
。
答案 2 :(得分:1)
- 相对于strcpy(),应该首选strdup()方式(在代码中被注释的部分)吗?我的理解是strcpy() 足够且更快,但我不确定。
您的解决方案是干净的,并且效果很好,所以请不要打扰。唯一的一点是,您正在使用现在是可选的VLA,然后使用strdup
不太容易出现标准问题。现在,关于性能,由于未指定VLA的实现方式,因此性能可能会因编译器/平台的不同而有所差异(已知gcc将堆栈用于VLA,但其他任何编译器也可能使用堆)。我们只知道strdup
在堆上分配,仅此而已。我怀疑这种选择是否会带来性能问题。
注意:您的分配大小错误,并且至少应为strlen(str)+1
。
- 由于没有为要返回的size_t值分配内存(这是一个局部变量),因此应该这样做以确保 功能是否健壮?或者正在使用size_t nwords = count_nwords(复制输入);完全安全,将永远正确 得到返回的值?
管理返回值和适合的内存是编译器关心的问题。通常,这些值在堆栈上传输或从堆栈上传输(在“堆栈帧”上有一些读数)。您可能会怀疑,恰好在调用之前在堆栈上为其分配了空间,并在调用之后将其释放(一旦您丢弃或复制返回的值)。