猿问

反转C中的字符串

我开发了一个反向字符串程序。我想知道是否有更好的方法来执行此操作,并且我的代码是否存在任何潜在问题。我希望练习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;

        }

    }

}


潇潇雨雨
浏览 440回答 3
3回答

慕沐林林

如果您想练习C的高级功能,那么指针呢?我们也可以在宏和异或交换中投入乐趣!#include <string.h> // for strlen()// reverse the given null-terminated string in placevoid inplace_reverse(char * str){&nbsp; if (str)&nbsp; {&nbsp; &nbsp; char * end = str + strlen(str) - 1;&nbsp; &nbsp; // swap the values in the two given variables&nbsp; &nbsp; // XXX: fails when a and b refer to same memory location#&nbsp; &nbsp;define XOR_SWAP(a,b) do\&nbsp; &nbsp; {\&nbsp; &nbsp; &nbsp; a ^= b;\&nbsp; &nbsp; &nbsp; b ^= a;\&nbsp; &nbsp; &nbsp; a ^= b;\&nbsp; &nbsp; } while (0)&nbsp; &nbsp; // walk inwards from both ends of the string,&nbsp;&nbsp; &nbsp; // swapping until we get to the middle&nbsp; &nbsp; while (str < end)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; XOR_SWAP(*str, *end);&nbsp; &nbsp; &nbsp; str++;&nbsp; &nbsp; &nbsp; end--;&nbsp; &nbsp; }#&nbsp; &nbsp;undef XOR_SWAP&nbsp; }}甲指针(例如char *,从右到左为读指针char是用于指位置的另一值的存储器在C语言的数据类型)。在这种情况下,a char的存储位置。我们可以 通过给指针加上前缀来取消引用指针*,从而为我们提供存储在该位置的值。因此,存储在的值str是*str。我们可以使用指针进行简单的算术运算。当我们增加(或减少)指针时,我们只需将其移动以引用该类型值的下一个(或上一个)存储位置。不同类型的递增指针可能会将指针移动不同的字节数,因为不同的值在C中具有不同的字节大小。在这里,我们使用一个指针来引用char字符串中的第一个未处理的指针(str),使用另一个指针来引用最后一个未处理的指针 (end)。我们交换它们的值(*str和*end),然后将指针向内移动到字符串的中间。一旦str >= end它们都指向相同的char,这意味着我们原始的字符串长度是奇数个(中间char不需要颠倒),或者我们已经处理了所有东西。为了进行交换,我定义了一个macro。宏是由C预处理程序完成的文本替换。它们与功能有很大不同,因此必须知道它们之间的区别。当您调用一个函数时,该函数将对您提供的值进行操作。调用宏时,它只是执行文本替换-因此,您直接给它提供的参数会被使用。由于我只使用过XOR_SWAP一次宏,因此定义它可能是过大的了,但是它使我在做什么更加清楚。在C预处理器扩展宏之后,while循环如下所示:&nbsp; &nbsp; while (str < end)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; do { *str ^= *end; *end ^= *str; *str ^= *end; } while (0);&nbsp; &nbsp; &nbsp; str++;&nbsp; &nbsp; &nbsp; end--;&nbsp; &nbsp; }请注意,每次在宏定义中使用宏参数时,它们都会显示一次。这可能非常有用-但如果使用不正确,也会破坏您的代码。例如,如果我已将增量/减量指令和宏调用压缩为一行,例如&nbsp; &nbsp; &nbsp; XOR_SWAP(*str++, *end--);然后这将扩展为&nbsp; &nbsp; &nbsp; 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&nbsp; // 原来:&nbsp; // a == v a&nbsp;&nbsp; // b == v b&nbsp; a ^ = b;&nbsp; //现在:a == v a ^ v b&nbsp; b ^ = a;&nbsp; //现在:b == v b ^(v a ^ v b)&nbsp; // == v a ^(v b ^ v b)&nbsp; // == v a ^ 0&nbsp; // == v a&nbsp; a ^ = b;&nbsp; //现在:a ==(v a ^ v b)^ v a&nbsp;&nbsp; // ==(v a ^ v a)^ v b&nbsp;&nbsp; // == 0 ^ v b&nbsp;&nbsp; // == v b因此,将交换值。这确实有一个错误-when a和b是相同的变量:&nbsp; // 原来:&nbsp; // a == v a&nbsp; a ^ = a;&nbsp; //现在:a == v a ^ v a&nbsp; // == 0&nbsp; a ^ = a;&nbsp; //现在:a == 0 ^ 0&nbsp; // == 0&nbsp; a ^ = a;&nbsp; //现在:a == 0 ^ 0&nbsp; // == 0由于我们str < end,在上面的代码中永远不会发生这种情况,所以我们可以。当我们担心正确性时,我们应该检查边缘情况。该if (str)行应确保没有NULL为字符串提供指针。空字符串""呢?好了strlen("") == 0,所以我们将初始化end为str - 1,这意味着while (str < end)条件永远不会成立,因此我们什么也不做。哪个是正确的。有很多C需要探索。玩得开心!更新: mmw带来了一个好处,那就是您确实需要谨慎操作,因为它确实就地运行。&nbsp;char stack_string[] = "This string is copied onto the stack.";&nbsp;inplace_reverse(stack_string);由于stack_string是一个数组,其内容初始化为给定的字符串常量,因此可以正常工作。然而&nbsp;char * string_literal = "This string is part of the executable.";&nbsp;inplace_reverse(string_literal);将导致您的代码在运行时启动并死亡。这是因为string_literal仅指向存储为可执行文件一部分的字符串-通常是操作系统不允许您编辑的内存。在一个更幸福的世界中,您的编译器会知道这一点,并在尝试编译时出现错误,并告诉您该string_literal类型必须为您,char const *因为您无法修改其内容。但是,这不是我的编译器所生活的世界。您可以尝试使用一些技巧来确保某些内存在堆栈或堆中(因此是可编辑的),但是它们不一定是可移植的,并且可能很丑陋。但是,我很乐意为此承担责任给函数调用者。我已经告诉他们该函数可以进行适当的内存操作,他们有责任给我一个允许这样做的参数。

