我开发了一个反向字符串程序。我想知道是否有更好的方法来做到这一点,如果我的代码有任何潜在的问题。我希望练习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;
}
}
}
答案 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 = 0
,
1^0 = 1
,0^1 = 1
。
一个众所周知的技巧是使用xor交换两个值。这是因为三个基本原因
所有值0^0 = 0
和x ^ 0 = x
的xor:x ^ x = 0
,x ^ y = y ^ x
和x
的属性。所以说我们有两个
最初存储两个值的变量y
和a
b
和va
。
// 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
因此值被交换。这确实有一个错误 - 当vb
和a
是同一个变量时:
// 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)
借助指针,您只需几步即可轻松完成:
首先指向字符串最后一个字符的指针
逆序读取指针指向的内容
#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;
}
答案 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来交换你的角色。