强大的字符串反转

时间:2009-11-08 22:29:39

标签: c string swap

我正在尝试编写一个扭转字符串的trival面试问题。

这是我的代码:

#include <string.h>

char* rev( char* str)
{
    int i,j,l;

    l = strlen(str);

    for(i=0,j=l-1; i<l/2 ; i++, j--)
    {
        str[i] = (str[i] + str[j]);
        str[j] = str[i] - str[j];
        str[j] = str[i] - str[j];
    }

    return str;
}

int main()
{
    char *str = " hello";
    printf("\nthe reverse is %s ...", rev(str));

    return 1;
}

基本上,这个给出了分段错误。

我有以下问题:

  1. 我得到分段错误可能是因为,字符加起来没有在ascii中定义,因此我不能将它们存回作为字符,我正在使用www.codepad.org [我想知道它是否只支持ascii! !]。我的理解是正确的还是还有别的东西。

  2. 如何解决同一平台的问题[我的意思是为codepad.org交换]

  3. 这里我必须使用额外的整数l来计算长度。所以通过交换来保存单个字符空间..我正在使用额外的int !!! ..只是为了给观众留下深刻的印象:) ...这种做法是否值得![/ p>

  4. 这个是针对那些有兴趣编写单元测试/ API测试的人。我希望有一个强大的实现,所以可能的测试用例。我假设如果面试官问这么简单的问题......他肯定想要一些非常抢劫的实施和测试用例。我想的很少:

    • 传递空字符串传递整数

    • 字符串传递整数数组 char数组。

    • 非常长的字符串,

    • 单个字符串字符串的特殊字符。

  5. 任何建议/建议都会有所帮助。

8 个答案:

答案 0 :(得分:8)

这一行:

char *str = " hello";

可能指向只读内存。试试这个:

char str[] = " hello";

(您还有其他一些错误,但此更改将修复您的段错误。)

答案 1 :(得分:7)

使用临时变量而不是您的方法进行交换。由于优化,编译器可能会使用寄存器作为临时变量。'

无论哪种方式,您都错误地实现了交换算法。它应该是

str[i] = str[i] + str[j];
str[j] = str[i] - str[j];
str[i] = str[i] - str[j];

答案 2 :(得分:5)

Kernighan&amp; Ritchie的The C Programming Language显示了一个带有临时变量的就地字符串反转算法。

与此类似:

char* rev_string(char* const str)
{
  int i, j;
  char tmp;
  for(i = 0, j = strlen(str)-1; i < j; i++; j--)
  {
    tmp = str[i];
    str[i] = str[j];
    str[j] = tmp;
  }
  return str;
}

这个算法比没有临时变量的人更容易理解。

关于问题列表中的第3项:

作为一名采访者,我希望看到简单,清晰,结构良好的代码。这很让人佩服。诡计不会让我印象深刻。特别是当它出现过早优化时。顺便说一句,我的解决方案使用一个额外的char而不是int来反转字符串。令人印象深刻的? :)

项目#4:

另一个测试用例是未终止的字符串。你的功能是否足够强大以处理这种情况?您的功能只会像它最不健壮的部分一样健壮。将未终止的字符串传递到我的解决方案会导致分段错误,因为strlen报告的字符串长度不正确。不太健壮。

关于健壮性的重点是,您的代码可能很健壮,但您必须确保您使用的所有其他外部函数也是如此!

答案 3 :(得分:4)

从哪里开始...

好的,首先你应该知道你的例程正在反转字符串到位,换句话说,对原始缓冲区进行了更改。

这意味着您可以

int main()
{
    char str[] = "hello";
    rev(str);
    printf("\nthe reverse is %s ...", str);

    return 0;
}

并且字符串将被颠倒。

另一种方法是创建一个 new 字符串,它是原始字符串的反转副本。算法有些不同,你也应该能够做到这一点。

下一点:

    str[i] = (str[i] + str[j]);
    str[j] = str[i] - str[j];
    str[j] = str[i] - str[j];

坏了。它应该是

    str[i] = str[i] + str[j];
    str[j] = str[i] - str[j];
    str[i] = str[i] - str[j];

但是,正如~mathepic所说,你应该这样做:

    temp = str[i];
    str[i] = str[j];
    str[j] = temp;

另外:键盘使调试代码变得困难。在您自己的计算机上安装编译器和调试器(例如gcc和gdb)。

  

字符加起来没有在ascii中定义,因此我无法将它们存储为字符,我正在使用www.codepad.org [我想知道它是否只支持ascii !!]。我的理解是正确的还是还有别的东西。

