【问题标题】:Reversing a String in C from scratch从头开始在 C 中反转字符串
【发布时间】:2020-09-22 17:10:38
【问题描述】:

我正在尝试为学校项目反转 C 中的字符串数组。这是我的代码。

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

void reverse(char * str) {
  char * p1 = str;
  int len = strlen(str) - 1;
  char temp;
  int m;

  for (m=0; m<(len/2); m++){
      temp = str[len];
      str[len]= str[m];
      str[m]=temp;
      len=len-1;
  }
  return;
}                                                                                                          

int main(void) {
  char str0[] = "";
  char str1[] = "123";
  char str2[] = "abcd";
  char str3[] = "Captain's log, Stardate 42523.7";
  char str4[] = "Hello, my name is Inigo Montoya.";
  char str5[] = "You can be my wingman anyday!";
  char str6[] = "Executor Selendis! Unleash the full power of your forces! There may be no tomorrow!";
  char * array[] = {str0, str1, str2, str3, str4, str5, str6};
  for (int i = 0; i < 7; i++) {
    reverse(array[i]);
    printf("%s\n", array[i]);
  }
  return EXIT_SUCCESS;
}

我写了反向函数。问题是当我编译和运行代码时,它给出了直到 str3[] 的所有字符串的预期结果。但在那之后,它搞乱了顺序。这是输出

321
dbca
7.32524 etlog, Starda s'niatpaC
.ayotnoM oname is Inig ym ,olleH
!yadyna ne my wingmab nac uoY
!worromot on eb yam erehT !the full power of your forces hsaelnU !sidneleS rotucexE

我不知道为什么它对某些字符串有效,但对其他字符串无效。任何帮助将非常感激。

【问题讨论】:

  • 我认为你需要使用一个 end 变量,你可以四处移动,不要管 len 。我认为但尚未验证问题是 (len/2),而您一直在缩短 len。
  • 我建议 len 只有在它的衡量标准也发生变化时才应该改变。开始使用变量名称,如idx 或带有前缀/后缀的变量来索引变量。将它们从用于幅度或计数的变量中分类更容易(len 是幅度、计数或用于计数的计数器等)。计数和其他量值通常用于限制,并且通常应该具有稳定的值,直到它们不被使用或重置。索引变量可能看起来像一个计数器,但这不是它们在代码中的主要作用,它们被用作数组的偏移量。

标签: c reverse c-strings strlen function-definition


【解决方案1】:

您在循环中修改len 变量,因此它不再保持字符串长度。相反,您可以使用以下代码:

 for (m = 0; m < (len + 1) / 2; m++) {
     temp = str[len - m]; // m symbol from end
     str[len - m] = str[m];
     str[m] = temp;
 }

【讨论】:

  • 是的,len/2 作为条件不起作用,因为len 实际上是strlen() - 1
  • 你可以使用m &lt; len - m作为for循环的条件。
【解决方案2】:

对于初学者来说,变量len 的值正在循环中被更改

for (m=0; m<(len/2); m++){
    //...
    len=len-1;
}

