从C中的字符串中删除所有非字母字符 - 可能的编译器问题

Chr*_*ris 2 c alphabet string.h

我在C中编写一个函数,它将获取一个字符串并删除所有不是小写字母字符的字符.我到目前为止编写了这段代码:

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

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

hellowoldd
Run Code Online (Sandbox Code Playgroud)

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

hello][]woldd
hello[]woldd
hello]woldd
hellowoldd
Run Code Online (Sandbox Code Playgroud)

它似乎非常接近,但我不明白为什么它会产生这个输出!最奇怪的部分是我已经将代码提供给其他两个朋友,它在他们的计算机上工作正常.我们都运行相同版本的Linux(ubuntu 14.04.3),并且都使用gcc进行编译.

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

pax*_*blo 9

strcpy如果范围重叠,则无法保证该功能正常工作,就像在您的情况下一样.从C11 7.24.2.3 The strcpy function /2(我的重点):

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

您可以使用类似memmove,它有重叠范围,按工作C11 7.24.2.2 The memmove function /2:

memmove函数n将指向的对象中的字符复制到指向s2的对象中s1.复制发生就好像n从对象人物指向s2首先拷贝到一个临时数组n不重叠的对象指向的字符s1s2,然后n从临时数组字符复制到对象指向s1.


但是有一个更好的解决方案,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';
}
Run Code Online (Sandbox Code Playgroud)

您会注意到我也在使用islower()(from ctype.h)来检测小写字母字符.这更便于携带,因为C标准并未强制要求字母字符具有连续的代码点(数字是唯一保证连续的数字).

也没有单独的检查需要,isalpha()因为,因为C11 7.4.1.2 The isalpha function /2,islower() == true暗示isalpha() == true:

isalpha函数测试任何字符为哪个isupperislower为真,或者......

  • 如果`islower`返回"true",那么'isalpha`也会返回"true"吗?意思是你不需要`isalpha`检查? (2认同)