คุณจะย้อนกลับสตริงในสถานที่ใน C หรือ C ++ ได้อย่างไร


173

คุณจะย้อนกลับสตริงใน C หรือ C ++ โดยไม่ต้องใช้บัฟเฟอร์ที่แยกต่างหากเพื่อเก็บสายอักขระที่ตรงกันข้ามได้อย่างไร


17
รหัสในคำตอบที่ได้รับการยอมรับเป็นที่ไม่ถูกต้องแสดงออก ดูstackoverflow.com/a/21263068/88656สำหรับรายละเอียด
Eric Lippert

คำตอบ:


123

อัลกอริธึมมาตรฐานคือการใช้พอยน์เตอร์ไปยังจุดเริ่มต้น / สิ้นสุดและเดินเข้าด้านในจนกระทั่งพบหรือข้ามตรงกลาง สลับที่คุณไป


สตริง ASCII ย้อนกลับเช่นอาร์เรย์ 0 สิ้นสุดที่ทุกเหมาะกับตัวละครใน char1 (หรือชุดอักขระที่ไม่ใช่มัลติไบต์อื่น ๆ )

void strrev(char *head)
{
  if (!head) return;
  char *tail = head;
  while(*tail) ++tail;    // find the 0 terminator, like head+strlen
  --tail;               // tail points to the last real char
                        // head still points to the first
  for( ; head < tail; ++head, --tail) {
      // walk pointers inwards until they meet or cross in the middle
      char h = *head, t = *tail;
      *head = t;           // swapping as we go
      *tail = h;
  }
}

// test program that reverses its args
#include <stdio.h>

int main(int argc, char **argv)
{
  do {
    printf("%s ",  argv[argc-1]);
    strrev(argv[argc-1]);
    printf("%s\n", argv[argc-1]);
  } while(--argc);

  return 0;
}

อัลกอริทึมเดียวกันใช้งานได้สำหรับอาร์เรย์จำนวนเต็มที่มีความยาวที่รู้จักเพียงใช้tail = start + length - 1แทนลูปการค้นหาขั้นสุดท้าย

(หมายเหตุบรรณาธิการ: คำตอบนี้เดิมใช้ XOR-swap สำหรับรุ่นง่าย ๆ นี้เช่นกันแก้ไขเพื่อประโยชน์ของผู้อ่านในอนาคตของคำถามยอดนิยมนี้ XOR-swap ไม่แนะนำเป็นอย่างยิ่งยากที่จะอ่านและทำให้การคอมไพล์รหัสของคุณมีประสิทธิภาพน้อยลง สามารถดูได้ใน Godbolt compiler explorerความซับซ้อนของ asm loop body มากขึ้นเมื่อ xor-swap ถูกคอมไพล์สำหรับ x86-64 ด้วย gcc -O3)


ตกลงโอเคลองแก้ไข UTF-8 ตัวอักษร ...

(นี่คือสิ่งที่ XOR-swap โปรดทราบว่าคุณต้องหลีกเลี่ยงการแลกเปลี่ยนด้วยตนเองเพราะถ้า*pและ*qเป็นสถานที่เดียวกันคุณจะกลายเป็นศูนย์ด้วย ^ a == 0 XOR-swap ขึ้นอยู่กับการมีสองตำแหน่งที่แตกต่างกัน ใช้แต่ละรายการเป็นที่จัดเก็บชั่วคราว)

หมายเหตุบรรณาธิการ: คุณสามารถแทนที่ SWP ด้วยฟังก์ชั่นอินไลน์ที่ปลอดภัยโดยใช้ตัวแปร tmp

#include <bits/types.h>
#include <stdio.h>

#define SWP(x,y) (x^=y, y^=x, x^=y)

void strrev(char *p)
{
  char *q = p;
  while(q && *q) ++q; /* find eos */
  for(--q; p < q; ++p, --q) SWP(*p, *q);
}

void strrev_utf8(char *p)
{
  char *q = p;
  strrev(p); /* call base case */

  /* Ok, now fix bass-ackwards UTF chars. */
  while(q && *q) ++q; /* find eos */
  while(p < --q)
    switch( (*q & 0xF0) >> 4 ) {
    case 0xF: /* U+010000-U+10FFFF: four bytes. */
      SWP(*(q-0), *(q-3));
      SWP(*(q-1), *(q-2));
      q -= 3;
      break;
    case 0xE: /* U+000800-U+00FFFF: three bytes. */
      SWP(*(q-0), *(q-2));
      q -= 2;
      break;
    case 0xC: /* fall-through */
    case 0xD: /* U+000080-U+0007FF: two bytes. */
      SWP(*(q-0), *(q-1));
      q--;
      break;
    }
}

int main(int argc, char **argv)
{
  do {
    printf("%s ",  argv[argc-1]);
    strrev_utf8(argv[argc-1]);
    printf("%s\n", argv[argc-1]);
  } while(--argc);

  return 0;
}
  • ทำไมใช่ถ้าข้อมูลถูก borked สิ่งนี้จะเปลี่ยนอย่างมีความสุขนอกสถานที่
  • ลิงค์ที่มีประโยชน์เมื่อทำลายทรัพย์สินใน UNICODE: http://www.macchiato.com/unicode/chart/
  • นอกจากนี้ UTF-8 ส่วนเกิน 0x10000 นั้นยังไม่ได้ทดสอบ (เนื่องจากฉันไม่มีแบบอักษรใด ๆ สำหรับมันและไม่มีความอดทนในการใช้เลขฐานสิบหก)

ตัวอย่าง:

