c

时间:2019-01-22 18:58:18

标签: c memory memory-management

我对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;
}

此函数计算字符串中的单词数量(定界符为空格,即“ ”)。我不希望修改传入参数中的字符串。

我有两个问题:

  1. strdup()相比,strcpy()方式(代码中的注释部分)应该更受青睐吗?我的理解是strcpy()足够且更快,但我不确定。
  2. 由于没有为要返回的size_t值分配任何内存(它是一个局部变量),是否应该这样做才能确保函数健壮?还是使用size_t nwords = count_nwords(copied_input);完全安全并且总是可以正确获取返回值?

谢谢!

编辑:我接受了唯一一个与我的问题完全相关的答案,但是我建议您阅读其他答案,因为它们可以很好地了解我在代码中所犯的错误。

3 个答案:

答案 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)

  
      
  1. 相对于strcpy(),应该首选strdup()方式(在代码中被注释的部分)吗?我的理解是strcpy()   足够且更快,但我不确定。
  2.   

您的解决方案是干净的,并且效果很好,所以请不要打扰。唯一的一点是,您正在使用现在是可选的VLA,然后使用strdup不太容易出现标准问题。现在,关于性能,由于未指定VLA的实现方式,因此性能可能会因编译器/平台的不同而有所差异(已知gcc将堆栈用于VLA,但其他任何编译器也可能使用堆)。我们只知道strdup在堆上分配,仅此而已。我怀疑这种选择是否会带来性能问题。

注意:您的分配大小错误,并且至少应为strlen(str)+1

  
      
  1. 由于没有为要返回的size_t值分配内存(这是一个局部变量),因此应该这样做以确保   功能是否健壮?或者正在使用size_t nwords =   count_nwords(复制输入);完全安全,将永远正确   得到返回的值?
  2.   

管理返回值和适合的内存是编译器关心的问题。通常,这些值在堆栈上传输或从堆栈上传输(在“堆栈帧”上有一些读数)。您可能会怀疑,恰好在调用之前在堆栈上为其分配了空间,并在调用之后将其释放(一旦您丢弃或复制返回的值)。