在C中反转一个字符串

时间:2009-04-24 03:41:05

标签: c string pointers

我开发了一个反向字符串程序。我想知道是否有更好的方法来做到这一点,如果我的代码有任何潜在的问题。我希望练习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;
        }
    }
}

26 个答案:

答案 0 :(得分:60)

如果你想练习C的高级功能,指针怎么样? 我们也可以投入宏和xor-swap以获得乐趣!

#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. 引用另一个值的内存中的位置。在这种情况下, 存储char的位置。我们可以取消引用 通过为它们添加*作为前缀的指针,它为我们提供了价值 存储在该位置。因此,str中存储的值为*str

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

这里,我们使用一个指针来引用第一个未处理的 字符串char的{​​{1}}和另一个引用最后一个(str)。 我们交换它们的值(end*str),然后移动指针 向内到弦的中间。一旦*end,也可以 它们都指向相同的str >= end,这意味着我们的原始字符串有一个 奇数长度(中间char不需要反转),或 我们处理了所有事情。

要进行交换,我已经定义了。宏是文本替换 由C预处理器完成。它们与功能非常不同, 知道差异很重要。当你调用一个函数时 该函数对您提供的值的副本进行操作。你打电话的时候 一个宏,它只是做一个文本替换 - 所以你给出的论点 它是直接使用的。

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

XOR_SWAP

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

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

然后这会扩展到

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

其中三次增量/减量操作,实际上并非如此 做它应该做的交换。

虽然我们正在研究这个主题,但您应该知道 xor do { *str++ ^= *end--; *end-- ^= *str++; *str++ ^= *end--; } while (0); )的含义。这是一个基本的 算术运算 - 如加,减,乘,除,除 它通常不会在小学教授。它一点一点地组合了两个整数 - 像添加,但我们不关心结转。 ^1^1 = 01^0 = 10^1 = 1

一个众所周知的技巧是使用xor交换两个值。这是因为三个基本原因 所有值0^0 = 0x ^ 0 = x的xor:x ^ x = 0x ^ y = y ^ xx的属性。所以说我们有两个 最初存储两个值的变量ya bva

  // initially:
  // a == va
  // b == vb
  a ^= b;
  // now: a == va ^ vb
  b ^= a;
  // now: b == vb ^ (va ^ vb)
  //        == va ^ (vb ^ vb)
  //        == va ^ 0
  //        == va
  a ^= b;
  // now: a == (va ^ vb) ^ va
  //        == (va ^ va) ^ vb
  //        == 0 ^ vb
  //        == vb

因此值被交换。这确实有一个错误 - 当vba是同一个变量时:

  // initially:
  // a == va
  a ^= a;
  // now: a == va ^ va
  //        == 0
  a ^= a;
  // now: a == 0 ^ 0
  //        == 0
  a ^= a;
  // now: a == 0 ^ 0
  //        == 0

由于我们b,上述代码中从未发生这种情况,所以我们没问题。

虽然我们关注正确性,但我们应该检查边缘情况。 str < end行应确保我们没有为字符串指定if (str)指针。空字符串NULL怎么样?好"",因此我们会将strlen("") == 0初始化为end,这意味着str - 1条件永远不会成立,因此我们不做任何事情。这是正确的。

有一堆C要探索。玩得开心!

更新: mmw提出了一个很好的观点,就是你必须要小心谨慎地调用它,因为它确实可以就地运行。

while (str < end)

这很好用,因为 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,因为您无法修改内容。但是,这不是我的编译器所处的世界。

有一些黑客可以尝试确保某些内存在堆栈上或堆中(因此可编辑),但它们不一定是可移植的,而且可能非常难看。但是,我非常乐意将此责任交给函数调用者。我已经告诉过他们这个函数可以实现内存操作,他们有责任给我一个允许这样做的参数。

答案 1 :(得分:23)

只是重新安排和安全检查。我还删除了你未使用的返回类型。我认为这是安全和干净的:

#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时,end不会指向可能错误的内存位置。

答案 2 :(得分:16)

您可以将(len/2)测试放入for循环:

for(i = 0,k=len-1 ; i < (len/2); i++,k--)
{
        temp = str[k];
        str[k] = str[i];
        str[i] = temp;

}

答案 3 :(得分:14)

这个完整的程序显示了我将如何做到这一点。请记住,当你们大多数的鞭子被你母亲的眼睛闪烁时,我正在写C,所以这是老式的,有做事的,长期名字都是为了懦夫。修复如果你愿意,我对代码的正确性更感兴趣。

它处理NULL,空字符串和所有字符串大小。我没有用最大尺寸的字符串(max(size_t))测试它,但它应该可以工作,如果你处理大字符串,你仍然是疯了: - )

#include <stdio.h>
#include <string.h>

char *revStr (char *str) {
    char tmp, *src, *dst;
    size_t len;
    if (str != NULL)
    {
        len = strlen (str);
        if (len > 1) {
            src = str;
            dst = src + len - 1;
            while (src < dst) {
                tmp = *src;
                *src++ = *dst;
                *dst-- = tmp;
            }
        }
    }
    return str;
}