$ ./strrev Räksmörgås ░▒▓○◔◑◕●

░▒▓○◔◑◕● ●◕◑◔○▓▒░

Räksmörgås sågrömskäR

./strrev verrts/.

162
ไม่มีเหตุผลที่ดีที่จะใช้ XOR swap นอกการแข่งขันรหัสที่สับสน
Chris Conway

28
คุณคิดว่า "ในสถานที่" หมายถึง "ไม่มีหน่วยความจำเพิ่มเติม" ไม่แม้แต่หน่วยความจำ O (1) สำหรับขมับ? สิ่งที่เกี่ยวกับพื้นที่ในสแต็กสำหรับ str และที่อยู่ผู้ส่ง?
Chris Conway

55
@ การเรียกเก็บเงินนั่นไม่ใช่ความหมายทั่วไปของ“ แบบแทน” อัลกอริธึมแบบแทนที่อาจใช้หน่วยความจำเพิ่มเติม อย่างไรก็ตามจำนวนหน่วยความจำเพิ่มเติมนี้จะต้องไม่ขึ้นอยู่กับอินพุต - นั่นคือต้องมีค่าคงที่ ดังนั้นการสลับค่าโดยใช้ที่เก็บข้อมูลเพิ่มเติมจึงเป็นแบบสมบูรณ์
Konrad Rudolph

19
อย่าอัปโหลดสิ่งนี้จนกว่าการแลกเปลี่ยนของ xor จะหายไป
Adam Rosenfield

34
การแลกเปลี่ยน XOR ช้ากว่าการแลกเปลี่ยนผ่านการลงทะเบียนบนโปรเซสเซอร์ที่ล้าสมัย
Patrick Schlüter

465
#include <algorithm>
std::reverse(str.begin(), str.end());

นี่เป็นวิธีที่ง่ายที่สุดใน C ++


6
ใน C ++ สตริงจะถูกแทนด้วยคลาสสตริง เขาไม่ได้ถามว่า "char star" หรือ "char charckets" อยู่อย่างมีระดับ, C.
jokoon

10
@fredsbend คำตอบที่เลือก "รุ่นยาวขัน" จัดการกับกรณีที่คำตอบง่ายๆนี้ไม่ได้ - อินพุต UTF-8 มันแสดงให้เห็นถึงความสำคัญของการระบุปัญหาอย่างเต็มที่ นอกจากนี้คำถามเกี่ยวกับรหัสที่จะทำงานใน C เช่นกัน
Mark Ransom

5
คำตอบนี้จะจัดการกรณีและปัญหาถ้าคุณใช้คลาสสตริงที่ทราบ UTF-8 (หรืออาจเป็นคลาสอักขระ utf-8 ที่มี std :: basic_string) นอกจากนี้คำถามที่ถามว่า "C หรือ C ++" ไม่ใช่ "C และ C ++" C ++ เท่านั้นคือ "C หรือ C ++"
Taywee

161

อ่าน Kernighan และ Ritchie

#include <string.h>

