Як повернути рядок на місці в C або C ++?


173

Як ви можете повернути рядок у C або C ++, не вимагаючи окремого буфера, щоб утримувати перетворену рядок?


17
Код у прийнятій відповіді жахливо неправильний . Докладніше див. У розділі stackoverflow.com/a/21263068/88656 .
Ерік Ліпперт

Відповіді:


123

Стандартний алгоритм полягає у використанні покажчиків на початок / кінець і просунути їх всередину, поки вони не зустрінуться або перетнуться посередині. Міняйте, як підете.


Зворотний рядок ASCII, тобто масив, що закінчується 0, де кожен символ вписується в 1 char. (Або інші набори багатобайтових символів).

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, наскільки складніше тіло циклу ASM, коли компілюється xor-swap для x86-64 з gcc -O3.)


Гаразд, добре, давайте виправимо символи UTF-8 ...

(Це справа XOR-swap. Слід зауважити, що ви повинні уникати заміни на себе, тому що якщо *pі *qви однакове місце, ви зануляєте його з ^ a == 0. XOR-своп залежить від наявності двох чітких місць, використовуючи їх як тимчасове зберігання.)

Примітка редактора: ви можете замінити 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;
}
  • Чому, так, якщо вхід оброблений, це буде весело змінюватися поза місцем.
  • Корисне посилання під час скасування в 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 своп поза межами конкуренції з прихованим кодом.
Кріс Конвей

28
Ви думаєте, що "на місці" означає "немає зайвої пам'яті", навіть не O (1) пам'яті для тимчасових виробників? Як щодо місця на стеку для str та зворотної адреси?
Кріс Конвей

55
@Bill, це не те, що означає загальне визначення "на місці". На місці алгоритми можуть використовувати додаткову пам'ять. Однак об'єм цієї додаткової пам'яті не повинен залежати від входу, тобто він повинен бути постійним. Тому заміна значень за допомогою додаткового сховища повністю не відповідає.
Конрад Рудольф

19
Не звертаючись до цього, поки не зміниться xor своп.
Адам Розенфілд

34
Заміна XOR проходить повільніше, ніж обмінятись через регістр на сучасних процесорах поза замовленням.
Патрік Шлютер

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

Це найпростіший спосіб в C ++.


6
У C ++ рядок представлений класом string. Він не запитував "чарівну зірку" чи "брекет". Залишайтеся класним, C.
jokoon

10
@fredsbend, "смішно довга" версія обраної відповіді обробляє випадок, на який ця проста відповідь не відповідає - UTF-8. Це свідчить про важливість повного конкретизації проблеми. Крім того, питання стосувалося коду, який би працював і в C.
Марк Рансом

5
Ця відповідь обробляє випадок, якщо ви використовуєте рядовий клас UTF-8 (або можливо клас символів utf-8 з std :: basic_string). Крім того, у запитанні було сказано "C або C ++", а не "C і C ++". Лише C ++ - це "C або C ++".
Taywee

161

Прочитайте Керніган та Річі

#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, це повільніше, ніж використання
нераціональних

3
Чи не повинна змінна "c" бути знаком замість int?
Lesswire

17
У цьому прикладі важливо зазначити, що рядок sповинен бути оголошений у формі масиву. Іншими словами, char s[] = "this is ok"а не char *s="cannot do this"тому, що остання призводить до константи рядка, яку неможливо змінити
user1527227

3
З вибаченнями у "Хрещеного батька" .... "Залиште зброю, принесіть К & Р". Як фанатик C, я використовував би покажчики, оскільки вони простіші та зрозуміліші для цієї проблеми, але код буде менш переносним для C #, Java тощо

2
@Eric Це не працює в O (log (n)) час. Він працює в O (n), якщо ви посилаєтесь на кількість символьних підкачок, які виконується кодом, для n довжини рядка, то виконується n свопів. Якщо ми говоримо про кількість виконаних циклів, то це все ще O (n) - хоча O (n / 2), але ви константи випадаєте у позначенні Big O.
Стівен Фокс

62

Зворотний рядок на місці (візуалізація):

Переверніть рядок на місці


Класно! Що ви використали для створення цього? Я думаю, що відповідь буде покращена, включивши посилання на неї.
Прифтан

Реалізація цього алгоритму насправді полягає у цій відповіді .
karlphillip

Чи можете ви це трохи пояснити, якщо посилання на зображення вмирає, відповідь не буде марною?
СС Енн

41

Не злий C, якщо припустити загальний випадок, коли рядок є charмасивом, що закінчується нулем :

#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--;
  }
}

Замість використання циклу time, щоб знайти кінцевий покажчик, чи не можна використовувати щось на зразок end_ptr = str + strlen (str); Я знаю, що це зробить практично те саме, але я вважаю це більш зрозумілим.
Пітер Кюне

Досить справедливо. Я намагався (і невдало) уникнути помилки у відповіді @ uvote.
Кріс Конвей

Окрім потенційного покращення продуктивності, можливо int temp, це рішення виглядає найкраще. +1
chux