char *str[] = {"", "a", "ab", "abc", "abcd", "abcde"};

int main(int argc, char *argv[]) {
    int i;
    char s[10000];
    for (i=0; i < sizeof(str)/sizeof(str[0]); i++) {
        strcpy (s, str[i]);
        printf ("'%s' -> '%s'\n", str[i], revStr(s));
    }
    return 0;
}

输出是:

'' -> ''
'a' -> 'a'
'ab' -> 'ba'
'abc' -> 'cba'
'abcd' -> 'dcba'
'abcde' -> 'edcba'

答案 4 :(得分:7)

试试这个:

reverse_string(NULL);
reverse_string("");

答案 5 :(得分:6)

没有人再使用指针吗?

void inplace_rev( char * s ) {
  char t, *e = s + strlen(s);
  while ( --e > s ) { t = *s;*s++=*e;*e=t; }
}

编辑:对不起,刚刚注意到上面的XOR示例...

答案 6 :(得分:6)

您可以更改for循环声明以缩短代码:

char* reverse_string(char *str)
{
    char temp;
    size_t len = strlen(str) - 1;
    size_t stop = len/2;
    size_t i,k;

    for(i = 0, k = len; i < stop; i++, k--)
    {
        temp = str[k];
        str[k] = str[i];
        str[i] = temp;
    }
    return str;
}

答案 7 :(得分:4)

我没有看到return语句,并且您正在更改输入字符串,这可能是程序员的问题。您可能希望输入字符串是不可变的。

此外,这可能是挑剔的,但len / 2应该只计算一次,IMO。

除此之外,只要您处理rossfabricant提到的问题案例,它就会起作用。

答案 8 :(得分:4)

void reverse(char *s)
{
  char *end,temp;
  end = s;
  while(*end != '\0'){
    end++;
  }
  end--;  //end points to last letter now
  for(;s<end;s++,end--){
    temp = *end;
    *end = *s;
    *s = temp; 
  }
}

答案 9 :(得分:2)

rev {
int len = strlen(str)-1;
for ( int i =0; i< len/2 ; i++ ) {
        char t = str[i];
        str[i] = str[len-i];
        str[len-i] = t;
        }

}

答案 10 :(得分:2)

bool reverse_string(char* str)
{
    // Make sure str is reversible
    if (!str ||  strlen(str) < 2)
        return false;

    char* first = str;
    char* last = str + strlen(str) - 1; // Minus 1 accounts for Index offset
    char temp;

    do{
        temp = *first;
        *first = *last;
        *last = temp;
    }
    while (++first < --last); // Update Pointer Addresses and check for equality

    return true;
}

此解决方案基于GManNickG的帖子,并进行了一些修改。如果在strlen操作之前未评估!str,则初始逻辑语句可能是危险的(对于NULL ptr)。我的编译器不是这种情况。我想我会添加这段代码,因为它是do-while循环的一个很好的例子。

答案 11 :(得分:1)

制作了一个微型程序来完成该任务:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char str[8192] = "string"; // string
    size_t len = strlen(str)-1; // get string length and reduce 1

    while(len+1 > 0) // Loop for every character on string
    {
        printf("%c",str[len--]); // Print string reversed and reducing len by one
    }
    return 0; // Quit program

}

说明:
我们取字符串的长度,然后从最后一个位置开始循环,直到到达索引0退出程序为止。

答案 12 :(得分:1)

借助指针,您只需几步即可轻松完成:

  1. 首先指向字符串最后一个字符的指针

  2. 逆序读取指针指向的内容

    #include #include

         int main()
         {
             char str[] = "This is an example";
             char *p = str + strlen(str); /* point to the end of the string */
             p--; /* points to the last char of the string */
    
             for (int i = 0; i < strlen(str); i++, p--)
             {
                printf("%c", *p);
             }
    
             return 0;
         }
    

答案 13 :(得分:1)

您实际上可以执行以下操作:

#include <string.h>

void reverse(char *);

int main(void){
 char name[7] = "walter";
 reverse(name);
 printf("%s", name);
}

void reverse(char *s) {
  size_t len = strlen(s);
  char *a = s;
  char *b = &s[(int)len - 1];
  char tmp;
  for (; a < b; ++a, --b) {
    tmp = *a;
    *a = *b;
    *b = tmp;
  }
}

答案 14 :(得分:1)

这是一个很好的问题ant2009。您可以使用独立函数来反转字符串。代码是......

#include <stdio.h>
#define MAX_CHARACTERS 99

int main( void );
int strlen( char __str );