void reverse(char s[])
{
    int length = strlen(s) ;
    int c, i, j;

    for (i = 0, j = length - 1; i < j; i++, j--)
    {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
}

8
ทดสอบกับ iphone ของฉันนี้จะช้ากว่าการใช้ที่อยู่ตัวชี้ดิบประมาณ 15%
jjxtra

3
ไม่ควรแปร "c" เป็นอักขระแทนที่จะเป็น int ใช่หรือไม่
Lesswire

17
มันสำคัญที่จะต้องทราบในตัวอย่างนี้ว่าสตริงsจะต้องประกาศในรูปแบบอาร์เรย์ กล่าวอีกนัยหนึ่งchar s[] = "this is ok"แทนที่จะchar *s="cannot do this"เป็นผลลัพธ์หลังในค่าคงที่สตริงซึ่งไม่สามารถแก้ไขได้
user1527227

3
ด้วยการขอโทษที่ "เจ้าพ่อ" .... "ออกจากปืนนำ K&R" ในฐานะที่เป็น Bigot C ฉันจะใช้พอยน์เตอร์เนื่องจากมันง่ายและตรงไปตรงมามากขึ้นสำหรับปัญหานี้ แต่โค้ดจะน้อยกว่าพกพาไปยัง C #, Java และอื่น ๆ

2
@Eric สิ่งนี้ไม่ทำงานในเวลา O (บันทึก (n)) มันจะทำงานใน O (n) ถ้าคุณอ้างถึงจำนวนของการแลกเปลี่ยนอักขระตัวอักษรรหัสดำเนินการสำหรับ n ของความยาวสตริงแล้ว n การแลกเปลี่ยนจะดำเนินการ หากคุณเรากำลังพูดถึงจำนวนลูปที่ทำแล้วยังคง O (n) - แม้ว่า O (n / 2) แต่คุณปล่อยค่าคงที่ในเครื่องหมาย Big O
สตีเฟ่นฟ็อกซ์

62

ย้อนกลับสตริงในสถานที่ (การสร้างภาพ):

ย้อนกลับสตริงในสถานที่


เย็น! คุณใช้อะไรในการสร้างสิ่งนี้ ฉันคิดว่าคำตอบจะได้รับการปรับปรุงโดยรวมลิงก์ไว้ด้วย
Pryftan

การใช้อัลกอริทึมนี้เป็นจริงในคำตอบนี้
karlphillip

คุณช่วยอธิบายให้หน่อยได้ไหมถ้าลิงก์รูปภาพเสียคำตอบนั้นจะไม่ไร้ประโยชน์?
SS Anne

41

Non-evil C สมมติกรณีทั่วไปที่สตริงเป็นcharอาร์เรย์ที่สิ้นสุดด้วยค่า null :

#include <stddef.h>
#include <string.h>

/* PRE: str must be either NULL or a pointer to a 
 * (possibly empty) null-terminated string. */
void strrev(char *str) {
  char temp, *end_ptr;

  /* If str is NULL or empty, do nothing */
  if( str == NULL || !(*str) )
    return;

  end_ptr = str + strlen(str) - 1;

  /* Swap the chars */
  while( end_ptr > str ) {
    temp = *str;
    *str = *end_ptr;
    *end_ptr = temp;
    str++;
    end_ptr--;
  }
}

แทนที่จะใช้ a loop เพื่อค้นหาตัวชี้ปลายคุณไม่สามารถใช้บางอย่างเช่น end_ptr = str + strlen (str); ฉันรู้ว่ามันจะทำแบบเดียวกัน แต่ฉันคิดว่าชัดเจนกว่า
Peter Kühne

ยุติธรรมพอสมควร ฉันพยายาม (และล้มเหลว) เพื่อหลีกเลี่ยงข้อผิดพลาดในคำตอบของ @ uvote
Chris Conway

นอกเหนือจากการปรับปรุงประสิทธิภาพที่อาจเกิดขึ้นด้วยint tempวิธีนี้อาจดูดีที่สุด +1
chux - Reinstate Monica

@ chux-ReinstateMonica ใช่ มันสวย. ส่วนตัวผมจะลบ () s จาก!(*str)แม้ว่า
Pryftan

@ PeterKühne Yes หรือการค้นหาสำหรับstrchr() '\0'ฉันคิดถึงทั้งสองอย่าง ไม่ต้องการห่วง
Pryftan

35

คุณใช้std::reverseอัลกอริทึมจากไลบรารีมาตรฐาน C ++


14
ไลบรารีเทมเพลตมาตรฐานเป็นคำที่เป็นมาตรฐานล่วงหน้า มาตรฐาน C ++ ไม่ได้กล่าวถึงและส่วนประกอบ STL เดิมอยู่ในไลบรารีมาตรฐาน C ++
Nemanja Trifunovic

16
ขวา. ฉันมักจะสงสัยว่าทำไมคนจำนวนมากยังคงเรียกมันว่า "STL" แม้ว่ามันจะทำหน้าที่สร้างความสับสนให้กับเรื่องนี้ จะดีกว่าถ้าคนมากขึ้นเช่นเดียวกับคุณและเรียกว่าเพียงแค่ "C ++ มาตรฐานห้องสมุด" หรือ STL และพูดว่า "มาตรฐานห้องสมุด" :)
โยฮันเน Schaub - litb

27

ไม่นานมานี้และฉันจำไม่ได้ว่าหนังสือเล่มใดสอนอัลกอริทึมนี้ให้ฉัน แต่ฉันคิดว่ามันค่อนข้างฉลาดและเข้าใจง่าย:

char input[] = "moc.wolfrevokcats";

int length = strlen(input);
int last_pos = length-1;
for(int i = 0; i < length/2; i++)
{
    char tmp = input[i];
    input[i] = input[last_pos - i];
    input[last_pos - i] = tmp;
}

printf("%s\n", input);

มันเป็นรูปแบบที่น่าสนใจใช่ ในทางเทคนิคควรจะเป็นsize_tแม้ว่า
Pryftan

24

ใช้std :: reverse method จากSTL :

std::reverse(str.begin(), str.end());

คุณจะต้องรวมถึง "ขั้นตอนวิธีการ" #include<algorithm>ห้องสมุด


1
ต้องรวมไลบรารีใด
Don Cruickshank

#include <algorithm> ฉันลืมเขียนแล้วแก้ไขคำตอบ แต่ไม่ได้รับการบันทึกนั่นคือสาเหตุที่ชื่อหายไป ..
user2628229

แม้ว่านี่จะเป็นคำตอบของฉันเช่นกันเพียงแค่อยากรู้อยากเห็นเพราะ OP ถามว่า "ไม่มีบัฟเฟอร์เพื่อเก็บสตริงที่กลับด้าน" (โดยทั่วไปคือแลกเปลี่ยนในสถานที่) วิธีหนึ่งพิสูจน์ได้ว่าสิ่งนี้ทำเช่นนั้น? มีเพียงแค่อธิบายว่ามันใช้ std :: iter_swap <> ภายในทำซ้ำถึงจุดกึ่งกลางของบัฟเฟอร์จากจุดปลายทั้งสอง? นอกจากนี้ OP ขอให้ทั้ง C และ C ++ นี่เป็นเพียงสำหรับ C ++ (เราหวังได้ไหมว่า <string.h> มีวิธี strrev ()?)
HidekiAI

21

โปรดทราบว่าความสวยงามของ std :: reverse คือมันทำงานได้กับchar *สตริงและstd::wstrings เช่นเดียวกับstd::strings

void strrev(char *str)
{
    if (str == NULL)
        return;
    std::reverse(str, str + strlen(str));
}

1
ในอีกด้านหนึ่งฉันต้องการที่จะอาเจียนเพราะคุณใช้ C ++ แต่ในทางกลับกันมันเป็นสิ่งที่สวยงามสวยงามและสวยงามอย่างยิ่งที่จะเห็นคนใช้ C ++ ที่ไม่ได้ใส่*ตามประเภทและวางไว้ในชื่อแทน ยอดเยี่ยม ผมยอมรับว่าผมมีอะไรบางอย่างพิถีพิถัน char* str;แต่ฉันประจบประแจงทุกครั้งที่ผมเห็นรหัสเช่น
Pryftan