在大多数C实现中(无论如何都在32位PC上运行),char是一个8位整数。 int是32位整数。当您添加或减去两个char并且结果超过8位时,它将“环绕”到其他某个值,但此过程是可逆的。

例如,255 + 1给出0,但0 - 1 = 255.(只是一个说明性的例子。)这意味着“我不能将它们作为字符存回”这里的问题

  

我希望有一个强大的实现

您希望表明您考虑了不同设计选择的成本和收益。如果您的例程提供了NULL,则可能更好导致分段错误,因为这会很快警告程序员他的代码中的错误。

  

传递空字符串

必须确保你的代码适用于这种情况。

  

传递整数   传递整数数组

您无法将整数或int []传递给期望char *的函数。在C中,您无法判断char *是否真的是字符串或其他内容。

  

单个字符串

确保您的例程适用于单个字符串,也适用于具有奇数编号和偶数字符数的字符串。

  

特殊字符串

C中没有特殊的char(除了惯例,空终止符'\ 0'除外)。但是,必须考虑多char个序列(反转UTF-8字符串与反转常规字符串不同)。但是,如果问题没有说明,我认为你不应该关注这一点。

最后三点:

  • main()中,return 1;通常表示您的计划失败。 return 0;更常见,但return EXIT_SUCCESS;最好,但您可能需要#include <stdlib.h>
  • 考虑使用更具描述性的变量名称。
  • 考虑创建strnrev()函数,类似于strncpy()和类似函数,如果在那里找不到空终止符,函数将不会超出n个字符。

答案 4 :(得分:2)

如果你打算在没有临时变量的情况下实现两个字符的交换(这是一个巧妙的技巧,但实际上你不应该实际使用它),那么使用“按位异或”代替它是明智的。加法/减法,或使用unsigned char而不是char,因为在C99标准中未定义有符号算术中的溢出,并猜测是什么,gcc开始利用这个未定义来进行优化。我只是在另一个问题中咆哮另一个不必要的优化案例。

答案 5 :(得分:0)

至于测试:

  1. null参数
  2. 空字符串参数
  3. 长度为1个字符串的参数
  4. 各种其他长度 - 也许是一根长串
  5. 您当然可以使用以下策略实施测试方法:

    1. 通用验证方法

      verifyEquals( expected, actual ) { ... }
      
    2. 各种案例的测试方法:

      testReverse() {
          verifyEquals(NULL, rev(NULL));
          verifyEquals("", rev(""));
          verifyEquals("a", rev("a"));
          verifyEquals("ba", rev("ab"));
          verifyEquals("zyx", rev("xyz"));
          verifyEquals("edcba", rev("abcde"));
      }
      
    3. 您还可以将交换“算法”重构为单独的过程,并对其进行单元测试。

答案 6 :(得分:0)

  

我得到分段错误可能是因为,字符加起来没有在ascii中定义的内容,因此我无法将它们存储为字符

我不这么认为。它们都只是C中的数字(尽管只有1个字节长),但你不应该有任何问题。

我认为(但我不确定)问题在于:

char *str = " hello";
printf("\nthe reverse is %s ...", rev(str));

你实际在做的是创建一个char数组“hello”,这是一个常量数组。这意味着,基本上,你不应该改变它。当你调用rev时,它实际上就地更改了数组,所以它试图将新值赋给常量char。

由于你执行char * str =“hello”,你实际上是对一个unsigned char进行“hello”,所以这不会被视为编译时错误。但是因为“hello”是所谓的“字符串文字”,它是作为可执行文件本身的一部分创建的,即它不在内存中,程序可以自由更改。这就是为什么你实际上得到了运行时段错误,而不是编译时错误(尽管你可能会收到关于此的警告)。

答案 7 :(得分:0)

感谢大家的回复。以下是每个人都建议更改的代码:

#include <string.h>

char* rev( char* str)
{

int start ,end ,len;

    len = strlen(str);

    for(start =0,end =len-1; start <len/2 ; start ++, end --)
    {
        str[start ] = str[start ] + str[end ];
        str[end ] = str[start ] - str[end ];
        str[start] = str[start ] - str[end ];
    }

    return str;
}

int main()
{

   char str[] = " hello there !";

printf("\n the reverse string is %s ...", rev(str));

    return 1;
}

分段错误是因为* str指向只读内存,将其更改为str []。感谢Carl Norum指出这一点。

  • 任何测试用例[专门用于API测试]?