@ chux-ReinstateMonica Так. Це красиво. Особисто я би видалив () s з цього моменту !(*str).
Прифтан

@ PeterKühne Так чи strchr()шукаю '\0'. Я думав про обох. Не потрібна петля.
Прифтан

35

Ви використовуєте std::reverseалгоритм зі стандартної бібліотеки C ++.


14
Стандартна бібліотека шаблонів - це стандартний термін. Стандарт C ++ не згадує про це, а колишні компоненти STL знаходяться у стандартній бібліотеці C ++.
Неманья Трифунович

16
правильно. Мені завжди цікаво, чому так багато людей все ще називають це "STL", хоча це просто служить для заплутування справи. було б приємніше, якщо більше людей схожі на вас і називають це просто "Стандартна бібліотека C ++" або STL і кажуть "Бібліотека СТАНДАРТів" :)
Йоханнес Шауб - ліб

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все-таки.
Прифтан

24

Використовуйте метод std :: reverse від STL :

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

Ви повинні включити «алгоритм» бібліотеку #include<algorithm>.


1
Яку бібліотеку потрібно включити?
Дон Круйшанк

#include <алгоритм> Я забув її написати, а потім відредагував відповідь, але це не було збережено, ось чому ім'я відсутнє ..
user2628229

Хоча це було б і моєю відповіддю, просто цікаво, тому що ОП попросили "без буфера тримати зворотний рядок" (в основному, заміни місця), як можна довести, що це робиться? Чи просто поясніть, що він використовує внутрішньо std :: iter_swap <>, ітераціюючи до середини буфера з обох кінцевих точок? Також ОП запитав і 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;.
Прифтан

11

Якщо ви шукаєте зворотних буферів, що закінчуються NULL, більшість розміщених тут рішень у порядку. Але, як вже вказував Тім Фарлі, ці алгоритми працюватимуть лише в тому випадку, якщо справедливо вважати, що рядок є семантично масивом байтів (тобто однобайтових рядків), що, на мою думку, є помилковим припущенням.

Візьмемо для прикладу рядок "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-термінатор. Задні ходи не відбудуться.

UTF-16 Little Endian:

Оригінал:

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

Другий байт буде розглядатися як NUL-термінатор. Результатом буде 0x61, 0x00, рядок, що містить символ "a".


std :: reverse буде працювати для двобайтових типів unicode, доки ви використовуєте wstring.
Затьмарення

Я не дуже знайомий із C ++, але я гадаю, що будь-яка поважна стандартна функція бібліотеки, що стосується рядків, зможе обробляти різні кодування, тому я згоден з вами. Під "цими алгоритмами" я мав на увазі розміщені тут спеціальні зворотні функції.
Хуан Пабло Каліфано

На жаль, у стандарті C ++ немає такого поняття, як "поважна функція, що стосується рядків".
Джем

@Eclipse Якщо вона поверне сурогатну пару, результат більше не буде правильним. Unicode насправді не є фіксованою шириною
phuclv

Що мені подобається у цій відповіді, це те, що вона показує фактичне впорядкування байтів (хоч ендіантизм може бути чимось - ах, я бачу, ви це дійсно вважали) і наскільки це правильно чи неправильно. Але я думаю, що відповідь буде покращена, якби ви включили якийсь код, що демонструє це.
Прифтан

11

В інтересах повноти слід зазначити, що на різних платформах є зображення рядків, у яких кількість байтів на символ змінюється залежно від символу. Програмісти старої школи називають це DBCS (подвійний набір символів) . Сучасні програмісти частіше стикаються з цим в UTF-8 (а також UTF-16 та інших). Є й інші такі кодування.

У будь-якій із цих схем кодування змінної ширини прості алгоритми, розміщені тут ( злі , недобрі чи інші ), взагалі не працюватимуть правильно! Насправді вони навіть можуть призвести до того, що рядок стане нечетливою або навіть незаконною рядком у цій схемі кодування. Дивіться відповідь Хуана Пабло Каліфано для кількох хороших прикладів.

std :: reverse () потенційно все ще працюватиме в цьому випадку, доки впровадження вашої платформи Стандартної бібліотеки C ++ (зокрема, ітераторів рядків) належним чином врахувало це.


6
std :: reverse НЕ враховує це. Він обертає значення value_type. У випадку std :: string він обертає значення char. Не символи.
MSalters

Скажіть, що ми, старі шкільні програмісти, знаємо про DBCS, але також знаємо про UTF-8: адже ми всі знаємо, що програмісти - це як наркомани, коли кажуть: «Ще один рядок, і я кину!». Я впевнений, що деякі програмісти врешті-решт кинуть, але відверто кажучи, програмування для мене - це як залежність; Я отримую зняття з не програмування. Це хороший момент, який ви додаєте сюди. Мені не подобається C ++ (я дуже намагався сподобатись, навіть написавши зовсім небагато цього, але це все ще для мене естетично непривабливо сказати), тому я не можу прокоментувати там, але ви все одно добре подумаєте, так мати +1.
Прифтан

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 слід використовувати в будь-якому випадку для читабельності. Для довшої струни завжди потрібно тримати довжину у варіалі. strlen на SIMD зазвичай подається як приклад із цим застереженням, тобто не в реальному житті, або, принаймні, це було 5 років тому, коли був написаний код. ;)
pprzemek