11

หากคุณกำลังมองหาการคืนค่าบัฟเฟอร์ที่ยกเลิกค่า NULL โซลูชันส่วนใหญ่ที่โพสต์ที่นี่ก็โอเค แต่ตามที่ Tim Farley ชี้ให้เห็นแล้วอัลกอริธึมเหล่านี้จะใช้งานได้ก็ต่อเมื่อมันถูกต้องที่จะสมมติว่าสตริงนั้นมีความหมายเป็นอาร์เรย์ของไบต์ (เช่นสตริงไบต์เดียว) ซึ่งเป็นข้อสันนิษฐานที่ผิด

ยกตัวอย่างเช่นสตริง "año" (ปีเป็นภาษาสเปน)

จุดโค้ด Unicode คือ 0x61, 0xf1, 0x6f

พิจารณาการเข้ารหัสที่ใช้มากที่สุด:

Latin1 / iso-8859-1 (การเข้ารหัสไบต์เดียว 1 ตัวคือ 1 ไบต์และในทางกลับกัน):

เดิม:

0x61, 0xf1, 0x6f, 0x00

ย้อนกลับ:

0x6f, 0xf1, 0x61, 0x00

ผลลัพธ์ก็โอเค

UTF-8:

เดิม:

0x61, 0xc3, 0xb1, 0x6f, 0x00

ย้อนกลับ:

0x6f, 0xb1, 0xc3, 0x61, 0x00

ผลที่ได้คือพูดพล่อยๆและลำดับ UTF-8 ที่ผิดกฎหมาย

UTF-16 Big Endian:

เดิม:

0x00, 0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00

ไบต์แรกจะถือว่าเป็น NUL-terminator จะไม่มีการย้อนกลับ

UTF-16 Little Endian:

เดิม:

0x61, 0x00, 0xf1, 0x00, 0x6f, 0x00, 0x00, 0x00

ไบต์ที่สองจะถูกใช้เป็น NUL-terminator ผลลัพธ์จะเป็น 0x61, 0x00 ซึ่งเป็นสตริงที่มีอักขระ 'a'


std :: reverse จะทำงานกับ unicode สองไบต์ตราบเท่าที่คุณใช้ wstring
Eclipse

ฉันไม่คุ้นเคยกับ C ++ มากนัก แต่ฉันเดาว่าฟังก์ชั่นไลบรารีมาตรฐานที่น่านับถือที่จัดการกับสตริงจะสามารถจัดการการเข้ารหัสที่แตกต่างกันได้ดังนั้นฉันจึงเห็นด้วยกับคุณ ด้วย "อัลกอริธึมเหล่านี้" ฉันหมายถึงฟังก์ชั่นสลับกลับรายการที่นี่
Juan Pablo Califano

น่าเสียดายที่ไม่มีสิ่งเช่น "ฟังก์ชั่นที่น่านับถือเกี่ยวกับสตริง" ใน C ++ มาตรฐาน
Jem

@Eclipse ถ้ามันตรงกันข้ามคู่ตัวแทนผลลัพธ์จะไม่ถูกต้องอีกต่อไป Unicode ไม่ใช่ชุดอักขระที่มีความกว้างคงที่
phuclv

สิ่งที่ฉันชอบเกี่ยวกับคำตอบนี้คือมันแสดงให้เห็นถึงการเรียงลำดับไบต์ที่แท้จริง (แม้ว่า endianness อาจเป็นบางสิ่ง - อาฉันเห็นว่าคุณได้พิจารณามันแล้ว) และมันถูกหรือไม่ถูกต้อง แต่ฉันคิดว่าคำตอบจะดีขึ้นหากคุณรวมรหัสที่แสดงให้เห็น
Pryftan

11

ในความสนใจของความสมบูรณ์มันควรจะชี้ให้เห็นว่ามีการแสดงออกของสตริงบนแพลตฟอร์มต่างๆที่จำนวนไบต์ต่อตัวละครแตกต่างกันไปขึ้นอยู่กับตัวละคร โปรแกรมเมอร์เก่าโรงเรียนจะอ้างถึงนี้เป็นDBCS (Double ชุดอักขระไบต์) โปรแกรมเมอร์สมัยใหม่มักพบสิ่งนี้ในUTF-8 (เช่นเดียวกับUTF-16และอื่น ๆ ) มีการเข้ารหัสอื่น ๆ เช่นกัน

ในใด ๆ ของตัวแปรความกว้างเหล่านี้รูปแบบการเข้ารหัสขั้นตอนวิธีการง่าย ๆ ที่โพสต์ได้ที่นี่ ( ความชั่วร้าย , ไม่ใช่ความชั่วร้ายหรือเป็นอย่างอื่น ) จะไม่ทำงานอย่างถูกต้องที่ทุกคน! ในความเป็นจริงพวกเขาอาจทำให้สายอักขระกลายเป็นอ่านไม่ออกหรือแม้แต่สายอักขระที่ผิดกฎหมายในรูปแบบการเข้ารหัสนั้น ดูคำตอบของ Juan Pablo Califanoสำหรับตัวอย่างที่ดี

std :: reverse () อาจยังสามารถใช้งานได้ในกรณีนี้ตราบใดที่การใช้งานแพลตฟอร์มของคุณในไลบรารีมาตรฐาน C ++ (โดยเฉพาะอย่างยิ่งตัววนซ้ำสตริง) จะต้องคำนึงถึงสิ่งนี้เป็นอย่างดี


