在C语言中反转一个字符串

10

我知道这个问题已经被问了成千上万次,但是我就是找不到我的代码中的错误。能否有人友善地指出我做错了什么?

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

void reverseString(char *myString){
  char temp;
  int len = strlen(myString);

  char *left = myString;
  //  char *right = &myString[len-1];                                                                                        
  char *right = myString + strlen(myString) - 1;

  while(left < right){
    temp = *left;
    *left = *right; // this line seems to be causing a segfault                                                              
    *right = temp;
    left++;
    right--;
  }
}

int main(void){
  char *somestring = "hello";
  printf("%s\n", somestring);
  reverseString(somestring);

  printf("%s", somestring);

}

1
尽管回答你问题的人已经弄清楚了,但你从未说过你遇到了什么错误或者哪些地方出了问题!你应该总是描述你期望得到的结果以及实际看到的情况。(输出不正确?段错误/GPF错误?无限循环/卡死?) - abelenky
这是一个段错误。我应该提到它,谢谢你的提醒。 - qwer
6个回答

13
最终,更加简洁的方法是直接原地反转,如下所示:
#include <stdio.h>
#include <string.h>

void
reverse(char *s)
{
    int a, b, c;
    for (b = 0, c = strlen(s) - 1; b < c; b++, c--) { 
        a = s[b]; 
        s[b] = s[c]; 
        s[c] = a; 
    }

    return; 
}

int main(void)
{
    char string[] = "hello";
    printf("%s\n", string);
    reverse(string);
    printf("%s\n", string);

    return 0;
}

你的解决方案本质上是这个的语义更大的版本。理解指针和数组之间的区别。标准明确规定了这种操作(修改字符串常量的内容)的行为是未定义的。你还应该看一下this eskimo的摘录:

当你使用字符串常量初始化字符数组时:

char string[] = "Hello, world!";

最终你会得到一个包含字符串的数组,你可以随心所欲地修改数组的内容:

string[0] = 'J';

然而,在您的代码中的其他位置使用字符串常量(正式术语为字符串字面值)是可能的。由于它们是数组,当它们在表达式中使用时,编译器会生成指向它们的第一个元素的指针,就像往常一样。也就是说,如果您说

char *p1 = "Hello";
int len = strlen("world");

这就好像你说了一样

char internal_string_1[] = "Hello";
char internal_string_2[] = "world";
char *p1 = &internal_string_1[0];
int len = strlen(&internal_string_2[0]);

在这里,命名为internal_string_1和internal_string_2的数组旨在表明编译器实际上每次在代码中使用字符串常量时都会生成一些临时数组。然而,微妙的事实是,“背后”的字符串常量所代表的数组不一定是可修改的。特别地,编译器可能将它们存储在只读内存中。因此,如果您写入:
char *p3 = "Hello, world!";
p3[0] = 'J';

你的程序可能会崩溃,因为它可能会尝试将一个值(在这种情况下是字符“J”)存储到不可写内存中。

教训是,无论何时你正在构建或修改字符串,都必须确保你正在构建或修改它们的内存是可写的。该内存应该是你已分配的数组,或者是你通过我们将在下一章节中看到的技术动态分配的某些内存。确保你的程序的任何部分都不会尝试修改实际上是编译器为你响应其中一个字符串常量而生成的未命名、不可写数组之一的字符串。 (唯一的例外是数组初始化,因为如果你写入这样的数组,你就是在写入数组,而不是用于初始化数组的字符串字面量。)


1
谢谢你提供代码和解释的链接!这次经历让我学到了比我想象中更多的东西 :P - qwer

12

问题就在这里。

char *somestring = "hello";

somestring指向字符串字面值"hello"。尽管C++标准不能保证这一点,但在大多数计算机上,这将是只读数据,因此您将不被允许修改它。

请改用如下方式声明

char somestring[] = "hello";

我不喜欢这种方法的原因是函数将修改其输入参数。你的情况可能会有所不同。 - dirkgently
1
dirkgently:是的,但它也比另一种方法快大约两倍,并且使用更少的内存。一些程序员致力于处理性能相关的任务。 - John Knoeller
3
如果你的应用程序中性能至关重要的部分是在紧密循环中反转字符串,我认为你应该重新考虑你的设计。 - Mark Byers
1
@Mark:你怎么知道的? - John Knoeller
3
@John:你需要反转的这些字符串是从哪里来的?你如何能够比反转它们更快地生成它们,以便反转成为瓶颈?我相信有一些情况下反转字符串会成为瓶颈,但这些情况可能非常少见。 - Mark Byers
@John Knoeller:修改输入参数如何提高性能?此外,采用这种方法会失去将字符串字面量作为参数传递的能力。最后,如果返回类型是void,则无法链式调用。 - dirkgently

5
试图修改一个可能是只读的内存区域会引起未定义的行为(字符串字面量隐式地被视为const,可以读取但不可写入)。创建一个新的字符串并返回它,或者传递一个足够大的缓冲区并将反转后的字符串写入其中。

0
您可以使用以下代码。
#include<stdio.h>
#include<string.h>
#include<malloc.h>
char * reverse(char*);

int main()
{
        char* string = "hello";
        printf("The reverse string is : %s", reverse(string));
        return 0;
}

char * reverse(char* string)
{

   int var=strlen(string)-1;
     int i,k;
     char *array;
     array=malloc(100);
     for(i=var,k=0;i>=0;i--)
    {
           array[k]=string[i];
            k++;
   }
  return array;
}

0

我猜调用strrev()不是一个选择?


-1

你的逻辑看起来是正确的。与使用指针相比,使用char[]更加简洁。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接