C语言如何知道字符串的结尾?

8

我有一个程序,想要从字符串中去除空格。我希望找到一种优雅的方法来实现,于是我找到了以下代码(我稍微修改了一下,以便更易读)在论坛上

char* line_remove_spaces (char* line)
{
    char *non_spaced = line;
    int i;
    int j = 0;
    for (i = 0; i <= strlen(line); i++)
    {
        if ( line[i] != ' ' )
        {
            non_spaced[j] = line[i];
            j++;
        }
    }
    return non_spaced;
}

如您所见,该函数接受一个字符串,并使用相同的分配内存空间,仅选择非空格字符。它有效!
无论如何,根据维基百科,在C中,字符串是“Null-terminated string”。我一直这样认为,一切都很好。但问题是:我们没有在non_spaced字符串的末尾放置“null-character”。而某种方式编译器知道它在由“non_spaced”字符串更改的最后一个字符结束。它是如何知道的?

"the compiler knows it" 是什么意思?你正在运行时更改它,编译过程已经结束了。 - Fred
2
就算价值不高,strlen(line)每次都会重新计算字符串的长度。这是一个非常复杂的计算,不应该在每个循环迭代中执行。你最好只需计算一次并将其存储:size_t len = strlen(line); for (i = 0; i <= len; i++)。(此外,你所有声明为int的变量在技术上都应该是size_t类型。) - Chris Lutz
7个回答

13

这并非魔法。你的代码中有:

for (i = 0; i <= strlen(line); i++)
              ^^
循环索引i一直运行到strlen(line),在此索引处字符数组中有一个空字符,这也被复制了。因此,你最终的结果在所需索引处有一个空字符。
如果你有:
for (i = 0; i < strlen(line); i++)
              ^^

那么你必须手动添加空字符,例如:

for (i = 0; i < strlen(line); i++)
{
    if ( line[i] != ' ' )
    {
        non_spaced[j] = line[i];
        j++;
    }
}
// put nul character
line[j] = 0;

空字符应该是 '\0' 而不是 ' '。 - KBN
@xFortyFourx:https://dev59.com/questions/em445IYBdhLWcg3wws8u - codaddict
你好,codaddict,我不确定你为什么发布了那个帖子。 '\0' 的 ASCII 值 = 0 空格的 ASCII 值 = 32 - KBN
1
最后一行 line[j] = 0; 应该改为 non_spaced[j] = 0;。而且 @KBN,这段代码是用来从给定的字符串中删除空格的,请阅读 OP 的问题,你就会知道 :) - starriet
@starriet 哈哈,那已经是10年前的事了,我完全不知道发生了什么。 - KBN

8

其他人已经回答了你的问题,但是这里有一个更快、更清晰的代码版本:

void line_remove_spaces (char* line)
{
  char* non_spaced = line;

  while(*line != '\0')
  {
    if(*line != ' ')
    {
      *non_spaced = *line;
      non_spaced++;
    }

    line++;
  }

  *non_spaced = '\0';
}

2
循环使用了 <= strlen,因此您将复制空终止符(在 i == strlen(line) 处)。

1
你可以尝试一下。在处理只包含一个空格的字符串" "时进行调试。仔细观察索引i发生了什么。

0

你怎么知道它“知道”?最有可能的情况是你只是在运气好,没有定义行为,并且在line有效字节之后有一个'\0'字符。

很可能你看不到末尾的空格,在命中偏离的“幸运'\0'”之前可能会打印出来。

还有几点:

  • 没有必要使用索引编写此代码。
  • 在每个循环迭代中调用strlen()效率不高。
  • 你可能想使用isspace()来删除更多的空白字符。

以下是我如何使用isspace()和指针编写它:

char * remove_spaces(char *str)
{
  char *ret = str, *put = str;

  for(; *str != '\0'; str++)
  {
    if(!isspace((unsigned char) *str)
      *put++ = *str;
  }
  *put = '\0';

  return ret;
}

请注意,这确实终止了字符串的无空格版本,因此返回的指针保证指向有效的字符串。

如果line是以空字符结尾的字符串,那么non_spaced也保证以空字符结尾,因此不会出现“幸运的\0”。 - Anthales

0

你的函数的字符串参数是以空字符结尾的,对吧? 在循环中,原始字符串的空字符也会被复制到非空格返回的字符串中。因此,非空格字符串实际上也是以空字符结尾的!

对于你的编译器来说,空字符只是另一种二进制数据,不会得到任何特殊处理,但它被字符串API用作一个方便的字符,以便轻松检测字符串的结尾。


0
如果你使用<= strlen(line),那么strlen(line)的长度将包括'\0',这样你的程序就可以正常工作。你可以使用调试和运行分析。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接