6
std :: reverse ไม่ได้คำนึงถึงสิ่งนี้ มันกลับค่าของ type_type ใน std :: case string จะทำการปัดอักขระของ char ไม่ใช่ตัวละคร
MSalters

พูดให้ดีว่าโปรแกรมเมอร์ในโรงเรียนเก่ารู้จัก DBCS แต่ก็รู้จัก UTF-8 ด้วยเพราะพวกเราทุกคนรู้ว่าโปรแกรมเมอร์เป็นเหมือนผู้ติดยาเสพติดเมื่อพวกเขาพูดว่า'อีกหนึ่งบรรทัดแล้วฉันจะออกไป!' ฉันแน่ใจว่าโปรแกรมเมอร์บางคนออกในที่สุด แต่การเขียนโปรแกรมตรงไปตรงมาจริงๆเป็นเหมือนติดยาเสพติดสำหรับฉัน; ฉันได้รับการถอนออกจากการไม่เขียนโปรแกรม เป็นจุดที่ดีที่คุณเพิ่มที่นี่ ฉันไม่ชอบ C ++ (ฉันพยายามอย่างหนักจริงๆที่จะชอบมันแม้จะเขียนค่อนข้างน้อย แต่มันก็ยังสำหรับฉันที่ไม่น่าสนใจที่จะกล่าวสุนทรพจน์อย่างสุนทรีย์) ดังนั้นฉันจึงไม่สามารถแสดงความคิดเห็นที่นั่นได้ มี +1
Pryftan

5

อีกวิธี C ++ (แม้ว่าฉันอาจจะใช้ std :: reverse () ตัวฉันเอง :) ว่าเป็นการแสดงออกและเร็วขึ้น)

str = std::string(str.rbegin(), str.rend());

วิธี C (มากหรือน้อย :)) และโปรดระวังเคล็ดลับ XOR สำหรับการแลกเปลี่ยนบางครั้งคอมไพเลอร์ไม่สามารถปรับให้เหมาะสม

ในกรณีเช่นนี้มันมักจะช้ากว่ามาก

char* reverse(char* s)
{
    char* beg = s, *end = s, tmp;
    while (*end) end++;
    while (end-- > beg)
    { 
        tmp  = *beg; 
        *beg++ = *end;  
        *end =  tmp;
    }
    return s;
} // fixed: check history for details, as those are interesting ones

ฉันจะใช้strlenเพื่อหาจุดสิ้นสุดของสตริงถ้ามันอาจจะยาว การติดตั้งไลบรารี่ที่ดีจะใช้ SIMD เวกเตอร์เพื่อค้นหาเร็วกว่า 1 ไบต์ต่อการวนซ้ำ แต่สำหรับสตริงที่สั้นมากwhile (*++end);จะต้องทำก่อนที่การเรียกฟังก์ชันไลบรารีจะเริ่มต้นค้นหา
ปีเตอร์คอ

@PeterCordes เห็นด้วยอย่างดีควรใช้ strlen เพื่อความสะดวกในการอ่าน สำหรับสตริงที่ยาวขึ้นคุณควรรักษาความยาวเป็น varialbe อยู่เสมอ strlen บน SIMD มักจะได้รับเป็นตัวอย่างพร้อมกับข้อจำกัดความรับผิดชอบนี้ซึ่งไม่ใช่แอปพลิเคชันในชีวิตจริงหรืออย่างน้อยก็เมื่อ 5 ปีที่แล้วเมื่อมีการเขียนโค้ด ;)
pprzemek

1
หากคุณต้องการให้สิ่งนี้ทำงานได้อย่างรวดเร็วบน CPU จริงคุณต้องใช้ SIMD shuffles เพื่อย้อนกลับในกลุ่ม 16B : P เช่นบน x86, _mm_shuffle_epi8(PSHUFB) สามารถกลับคำสั่งของเวกเตอร์ 16B ได้เนื่องจากเวกเตอร์ควบคุมการสลับแบบถูกต้อง มันอาจจะทำงานที่เกือบ memcpy ด้วยการเพิ่มประสิทธิภาพอย่างระมัดระวังโดยเฉพาะอย่างยิ่งกับ AVX2
Peter Cordes

char* beg = s-1มีพฤติกรรมที่ไม่ได้กำหนด (อย่างน้อยถ้าsชี้ไปที่องค์ประกอบแรกของอาร์เรย์ซึ่งเป็นกรณีที่พบบ่อยที่สุด) while (*++end);มีพฤติกรรมที่ไม่ได้กำหนดหากsเป็นสตริงว่าง
melpomene

@pprzemek เอาล่ะคุณกำลังอ้างสิทธิ์ที่s-1ได้กำหนดพฤติกรรมแม้ว่าจะsชี้ไปที่องค์ประกอบแรกของอาร์เรย์ดังนั้นคุณจึงควรจะสามารถอ้างอิงมาตรฐานในการสนับสนุนได้
melpomene

4
#include <cstdio>
#include <cstdlib>
#include <string>

void strrev(char *str)
{
        if( str == NULL )
                return;

        char *end_ptr = &str[strlen(str) - 1];
        char temp;
        while( end_ptr > str )
        {
                temp = *str;
                *str++ = *end_ptr;
                *end_ptr-- = temp;
        }
}

