2

我正在用 C 语言编写一个函数,它将接受一个字符串并删除所有不是小写字母字符的字符。到目前为止,我已经编写了这段代码:

void strclean(char* str) {
   while (*str) {
      if (!(*str >= 'a' && *str <= 'z')) {
         strcpy(str, str + 1);
         str--;
      }
      str++;
   }
}

当我将字符串“hello[][]world”传递给它时,该函数似乎主要工作,除了输出是:

hellowoldd

当我在它进入 if 语句的每一行之后打印它时,这是我收到的输出:

hello][]woldd
hello[]woldd
hello]woldd
hellowoldd

它似乎真的很接近,但我不明白为什么它会产生这个输出!最奇怪的部分是我把代码给了另外两个朋友,它在他们的电脑上运行良好。我们都在运行相同版本的 Linux(ubuntu 14.04.3),并且都使用 gcc 进行编译。

我不确定代码是否存在会导致输出不一致的问题,或者是否是编译器问题导致了问题。与他们的机器相比,也许它与我机器上的 strcpy 有关?

4

2 回答 2

10

strcpy如果范围重叠,则不能保证该功能可以正常工作,就像您的情况一样。来自C11 7.24.2.3 The strcpy function /2(我的重点):

strcpy函数将 所指向的字符串s2(包括终止空字符)复制到 所指向的数组中s1如果复制发生在重叠的对象之间,则行为未定义。

您可以使用类似的东西memmove,它确实适用于重叠范围,根据C11 7.24.2.2 The memmove function /2

该函数将字符从 指向的对象memmove复制到 指向的对象中。复制就像首先将来自 指向的对象的字符复制到不与 和 指向的对象重叠的临时字符数组中一样,然后将临时数组中的字符复制到 指向的对象中ns2s1ns2ns1s2ns1


但是有一个更好的解决方案,O(n)而不是时间复杂度,同时仍然是重叠安全的:O(n2)

void strclean (char* src) {
    // Run two pointers in parallel.

    char *dst = src;

    // Process every source character.

    while (*src) {
        // Only copy (and update destination pointer) if suitable.
        // Update source pointer always.

        if (islower(*src)) *dst++ = *src;
        src++;
    }

    // Finalise destination string.

    *dst = '\0';
}

您会注意到我还使用islower()(from ctype.h) 来检测小写字母字符。这更便于移植,因为 C 标准不要求字母字符具有连续的代码点(数字是唯一保证连续的代码点)。

也不需要单独检查,isalpha()因为根据C11 7.4.1.2 The isalpha function /2islower() == true意味着isalpha() == true

isalpha函数测试isupperislower为真的任何字符,或...

于 2015-09-25T07:33:24.423 回答
4

来自N1256 7.21.2.3 strcpy 函数

如果复制发生在重叠的对象之间,则行为未定义。

memmove即使区域重叠也可以使用。

void strclean(char* str) {
   while (*str) {
      if (!islower(*str)) { /* include ctype.h to use islower function */
         memmove(str, str + 1, strlen(str)); /* strlen(str + 1) + 1 (for terminating null character) should be strlen(str) */
      } else {
         str++;
      }
   }
}

由于从指针中减去以使其指向数组之前的区域是未定义的行为,因此我还重新构造了str操作。

于 2015-09-25T07:33:38.937 回答