int main() {
    char *str[ MAX_CHARACTERS ];
    char *new_string[ MAX_CHARACTERS ];
    int i, j;

    printf( "enter string: " );
    gets( *str );

    for( i = 0; j = ( strlen( *str ) - 1 ); i < strlen( *str ), j > -1; i++, j-- ) {
        *str[ i ] = *new_string[ j ];
    }
    printf( "Reverse string is: %s", *new_string" );
    return ( 0 );
}

int strlen( char __str[] ) {
    int count;
    for( int i = 0; __str[ i ] != '\0'; i++ ) {
         ++count;
    }
    return ( count );
}

答案 15 :(得分:1)

您可以尝试此指针算法:

void revString(char *s)
{
  char *e = s; while(*e){ e++; } e--;
  while(e > s){ *s ^= *e; *e ^= *s; *s++ ^= *e--; }
}

答案 16 :(得分:1)

简单易用的代码xD

void strrev (char s[]) {

int i;
int dim = strlen (s);
char l;

for (i = 0; i < dim / 2; i++) {
    l = s[i];
    s[i] = s[dim-i-1];
    s[dim-i-1] = l;
    }

}

答案 17 :(得分:1)

代码看起来不必要复杂。这是我的版本:

void strrev(char* str) { 
    size_t len = strlen(str);
    char buf[len]; 

    for (size_t i = 0; i < len; i++) { 
        buf[i] = str[len - 1 - i]; 
    }; 

    for (size_t i = 0; i < len; i++) { 
        str[i] = buf[i]; 
    }
}

答案 18 :(得分:1)

/* Author: Siken Dongol */
#include <stdio.h>

int strLength(char *input) {
    int i = 0;
    while(input[i++]!='\0');
    return --i;
}

int main()
{
    char input[] = "Siken Man Singh Dongol";

    int len = strLength(input);
    char output[len];

    int index = 0;
    while(len >= 0) {
        output[index++] = input[--len];
    }

    printf("%s\n",input);
    printf("%s\n",output);
    return 0;
}

答案 19 :(得分:1)

我的两分钱:

/* Reverses n characters of a string and adds a '\0' at the end */
void strnrev (char *txt, size_t len) {
    size_t idx;
    for (idx = len >> 1; idx > 0; idx--) {
        txt[len] = txt[idx - 1];
        txt[idx - 1] = txt[len - idx];
        txt[len - idx] = txt[len];
    }
    txt[len] = '\0';
}

/* Reverses a null-terminated string */
void strrev (char *txt) {
    size_t len = 0;
    while (txt[len++]);
    strnrev(txt, --len);
}

测试#1 - strrev()

char string[] = "Hello world!";
strrev(string);
printf("%s\n", string); // Displays "!dlrow olleH"

测试#2 - strnrev()

char string[] = "Hello world!";
strnrev(string, 5);
printf("%s\n", string); // Displays "olleH"

答案 20 :(得分:1)

Here is my shot which will handle all the cases 
char *p ="KDLAKDADKADAD"
char p[] = "lammdlamldaldladadada"
also empty string 

#include<stdio.h>
#include<string.h>enter code here
#include<stdlib.h>
char *string_reverse(char *p);
int main()
{

        char *p = " Deepak@klkaldkaldkakdoroorerr";
        char *temp = string_reverse(p);
        printf("%s", temp);
}
char *  string_reverse( char *p )
{

        if(*p == '\0')
        {
                printf("No charecters are present \n");
                return 0;
        }
        int count = strlen(p)+1;
        int mid = strlen(p)/2;
        char *q  = (char *)malloc(count * sizeof(char));
        if( q )
        {
                strcpy(q,p);
                char *begin,*end,temp;
                begin = q ;
                end = q+strlen(p)-1  ;
                int i = 0;
                while( i < mid/2 )
                {
                        temp = *end;
                        *end = *begin;
                        *begin = temp;
                        begin++;
                        end--;
                        i++;
                }
                return q;
        }
        else
        {

                printf("Memory Not allocated ");
        }
        free(q);
}

答案 21 :(得分:1)

这是我的镜头。我只是使用标准strcpy模式避免交换:

char *string_reverse(char *dst, const char *src)
{
    if (src == NULL) return NULL;

    const char *src_start = src;
    char *dst_end = dst + strlen(src);
    *dst_end = '\0';

    while ((*--dst_end = *src_start++)) { ; }

    return dst;
}

and here a running example

答案 22 :(得分:1)

#include <stdio.h>

int main()    
{

    char string[100];
    int i;

    printf("Enter a string:\n");
    gets(string);
    printf("\n");
    for(i=strlen(string)-1;i>-1;i--)

    printf("%c",string[i]);
}

答案 23 :(得分:1)

#include <stdio.h>
#include <string.h>

int main() 
{
    char *data = "hello world";
    int length=strlen(data);
    char bytes[length];
    int n=0;
    while(n<=length)
    {
       bytes[n] = data[length-n-1];
       n++;
    }
    printf("%s\n", bytes);
    return 0;   
}

答案 24 :(得分:1)

您应该简单地缩短循环时间,而不是中途缩短。

size_t length = strlen(str);
size_t i;

for (i = 0; i < (length / 2); i++)
{
    char temp = str[length - i - 1];
    str[length - i - 1] = str[i];
    str[i] = temp;
}

答案 25 :(得分:1)

既然你说想要变幻想,也许你想用XOR swap来交换你的角色。