为了账号安全,请及时绑定邮箱和手机立即绑定

反转C中的字符串

反转C中的字符串

C
潇潇雨雨 2019-12-13 09:43:30
我开发了一个反向字符串程序。我想知道是否有更好的方法来执行此操作,并且我的代码是否存在任何潜在问题。我希望练习C的一些高级功能。char* reverse_string(char *str){    char temp;    size_t len = strlen(str) - 1;    size_t i;    size_t k = len;    for(i = 0; i < len; i++)    {        temp = str[k];        str[k] = str[i];        str[i] = temp;        k--;        /* As 2 characters are changing place for each cycle of the loop           only traverse half the array of characters */        if(k == (len / 2))        {            break;        }    }}
查看完整描述

3 回答

?
慕沐林林

TA贡献2016条经验 获得超9个赞

如果您想练习C的高级功能,那么指针呢?我们也可以在宏和异或交换中投入乐趣!


#include <string.h> // for strlen()


// reverse the given null-terminated string in place

void inplace_reverse(char * str)

{

  if (str)

  {

    char * end = str + strlen(str) - 1;


    // swap the values in the two given variables

    // XXX: fails when a and b refer to same memory location

#   define XOR_SWAP(a,b) do\

    {\

      a ^= b;\

      b ^= a;\

      a ^= b;\

    } while (0)


    // walk inwards from both ends of the string, 

    // swapping until we get to the middle

    while (str < end)

    {

      XOR_SWAP(*str, *end);

      str++;

      end--;

    }

#   undef XOR_SWAP

  }

}

甲指针(例如char *,从右到左为读指针char是用于指位置的另一值的存储器在C语言的数据类型)。在这种情况下,a char的存储位置。我们可以 通过给指针加上前缀来取消引用指针*,从而为我们提供存储在该位置的值。因此,存储在的值str是*str。


我们可以使用指针进行简单的算术运算。当我们增加(或减少)指针时,我们只需将其移动以引用该类型值的下一个(或上一个)存储位置。不同类型的递增指针可能会将指针移动不同的字节数,因为不同的值在C中具有不同的字节大小。


在这里,我们使用一个指针来引用char字符串中的第一个未处理的指针(str),使用另一个指针来引用最后一个未处理的指针 (end)。我们交换它们的值(*str和*end),然后将指针向内移动到字符串的中间。一旦str >= end它们都指向相同的char,这意味着我们原始的字符串长度是奇数个(中间char不需要颠倒),或者我们已经处理了所有东西。


为了进行交换,我定义了一个macro。宏是由C预处理程序完成的文本替换。它们与功能有很大不同,因此必须知道它们之间的区别。当您调用一个函数时,该函数将对您提供的值进行操作。调用宏时,它只是执行文本替换-因此,您直接给它提供的参数会被使用。


由于我只使用过XOR_SWAP一次宏,因此定义它可能是过大的了,但是它使我在做什么更加清楚。在C预处理器扩展宏之后,while循环如下所示:


    while (str < end)

    {

      do { *str ^= *end; *end ^= *str; *str ^= *end; } while (0);

      str++;

      end--;

    }

请注意,每次在宏定义中使用宏参数时,它们都会显示一次。这可能非常有用-但如果使用不正确,也会破坏您的代码。例如,如果我已将增量/减量指令和宏调用压缩为一行,例如


      XOR_SWAP(*str++, *end--);

然后这将扩展为


      do { *str++ ^= *end--; *end-- ^= *str++; *str++ ^= *end--; } while (0);

它具有三倍的增/减操作,并且实际上并没有执行它应该执行的交换操作。


当我们讨论这个主题时,您应该知道xor(^)的含义。这是一种基本的算术运算-像加法,减法,乘法,除法,但它通常不在小学里教。它一点一点地结合了两个整数-像加法一样,但是我们不在乎结转。 1^1 = 0,1^0 = 1, 0^1 = 1,0^0 = 0。