因此循环中的条件m&lt;(len/2也被改变了。

但是,如果您将为循环的右侧元素的索引引入一个中间变量,而不是使用变量len,因为它在其他答案中的建议在任何情况下循环都将无效。

在这种情况下,您的函数 reverse 的问题是您减少了变量 len

int len = strlen(str) - 1;

循环之前

for (m=0; m<(len/2); m++){

例如,如果传递的字符串的长度等于4,那么循环应该是这样的

for (m=0; m < 2; m++){

即索引为01 的字符串的元素将与索引为32 的字符串的元素相应地交换。

然而,在减少变量 len 之后,循环现在看起来像

for (m=0; m < 1; m++){

因为3 / 2 产生1

例如,如果您尝试反转字符串"ab",输出将是相同的

ab

因为字符串的长度等于2,长度减1等于1。所以条件m&lt;(len/2)等于m &lt; 0,不会执行循环。

所以对于偶数长度的字符串,你会得到不正确的结果。

请注意,您应该使用类型size_t,而不是用于存储字符串长度的对象的类型int。它是函数strlen 所具有的类型。此外,通常int 类型的对象无法为大字符串保存size_t 类型的值。所以你的函数有一个缺点,它不能正确处理非常大的字符串。

也不要使用幻数,例如7。而是尝试使用命名常量。

所以函数 reverse 可以如下所示

char * reverse( char *s )
{
    for ( size_t i = 0, n = strlen( s ); i < n / 2; i++ )
    {
        char c = s[i];
        s[i] = s[n-i-1];
        s[n-i-1] = c;
    }

    return s;
}

要输出一个反转的字符串,您可以在 main 中编写示例

puts( reverse( array[i] ) );

这是一个演示程序。

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

char * reverse( char *s )
{
    for ( size_t i = 0, n = strlen( s ); i < n / 2; i++ )
    {
        char c = s[i];
        s[i] = s[n-i-1];
        s[n-i-1] = c;
    }

    return s;
}

int main(void) 
{
    char str0[] = "";
    char str1[] = "123";
    char str2[] = "abcd";
    char str3[] = "Captain's log, Stardate 42523.7";
    char str4[] = "Hello, my name is Inigo Montoya.";
    char str5[] = "You can be my wingman anyday!";
    char str6[] = "Executor Selendis! Unleash the full power of your forces! There may be no tomorrow!";
    char * array[] = { str0, str1, str2, str3, str4, str5, str6 };

    const size_t N = sizeof( array ) / sizeof( *array );

    for ( size_t i = 0; i < N; i++ ) 
    {
        puts( reverse( array[i] ) );
    }

    return EXIT_SUCCESS;
}

它的输出是

321
dcba
7.32524 etadratS ,gol s'niatpaC
.ayotnoM oginI si eman ym ,olleH
!yadyna namgniw ym eb nac uoY
!worromot on eb yam erehT !secrof ruoy fo rewop lluf eht hsaelnU !sidneleS rotucexE

如果你想使用指针而不是下标运算符来实现函数,那么函数可以看起来像

char * reverse( char *s )
{
    if ( *s )    // check that the passed string is not empty
    {
        for ( char *first = s, *last = s + strlen( s ); first < --last; ++first )
        {
            char c = *first;
            *first = *last;
            *last = c;
        }
    }       

    return s;
}

【讨论】:

    【解决方案3】:

    另一种方法是有两个变量(例如)leftright

    left 递增,right 递减,只要:left &lt; right

    这里有两种方法。一种是使用索引变量。另一个使用指针变量:

    #include <string.h>
    
    void
    reverse_byidx(char *str)
    {
        int left = 0;
        int right = strlen(str) - 1;
        char temp;
    
        for (;  left < right;  ++left, --right) {
            temp = str[left];
            str[left] = str[right];
            str[right] = temp;
        }
    }
    
    void
    reverse_byptr(char *str)
    {
        char *left = str;
        char *right = &str[strlen(str) - 1];
        char temp;
    
        for (;  left < right;  ++left, --right) {
            temp = *left;
            *left = *right;
            *right = temp;
        }
    }
    

    【讨论】:

      【解决方案4】:

      改成这样:

        // here we add a back variable
        // so that we don't modify the len variable on which the for loop
        // depends, now back will be our index at the last character
        int back = len;
      
        for (m=0; m<(len/2); ++m){
            temp = str[back];
            str[back]= str[m];
            str[m]=temp;
            --back;
        }
      

      【讨论】:

      • back -= 1 会更好。
      • 更好! ;)
      • m&lt;(len/2) 因为循环条件不起作用,因为在 OP 的代码中,len 等于 strlen(str) - 1... 您可以改用 m&lt;back 作为条件。
      • 就我个人而言,我会将--back 放在for 循环的后置语句中(或其他任何名称)。请参阅 Craig Estey 的答案,了解它的外观(以及正常工作的条件的额外奖励)
      【解决方案5】:

      在您的for 循环中,您使用len/2 作为边界条件,但您还修改了循环中的len,这意味着您最终会提前停止循环。

      它实际上很容易修复——将循环条件更改为:

      for (m=0; m<len; m++){
      

      或者,复制len 并减少:

      for (m=0, e=len; m<(len/2); m++, e--){
        temp = str[e];
        str[e]= str[m];
        str[m]=temp;
      

      }

      【讨论】:

      • m&lt;(len/2) 因为循环条件不起作用,因为在 OP 的代码中 len 等于 strlen(str) - 1... 您可以使用 m&lt;e 作为条件。
      • @isrnick: m &lt; (len/2) 工作正常——不需要在奇数长度的字符串中将中间字符与自身交换(如果没有 -1 就会发生这种情况。) m &lt; e 工作得同样好(并且避免需要 /2)
      • m&lt;(len/2) 在偶数长度的字符串上会失败。假设你有一个长度为 4 的字符串,那么 len 将是 3。3/2 = 1。循环将在一次迭代后结束,因为 m&lt;(len/2)-> 1&lt;1 的计算结果为 false。
      • 在这种情况下,m &lt; (len/2) 对于偶数长度的字符串(不是奇数)失败,中间字符不会交换。
      猜你喜欢
      • 2010-10-21
      • 2015-05-24
      • 2015-02-03
      • 1970-01-01
      • 2020-02-07
      • 1970-01-01
      相关资源
      最近更新 更多