int main(int argc, char *argv[])
{
        char buffer[32];

        strcpy(buffer, "testing");
        strrev(buffer);
        printf("%s\n", buffer);

        strcpy(buffer, "a");
        strrev(buffer);
        printf("%s\n", buffer);

        strcpy(buffer, "abc");
        strrev(buffer);
        printf("%s\n", buffer);

        strcpy(buffer, "");
        strrev(buffer);
        printf("%s\n", buffer);

        strrev(NULL);

        return 0;
}

รหัสนี้สร้างผลลัพธ์นี้:

gnitset
a
cba

2
@uvote อย่าใช้ strcpy เคย. ถ้าคุณต้องใช้อะไรเช่น strcpy ใช้ strncpy strcpy เป็นอันตราย โดยวิธี C และ C ++ เป็นสองภาษาแยกกันพร้อมสิ่งอำนวยความสะดวกแยก ฉันคิดว่าคุณกำลังใช้ไฟล์ส่วนหัวที่มีเฉพาะใน C ++ ดังนั้นคุณต้องการคำตอบใน C หรือไม่?
Onorio Catenacci

6
strcpy มีความปลอดภัยอย่างสมบูรณ์แบบหากโปรแกรมเมอร์สามารถติดตามขนาดของอาเรย์ของเขาได้หลายคนอ้างว่า strncpy นั้นมีความปลอดภัยน้อยลงเนื่องจากไม่รับประกันว่าผลลัพธ์สตริงนั้นจะถูกยกเลิก ไม่ว่าในกรณีใดไม่มีอะไรผิดปกติกับการใช้ strote ของ uvote ที่นี่
Robert Gamble

1
@ Onorio Catenacci, strcpy ไม่เป็นอันตรายหากคุณรู้ว่าสตริงต้นทางจะพอดีกับบัฟเฟอร์ปลายทางเช่นเดียวกับในกรณีที่ระบุไว้ในรหัสข้างต้น นอกจากนี้ strncpy ศูนย์เติมได้ถึงจำนวนตัวอักษรที่ระบุไว้ในพารามิเตอร์ขนาดถ้ามีห้องเหลือซึ่งอาจไม่ต้องการ
Chris Young

3
ทุกคนที่ไม่สามารถใช้ strcpy ได้อย่างเหมาะสมไม่ควรเขียนโปรแกรมใน C.
Robert Gamble

2
@ Robert Gamble ฉันเห็นด้วย อย่างไรก็ตามเนื่องจากฉันไม่รู้วิธีที่จะป้องกันไม่ให้ผู้คนเขียนโปรแกรมใน C ไม่ว่าพวกเขาจะมีทักษะอะไรฉันจึงมักจะแนะนำเรื่องนี้
Onorio Catenacci


3

ฉันชอบคำตอบ K&R ของ Evgeny อย่างไรก็ตามมันเป็นเรื่องดีที่ได้เห็นรุ่นที่ใช้ตัวชี้ ไม่เช่นนั้นมันจะเหมือนกัน:

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

char *reverse(char *str) {
    if( str == NULL || !(*str) ) return NULL;
    int i, j = strlen(str)-1;
    char *sallocd;
    sallocd = malloc(sizeof(char) * (j+1));
    for(i=0; j>=0; i++, j--) {
        *(sallocd+i) = *(str+j);
    }
    return sallocd;
}

int main(void) {
    char *s = "a man a plan a canal panama";
    char *sret = reverse(s);
    printf("%s\n", reverse(sret));
    free(sret);
    return 0;
}

3

ฟังก์ชั่นวนซ้ำเพื่อย้อนกลับสตริงในตำแหน่ง (ไม่มีบัฟเฟอร์พิเศษ, malloc)

รหัสสั้นเซ็กซี่ การใช้งานสแต็กไม่ดี

#include <stdio.h>

/* Store the each value and move to next char going down
 * the stack. Assign value to start ptr and increment 
 * when coming back up the stack (return).
 * Neat code, horrible stack usage.
 *
 * val - value of current pointer.
 * s - start pointer
 * n - next char pointer in string.
 */
char *reverse_r(char val, char *s, char *n)
{
    if (*n)
        s = reverse_r(*n, s, n+1);
   *s = val;
   return s+1;
}

/*
 * expect the string to be passed as argv[1]
 */
int main(int argc, char *argv[])
{
    char *aString;

    if (argc < 2)
    {
        printf("Usage: RSIP <string>\n");
        return 0;
    }

    aString = argv[1];
    printf("String to reverse: %s\n", aString );

    reverse_r(*aString, aString, aString+1); 
    printf("Reversed String:   %s\n", aString );

    return 0;
}

1
นั่นเป็นวิธีที่สนุกมากคุณควรเพิ่มการติดตามบางอย่างเช่น printf ("% * s> [% d] reverse_r ('% c',% p = \"% s \ ",% p = \"% s \ ") \ n ", ความลึก," ", ความลึก, วาล, s, (s? s:" โมฆะ "), n, (n? n:" โมฆะ ")); ที่เริ่มและ <ท้าย
Benoît

การกดแต่ละครั้งcharลงบนสแต็กจะไม่นับเป็น "เข้าแทนที่" โดยเฉพาะอย่างยิ่งเมื่อคุณกด 4 * 8B ต่อตัวอักษรจริง ๆ (บนเครื่อง 64- บิต: 3 args + a address return)
ปีเตอร์คอ

คำถามเดิมคือ "คุณจะย้อนกลับสตริงใน C หรือ C ++ โดยไม่ต้องใช้บัฟเฟอร์ที่แยกต่างหากเพื่อเก็บสตริงที่ตรงกันข้ามได้อย่างไร" - ไม่มีข้อกำหนดในการสลับ 'แทนที่' นอกจากนี้โซลูชันนี้ยังกล่าวถึงการใช้งานสแต็กที่ไม่ดีตั้งแต่เริ่มต้น ฉันถูกลงคะแนนเนื่องจากความเข้าใจในการอ่านที่ไม่ดีของคนอื่นหรือไม่?
Simon Peverett