一个众所周知的技巧是使用xor交换两个值。这工作XOR因为三个基本属性:x ^ 0 = x,x ^ x = 0和x ^ y = y ^ x所有值x和y。所以说,我们有两个变量a,并b与起初存储两个值 和。vavb


  // 原来:

  // a == v a 

  // b == v b

  a ^ = b;

  //现在:a == v a ^ v b

  b ^ = a;

  //现在:b == v b ^(v a ^ v b)

  // == v a ^(v b ^ v b)

  // == v a ^ 0

  // == v a

  a ^ = b;

  //现在:a ==(v a ^ v b)^ v a 

  // ==(v a ^ v a)^ v b 

  // == 0 ^ v b 

  // == v b

因此,将交换值。这确实有一个错误-when a和b是相同的变量:


  // 原来:

  // a == v a

  a ^ = a;

  //现在:a == v a ^ v a

  // == 0

  a ^ = a;

  //现在:a == 0 ^ 0

  // == 0

  a ^ = a;

  //现在:a == 0 ^ 0

  // == 0

由于我们str < end,在上面的代码中永远不会发生这种情况,所以我们可以。


当我们担心正确性时,我们应该检查边缘情况。该if (str)行应确保没有NULL为字符串提供指针。空字符串""呢?好了strlen("") == 0,所以我们将初始化end为str - 1,这意味着while (str < end)条件永远不会成立,因此我们什么也不做。哪个是正确的。


有很多C需要探索。玩得开心!


更新: mmw带来了一个好处,那就是您确实需要谨慎操作,因为它确实就地运行。


 char stack_string[] = "This string is copied onto the stack.";

 inplace_reverse(stack_string);

由于stack_string是一个数组,其内容初始化为给定的字符串常量,因此可以正常工作。然而


 char * string_literal = "This string is part of the executable.";

 inplace_reverse(string_literal);

将导致您的代码在运行时启动并死亡。这是因为string_literal仅指向存储为可执行文件一部分的字符串-通常是操作系统不允许您编辑的内存。在一个更幸福的世界中,您的编译器会知道这一点,并在尝试编译时出现错误,并告诉您该string_literal类型必须为您,char const *因为您无法修改其内容。但是,这不是我的编译器所生活的世界。


您可以尝试使用一些技巧来确保某些内存在堆栈或堆中(因此是可编辑的),但是它们不一定是可移植的,并且可能很丑陋。但是,我很乐意为此承担责任给函数调用者。我已经告诉他们该函数可以进行适当的内存操作,他们有责任给我一个允许这样做的参数。


查看完整回答
反对 回复 2019-12-13
?
九州编程

TA贡献1785条经验 获得超4个赞

只是重新布置,并进行安全检查。我还删除了您未使用的退货类型。我认为这是安全和干净的:


#include <stdio.h>

#include <string.h>


void reverse_string(char *str)

{

    /* skip null */

    if (str == 0)

    {

        return;

    }


    /* skip empty string */

    if (*str == 0)

    {

        return;

    }


    /* get range */

    char *start = str;

    char *end = start + strlen(str) - 1; /* -1 for \0 */

    char temp;


    /* reverse */

    while (end > start)

    {

        /* swap */

        temp = *start;

        *start = *end;

        *end = temp;


        /* move */

        ++start;

        --end;

    }

}



int main(void)

{

    char s1[] = "Reverse me!";

    char s2[] = "abc";

    char s3[] = "ab";

    char s4[] = "a";

    char s5[] = "";


    reverse_string(0);


    reverse_string(s1);

    reverse_string(s2);

    reverse_string(s3);

    reverse_string(s4);

    reverse_string(s5);


    printf("%s\n", s1);

    printf("%s\n", s2);

    printf("%s\n", s3);

    printf("%s\n", s4);

    printf("%s\n", s5);


    return 0;

}

已进行编辑,以使当strlen为0时,结束点不会指向可能损坏的内存位置。


查看完整回答
反对 回复 2019-12-13
  • 3 回答
  • 0 关注
  • 439 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信