亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

反轉C中的字符串

反轉C中的字符串

C
瀟瀟雨雨 2019-12-13 09:43:30
我開發了一個反向字符串程序。我想知道是否有更好的方法來執行此操作,并且我的代碼是否存在任何潛在問題。我希望練習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;        }    }}
查看完整描述

3 回答

?
慕沐林林

TA貢獻2016條經驗 獲得超9個贊

如果您想練習C的高級功能,那么指針呢?我們也可以在宏和異或交換中投入樂趣!


#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語言的數據類型)。在這種情況下,a char的存儲位置。我們可以 通過給指針加上前綴來取消引用指針*,從而為我們提供存儲在該位置的值。因此,存儲在的值str是*str。


我們可以使用指針進行簡單的算術運算。當我們增加(或減少)指針時,我們只需將其移動以引用該類型值的下一個(或上一個)存儲位置。不同類型的遞增指針可能會將指針移動不同的字節數,因為不同的值在C中具有不同的字節大小。


在這里,我們使用一個指針來引用char字符串中的第一個未處理的指針(str),使用另一個指針來引用最后一個未處理的指針 (end)。我們交換它們的值(*str和*end),然后將指針向內移動到字符串的中間。一旦str >= end它們都指向相同的char,這意味著我們原始的字符串長度是奇數個(中間char不需要顛倒),或者我們已經處理了所有東西。


為了進行交換,我定義了一個macro。宏是由C預處理程序完成的文本替換。它們與功能有很大不同,因此必須知道它們之間的區別。當您調用一個函數時,該函數將對您提供的值進行操作。調用宏時,它只是執行文本替換-因此,您直接給它提供的參數會被使用。


由于我只使用過XOR_SWAP一次宏,因此定義它可能是過大的了,但是它使我在做什么更加清楚。在C預處理器擴展宏之后,while循環如下所示:


    while (str < end)

    {

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

      str++;

      end--;

    }

請注意,每次在宏定義中使用宏參數時,它們都會顯示一次。這可能非常有用-但如果使用不正確,也會破壞您的代碼。例如,如果我已將增量/減量指令和宏調用壓縮為一行,例如


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

然后這將擴展為


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

它具有三倍的增/減操作,并且實際上并沒有執行它應該執行的交換操作。


當我們討論這個主題時,您應該知道xor(^)的含義。這是一種基本的算術運算-像加法,減法,乘法,除法,但它通常不在小學里教。它一點一點地結合了兩個整數-像加法一樣,但是我們不在乎結轉。 1^1 = 0,1^0 = 1, 0^1 = 1,0^0 = 0。


一個眾所周知的技巧是使用xor交換兩個值。這工作XOR因為三個基本屬性:x ^ 0 = x,x ^ x = 0和x ^ y = y ^ x所有值x和y。所以說,我們有兩個變量a,并b與起初存儲兩個值 和。vavb


  // 原來:

  // a == v a 

  // b == v b

  a ^ = b;

  //現在:a == v a ^ v b

  b ^ = a;

  //現在:b == v b ^(v a ^ v b)

  // == v a ^(v b ^ v b)

  // == v a ^ 0

  // == v a

  a ^ = b;

  //現在:a ==(v a ^ v b)^ v a 

  // ==(v a ^ v a)^ v b 

  // == 0 ^ v b 

  // == v b

因此,將交換值。這確實有一個錯誤-when a和b是相同的變量:


  // 原來:

  // a == v a

  a ^ = a;

  //現在:a == v a ^ v a

  // == 0

  a ^ = a;

  //現在:a == 0 ^ 0

  // == 0

  a ^ = a;

  //現在:a == 0 ^ 0

  // == 0

由于我們str < end,在上面的代碼中永遠不會發生這種情況,所以我們可以。


當我們擔心正確性時,我們應該檢查邊緣情況。該if (str)行應確保沒有NULL為字符串提供指針。空字符串""呢?好了strlen("") == 0,所以我們將初始化end為str - 1,這意味著while (str < end)條件永遠不會成立,因此我們什么也不做。哪個是正確的。


有很多C需要探索。玩得開心!


更新: mmw帶來了一個好處,那就是您確實需要謹慎操作,因為它確實就地運行。


 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類型必須為您,char const *因為您無法修改其內容。但是,這不是我的編譯器所生活的世界。


您可以嘗試使用一些技巧來確保某些內存在堆棧或堆中(因此是可編輯的),但是它們不一定是可移植的,并且可能很丑陋。但是,我很樂意為此承擔責任給函數調用者。我已經告訴他們該函數可以進行適當的內存操作,他們有責任給我一個允許這樣做的參數。


查看完整回答
反對 回復 2019-12-13
?
九州編程

TA貢獻1785條經驗 獲得超4個贊

只是重新布置,并進行安全檢查。我還刪除了您未使用的退貨類型。我認為這是安全和干凈的:


#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時,結束點不會指向可能損壞的內存位置。


查看完整回答
反對 回復 2019-12-13
  • 3 回答
  • 0 關注
  • 484 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號