九州编程

只是重新布置,并进行安全检查。我还删除了您未使用的退货类型。我认为这是安全和干净的:#include <stdio.h>#include <string.h>void reverse_string(char *str){&nbsp; &nbsp; /* skip null */&nbsp; &nbsp; if (str == 0)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; return;&nbsp; &nbsp; }&nbsp; &nbsp; /* skip empty string */&nbsp; &nbsp; if (*str == 0)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; return;&nbsp; &nbsp; }&nbsp; &nbsp; /* get range */&nbsp; &nbsp; char *start = str;&nbsp; &nbsp; char *end = start + strlen(str) - 1; /* -1 for \0 */&nbsp; &nbsp; char temp;&nbsp; &nbsp; /* reverse */&nbsp; &nbsp; while (end > start)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; /* swap */&nbsp; &nbsp; &nbsp; &nbsp; temp = *start;&nbsp; &nbsp; &nbsp; &nbsp; *start = *end;&nbsp; &nbsp; &nbsp; &nbsp; *end = temp;&nbsp; &nbsp; &nbsp; &nbsp; /* move */&nbsp; &nbsp; &nbsp; &nbsp; ++start;&nbsp; &nbsp; &nbsp; &nbsp; --end;&nbsp; &nbsp; }}int main(void){&nbsp; &nbsp; char s1[] = "Reverse me!";&nbsp; &nbsp; char s2[] = "abc";&nbsp; &nbsp; char s3[] = "ab";&nbsp; &nbsp; char s4[] = "a";&nbsp; &nbsp; char s5[] = "";&nbsp; &nbsp; reverse_string(0);&nbsp; &nbsp; reverse_string(s1);&nbsp; &nbsp; reverse_string(s2);&nbsp; &nbsp; reverse_string(s3);&nbsp; &nbsp; reverse_string(s4);&nbsp; &nbsp; reverse_string(s5);&nbsp; &nbsp; printf("%s\n", s1);&nbsp; &nbsp; printf("%s\n", s2);&nbsp; &nbsp; printf("%s\n", s3);&nbsp; &nbsp; printf("%s\n", s4);&nbsp; &nbsp; printf("%s\n", s5);&nbsp; &nbsp; return 0;}已进行编辑,以使当strlen为0时,结束点不会指向可能损坏的内存位置。
随时随地看视频慕课网APP
我要回答