1
ฉันจะไม่เรียกมันว่า 'เซ็กซี่' แต่ฉันจะบอกว่ามันเป็นการสาธิตและการเรียนการสอน หากมีการใช้การสอบถามซ้ำอย่างถูกต้องจะมีค่ามาก อย่างไรก็ตามมันควรจะชี้ให้เห็นว่า - ล่าสุดที่ฉันรู้ - C ไม่จำเป็นต้องมีสแต็คต่อ se; อย่างไรก็ตามมันจะเรียกซ้ำ ไม่ว่าจะเป็นตัวอย่างของการเรียกซ้ำซึ่งหากใช้อย่างเหมาะสมจะมีประโยชน์และมีคุณค่ามาก ฉันไม่คิดว่าฉันเคยเห็นมันใช้เพื่อย้อนกลับสตริงแม้ว่า
Pryftan


1

แบ่งปันรหัสของฉัน ในฐานะผู้เรียน C ++ ซึ่งเป็นตัวเลือกในการใช้ swap () ฉันขอความคิดเห็นอย่างถ่อมตน

void reverse(char* str) {
    int length = strlen(str);
    char* str_head = str;
    char* str_tail = &str[length-1];
    while (str_head < str_tail) 
        swap(*str_head++, *str_tail--);
}


0

อีกอย่างหนึ่ง:

#include <stdio.h>
#include <strings.h>

int main(int argc, char **argv) {

  char *reverse = argv[argc-1];
  char *left = reverse;
  int length = strlen(reverse);
  char *right = reverse+length-1;
  char temp;

  while(right-left>=1){

    temp=*left;
    *left=*right;
    *right=temp;
    ++left;
    --right;

  }

  printf("%s\n", reverse);

}

นี่แสดงให้เห็นถึงเลขคณิตตัวชี้เหมือนกับคำตอบของฉัน แต่รวมกับ Swap ฉันเชื่อว่าคำตอบนี้เพิ่มขึ้นมากมายจริง ๆ แล้ว คุณควรจะเข้าใจโค้ดประเภทนี้ก่อนที่จะเพิ่มห้องสมุดเป็นพันล้านเพื่อเป็นการอ้างอิงเพียงเพื่อให้ได้กล่องข้อความธรรมดา (สิ่งที่ฉันเห็นบ่อยเกินไปในแอพพลิเคชั่นที่ทันสมัยที่ฉันทำงานด้วย)
Stephen J

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

unsigned char * utf8_reverse(const unsigned char *, int);
void assert_true(bool);

int main(void)
{
    unsigned char str[] = "mañana mañana";
    unsigned char *ret = utf8_reverse(str,  strlen((const char *) str) + 1);

    printf("%s\n", ret);
    assert_true(0 == strncmp((const char *) ret, "anãnam anañam", strlen("anãnam anañam") + 1));

    free(ret);

    return EXIT_SUCCESS;
}

unsigned char * utf8_reverse(const unsigned char *str, int size)
{
    unsigned char *ret = calloc(size, sizeof(unsigned char*));
    int ret_size = 0;
    int pos = size - 2;
    int char_size = 0;

    if (str ==  NULL) {
        fprintf(stderr, "failed to allocate memory.\n");
        exit(EXIT_FAILURE);
    }

    while (pos > -1) {

        if (str[pos] < 0x80) {
            char_size = 1;
        } else if (pos > 0 && str[pos - 1] > 0xC1 && str[pos - 1] < 0xE0) {
            char_size = 2;
        } else if (pos > 1 && str[pos - 2] > 0xDF && str[pos - 2] < 0xF0) {
            char_size = 3;
        } else if (pos > 2 && str[pos - 3] > 0xEF && str[pos - 3] < 0xF5) {
            char_size = 4;
        } else {
            char_size = 1;
        }

        pos -= char_size;
        memcpy(ret + ret_size, str + pos + 1, char_size);
        ret_size += char_size;
    }    

    ret[ret_size] = '\0';

    return ret;
}

void assert_true(bool boolean)
{
    puts(boolean == true ? "true" : "false");
}

0

ด้วยแลมบ์ดา C ++:

 auto reverse = [](std::string& s) -> std::string {
        size_t start = 0, end = s.length() -1;
        char temp;

        while (start < end) {
          temp = s[start];
          s[start++] = s[end];
          s[end--] = temp;
        } 

        return s;
   };

0

คำตอบของฉันจะคล้ายกับส่วนใหญ่ แต่โปรดหารหัสของฉันที่นี่

//Method signature to reverse string
string reverseString(string str);

int main(void){
    string str;
    getline(cin, str);
    str =  reverseString(str);
    cout << "The reveresed string is : " << str;
    return 0;
}

/// <summary>
///     Reverses the input string.
/// </summary>
/// <param name="str">
///    This is the input string which needs to be reversed.
/// </param>
/// <return datatype = string>
///     This method would return the reversed string
/// </return datatype>

string reverseString(string str){
    int length = str.size()-1;
    char temp;
    for( int i=0 ;i<(length/2);i++)
    {
        temp = str[i];
        str[i] = str[length-i];
        str[length-i] = temp;
    }
    return str;
}

0