1
Якщо ви хочете, щоб це швидко працювало на реальних процесорах, ви використовуєте перемикання SIMD, щоб зробити зворотний зв'язок в 16B фрагментах. : P, наприклад, на x86, _mm_shuffle_epi8(PSHUFB) може змінити порядок вектора 16В, задавши правий вектор управління переміщенням. Він, ймовірно, може працювати з майже мемкпі швидкістю з ретельною оптимізацією, особливо з AVX2.
Пітер Кордес

char* beg = s-1має невизначену поведінку (принаймні, якщо sвказує на перший елемент масиву, що є найбільш поширеним випадком). while (*++end);має невизначене поведінку, якщо sце порожній рядок.
Мельпомена

@pprzemek Добре, ви пред'являєте претензію, s-1яка визначає поведінку, навіть якщо sвказує на перший елемент масиву, тож саме ви повинні мати можливість цитувати стандарт у підтримці.
мельпомена

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?
Оноріо Катенац

6
strcpy цілком безпечний, якщо програміст може відслідковувати розміри своїх масивів, багато хто стверджує, що strncpy менш безпечний, оскільки це не гарантує, що отримана рядок буде скасована на нуль. У будь-якому випадку, тут немає нічого поганого у використанні strcpy увоте.
Роберт Гембл

1
@Onorio Catelci, strcpy не є небезпечним, якщо ви знаєте, що вихідна рядок поміститься всередині буфера призначення, як у випадках, наведених у наведеному вище коді. Крім того, strncpy нульовим заповнює до числа символів, зазначених у параметрі розміру, якщо є залишене приміщення, що може бути не бажаним.
Кріс Янг

3
Той, хто не може правильно використовувати strcpy, не повинен програмувати на C.
Роберт Гембл

2
@Robert Gamble, я згоден. Однак, оскільки я не знаю жодного способу утримати людей від програмування на С незалежно від їх компетенції, я зазвичай рекомендую проти цього.
Оноріо Катенач


3

Мені подобається відповідь Євгенія K&R. Однак приємно бачити версію за допомогою покажчиків. Інакше це по суті те саме:

#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 ", глибина," ", глибина, val, s, (s? s:" null "), n, (n? n:" null ")); на початку та <в кінці.
Беньот

Натискання кожного charна стек не вважається "на місці". Тим більше не тоді, коли ти насправді натискаєш 4 * 8B на персонажа (на 64-розрядній машині: 3 аргументи + зворотна адреса).
Пітер Кордес

Оригінальне запитання: "Як повернути рядок у C або C ++, не вимагаючи окремого буфера, щоб утримувати перевернутий рядок?" - не було необхідності поміняти місцями "на місці". Також це рішення згадує про погане використання стека з самого початку. Невже я проголосував через погане розуміння читання інших людей?
Simon Peverett

1
Я б не назвав це "сексуальним", але я б сказав, що це демонстративно і повчально. Якщо рекурсія використовується правильно, це може бути дуже цінним. Однак слід зазначити, що - востаннє я знав - C навіть не вимагає стека як такої; однак це робить рекурсію. Так чи інакше, це приклад рекурсії, який при правильному використанні може бути дуже корисним і цінним. Я не думаю, що я коли-небудь бачив, як це використовувалось для перевертання рядка, хоча.
Прифтан


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);

}

Це демонструє арифметику вказівника, подібно до моєї відповіді, але поєднує її зі свопом. Я вважаю, що ця відповідь насправді додає багато. Ви повинні бути в змозі зрозуміти цей тип коду, перш ніж додавати мільярд бібліотек як залежність, просто щоб отримати просте текстове поле (те, що я занадто часто бачу в сучасних додатках, з якими я працюю)
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 () не робить те, що я думаю).
Прифтан

-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");
}

Здається, я є єдиною відповіддю, яка не має буфера чи тимчасової змінної. Я використовую довжину струни, але інші, що роблять це, додають ще одну для голови (проти хвоста). Я припускаю, що стандартний зворотний функціонал зберігає змінну, за допомогою swap або чогось іншого. Таким чином, я маю враження, припускаючи, що математика вказівника та тип UTF вирівнюються, що це, можливо, єдина відповідь, яка насправді відповідає на питання. Додані printf () можна вийняти, я просто зробив це, щоб виглядати приємніше для результату. Я написав це для швидкості. Ніяких додаткових асигнувань чи змін. Напевно, найшвидший алгоритм відображення зворотного 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

Але я думаю, що алгоритм swap 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;
}

Це оптимізований код мовою С для обертання рядка ... І це просто; просто використовуйте простий покажчик, щоб виконати роботу ...


Це друкує рядок по одному знаку за раз. Це не повертає його на місці. Він також чекає введення користувача без причини.
Пітер Кордес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.