在C中反转string

我开发了一个反向string程序。 我想知道是否有更好的方法来做到这一点,如果我的代码有任何潜在的问题。 我期待练习一些C的高级function

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; } } } 

如果你想练习C的高级特性,那么指针怎么样? 我们可以折腾macros和XOR换太好玩了!

 #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中的一种数据types,用于指向另一个值在内存中的位置。 在这种情况下, char存储的位置。 我们可以通过给它加一个*作为前缀来解引用指针,这给了我们存储在那个位置的值。 所以存储在str的值是*str

我们可以用指针来做简单的算术。 当我们递增(或递减)一个指针时,我们简单地将它移动到指向该types值的下一个(或前一个)存储器位置。 递增不同types的指针可能会将指针移动不同数量的字节,因为不同的值在C中具有不同的字节大小。

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

为了做交换,我定义了一个macros 。 macros是由C预处理器完成的文本replace。 它们与function非常不同,了解它们的区别很重要。 当你调用一个函数的时候,函数会在你赋值的一个副本上运行。 当你调用一个macros时,它只是做一个文本replace – 所以你给它的参数直接使用。

由于我只用了一次XOR_SWAPmacros,所以定义它可能是矫枉过正的,但是它更清楚了我在做什么。 在C预处理器扩展macros之后,while循环看起来像这样:

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

请注意,macros参数每次在macros定义中使用时都会显示一次。 这可能是非常有用的 – 但也可以打破你的代码,如果使用不当。 例如,如果我已经将增量/减量指令和macros调用压缩成一行,如

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

那么这将扩大到

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

其中有三倍的增加/减less操作,并没有实际做它应该做的交换。

当我们谈论这个问题时,你应该知道异或^ )是什么意思。 这是一个基本的算术运算 – 如加,减,乘,除,小学通常不会教。 它将两个整数一点一点地结合在一起,就像增加一样,但是我们不关心结转。 1^1 = 0 1^0 = 1 0^1 = 1 0^0 = 0

众所周知的技巧是使用XOR交换两个值。 这是因为xor的三个基本性质: x ^ 0 = xx ^ x = 0x ^ y = y ^ x对于所有值xy 。 所以说,我们有两个variablesab ,最初存储两个值v av b

   // 原来:
   // a == v a
   // b == v b
   a ^ = b;
   // now:a == v a ^ v b
   b ^ = a;
   // now:b == v b ^(v a ^ v b )
   // == v a ^(v b ^ v b )
   // == v a ^ 0
   // == v a
   a ^ = b;
   // now:a ==(v a ^ v b )^ v a
   // ==(v a ^ v a )^ v b
   // == 0 ^ v b
   // == v b

所以值被交换。 这确实有一个错误 – 当ab是相同的variables:

   // 原来:
   // a == v a
   a ^ = a;
   // now:a == v a ^ v a
   // == 0
   a ^ = a;
   // now:a == 0 ^ 0
   // == 0
   a ^ = a;
   // now:a == 0 ^ 0
   // == 0

由于我们str < end ,这从来没有发生在上面的代码,所以我们没事。

当我们关心正确性时,我们应该检查我们的边缘情况。 if (str)行应该确保我们没有给string的NULL指针。 怎么样的空string"" ? 那么strlen("") == 0 ,所以我们将初始化为str - 1 ,这意味着while (str < end)条件永远不是真的,所以我们什么也不做。 哪个是对的。

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

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

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

这工作正常,因为stack_string是一个数组,其内容被初始化为给定的string常量。 然而

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

将导致你的代码在运行时火焰和死亡。 这是因为string_literal仅指向作为可执行文件的一部分存储的string – 通常是您不允许由操作系统编辑的内存。 在一个更快乐的世界里,你的编译器会知道这一点,当你试图编译时,咳嗽一个错误,告诉你string_literal需要是char const *types,因为你不能修改内容。 但是,这不是我的编译器所在的世界。

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

只是重新安排,安全检查。 我也删除了你未使用的返回types。 我认为这是一个安全和干净的,因为它得到:

 #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时,结束不会指向可能不好的内存位置。

你可以把你的(len/2)testing放在for循环中:

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

这个完整的程序显示了我将如何做到这一点。 请记住,我写C的时候,你们中的大多数人都是你母亲眼中的闪光,所以它是老派的,在职的,长时间的名字是为了懦夫。 修复,如果你愿意,我更感兴趣的代码的正确性。

它处理NULL,空string和所有string大小。 我没有用最大尺寸的string(max(size_t))进行testing,但是它应该可以工作,而且如果你处理的string很大,那你就是疯了.-)

 #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' 

尝试这个:

 reverse_string(NULL); reverse_string(""); 

你可以改变你的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; } 

我没有看到return语句,而且你正在改变inputstring,这对程序员来说可能是一个问题。 您可能希望inputstring是不可变的。

此外,这可能是挑剔的,但len / 2应该只计算一次,国际海事组织。

除此之外,只要你照顾rossfabricant提到的问题,它就可以工作。

没有人再使用指针了吗?

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

编辑:对不起,只注意到上面的XOR例子…

 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; } } 
 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; } } 
 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; } 

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

既然你说你想变得有趣,也许你会想用XOR交换来交换你的angular色。

而不是打破一半,你应该简单地缩短你的循环。

 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; } 
 #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; } 
 #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]); } 
 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); } 
 /* 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; } 

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

 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]; } } 

简单易用的代码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; } } 

这是我的镜头。 我只是通过使用标准的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; } 

这里是一个运行的例子 。

我的两分钱:

 /* 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); } 

testing#1strrev()

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

testing#2strnrev()

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

你可以试试这个指针算术:

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