ฉันคิดว่ามีวิธีอื่นในการย้อนกลับสตริง รับอินพุตจากผู้ใช้และย้อนกลับ

void Rev() {
    char ch;
    cin.get(ch);
    if(ch != '\n') {
        Rev();
        cout.put(ch);
    }
}

เป็นจริง แต่เพียงพิมพ์คำสั่งที่กลับด้านเท่านั้นใช่มั้ย (ฉันไม่ได้ใช้ C ++ ดังนั้นบางที .put () ไม่ได้ทำตามที่ฉันคิด)
Pryftan

-1

หากคุณไม่ต้องการเก็บไว้คุณสามารถลดเวลาที่ใช้ในการทำสิ่งนี้:

void showReverse(char s[], int length)
{
    printf("Reversed String without storing is ");
    //could use another variable to test for length, keeping length whole.
    //assumes contiguous memory
    for (; length > 0; length--)
    {
        printf("%c", *(s+ length-1) );
    }
    printf("\n");
}

ฉันดูเหมือนจะเป็นคำตอบเดียวที่ไม่มีบัฟเฟอร์หรือตัวแปรชั่วคราว ฉันใช้ความยาวของสตริง แต่คนอื่น ๆ ที่ทำนี้เพิ่มอีกหนึ่งสำหรับหัว (vs หาง) ฉันสมมติว่า func แบบย้อนกลับมาตรฐานเก็บตัวแปรผ่านทาง swap หรืออะไรทำนองนั้น ดังนั้นฉันอยู่ภายใต้การแสดงผลสมมติว่ามีตัวชี้คณิตศาสตร์และประเภท UTF ขึ้นมานี่อาจเป็นคำตอบเดียวที่ตอบคำถามได้จริง printf () ที่เพิ่มเข้ามานั้นสามารถนำออกมาได้ฉันเพียง แต่ทำเพื่อให้ได้ผลลัพธ์ที่ดีกว่า ฉันเขียนมันเพื่อความเร็ว ไม่มีการจัดสรรหรือ vars พิเศษ อาจเป็นอัลกอริทึมที่เร็วที่สุดสำหรับการแสดง reverse str ()
Stephen J

-3

นี่คือสิ่งที่ฉันทำใน C. สำหรับการฝึกฝนและพยายามที่จะกระชับที่สุด! คุณป้อนสตริงผ่านบรรทัดคำสั่งเช่น. /program_name "ป้อนสตริงที่นี่"

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

void reverse(int s,int e,int len,char t,char* arg) {
   for(;s<len/2;t=arg[s],arg[s++]=arg[e],arg[e--]=t);
}

int main(int argc,char* argv[]) {
  int s=0,len=strlen(argv[1]),e=len-1; char t,*arg=argv[1];
  reverse(s,e,len,t,arg);
  for(s=0,e=0;e<=len;arg[e]==' '||arg[e]=='\0'?reverse(s,e-1,e+s,t,arg),s=++e:e++);
  printf("%s\n",arg);
}

1
เป็นระเบียบอ่านไม่ได้ แต่เห็นได้ชัดว่าคุณจะไม่ได้รหัสสั้นที่สุด ( รหัสกอล์ฟ ) เพราะชื่อตัวแปรบางตัวมีมากกว่าหนึ่งตัวอักษร
ปีเตอร์คอ

-4

แต่ฉันคิดว่าอัลกอริทึมการแลกเปลี่ยน XOR นั้นดีที่สุด ...

char str[]= {"I am doing reverse string"};
char* pStr = str;

for(int i = 0; i != ((int)strlen(str)-1)/2; i++)
{
    char b = *(pStr+i);
    *(pStr+i) = *(pStr+strlen(str)-1-i);
    *(pStr+strlen(str)-1-i) = b;
}

การเรียกไปยังstrlen()สภาวะลูปและในเนื้อความลูปอาจไม่ได้ปรับให้เหมาะสม
ปีเตอร์คอ

-5

นี่เป็นวิธีที่สะอาดปลอดภัยและง่ายที่สุดในการย้อนกลับสตริงใน C ++ (ตามความเห็นของฉัน):

#include <string>

void swap(std::string& str, int index1, int index2) {

    char temp = str[index1];
    str[index1] = str[index2];
    str[index2] = temp;

}

void reverse(std::string& str) {

    for (int i = 0; i < str.size() / 2; i++)
        swap(str, i, str.size() - i - 1);

}

อีกทางเลือกหนึ่งคือใช้std::swapแต่ฉันชอบกำหนดฟังก์ชั่นของตัวเอง - มันเป็นแบบฝึกหัดที่น่าสนใจและคุณไม่จำเป็นต้องมีincludeอะไรพิเศษ


-5
#include<stdio.h>
#include<conio.h>

int main()
{
    char *my_string = "THIS_IS_MY_STRING";
    char *rev_my_string = my_string;

    while (*++rev_my_string != '\0')
        ;

    while (rev_my_string-- != (my_string-1))
    {
        printf("%c", *rev_my_string);
    }

    getchar();
    return 0;
}

นี่คือรหัสที่ปรับให้เหมาะสมที่สุดในภาษา C สำหรับการย้อนกลับสตริง ... และมันก็เป็นเรื่องง่าย เพียงใช้ตัวชี้แบบง่าย ๆ ในการทำงาน ...


สิ่งนี้พิมพ์สตริงหนึ่งอักขระในเวลา มันไม่ได้ย้อนกลับเข้าที่ นอกจากนี้ยังรอการป้อนข้อมูลของผู้ใช้โดยไม่มีเหตุผล
ปีเตอร์คอ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.