Как вы перевернуть строку на месте в C или C ++?

173

Как перевернуть строку в C или C ++, не требуя отдельного буфера для хранения перевернутой строки?

Кодирование Маш
источник
17
Код в принятом ответе ужасно неверен . См. Stackoverflow.com/a/21263068/88656 для получения подробной информации.
Эрик Липперт

Ответы:

123

Стандартный алгоритм состоит в том, чтобы использовать указатели на начало / конец и перемещать их внутрь, пока они не встретятся или не пересекаются в середине. Поменяйтесь, как вы идете.


Обратная строка ASCII, то есть массив с нулем в конце, где каждый символ помещается в 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. Обратите внимание, что вы должны избегать обмена с self, потому что если *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;
}
  • Почему, да, если ввод не работает, это будет весело поменять местами.
  • Полезная ссылка при вандализме в 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
@ Билл, это не то, что означает общее определение «на месте». Алгоритмы на месте могут использовать дополнительную память. Тем не менее, объем этой дополнительной памяти не должен зависеть от ввода - т.е. он должен быть постоянным. Таким образом, обмен значениями с использованием дополнительного хранилища полностью на месте.
Конрад Рудольф
19
Не одобряйте это, пока обмен XOR не пройдет.
Адам Розенфилд
34
Обмен XOR выполняется медленнее, чем обмен через регистр на современных процессорах, вышедших из строя.
Патрик Шлютер
465
#include <algorithm>
std::reverse(str.begin(), str.end());

Это самый простой способ в C ++.

Грег Роджерс
источник
6
В C ++ строка представлена ​​классом string. Он не просил «чар звезду» или «символ скобки». Оставайтесь стильными, C.
Jokoon
10
@fredsbend, «смешно длинная» версия выбранного ответа обрабатывает случай, которого нет в этом простом ответе - ввод UTF-8. Это показывает важность полного определения проблемы. Кроме того, вопрос был о коде, который будет работать и в Си.
Марк Рэнсом
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 это медленнее, чем использование необработанных адресов указателей примерно на 15%
jjxtra
3
Разве переменная "c" не должна быть char вместо int?
Lesswire
17
В этом примере важно отметить, что строка sдолжна быть объявлена ​​в виде массива. Другими словами, char s[] = "this is ok"а не char *s="cannot do this"потому, что последнее приводит к строковой константе, которая не может быть изменена
user1527227
3
С извинениями перед «Крестным отцом» .... «Оставь оружие, принеси K & R». Как фанат C, я бы использовал указатели, так как они проще и
2
@Eric Это не выполняется за время O (log (n)). Он запускается в O (n), если вы ссылаетесь на количество символьных перестановок, которые выполняет код, для n длины строки выполняется n перестановок. Если вы говорите о количестве выполненных циклов, то оно все равно O (n) - хотя O (n / 2), но вы отбрасываете константы в обозначении Big O.
Стивен Фокс
62

Переверните строку на месте (визуализация):

Переверните строку на месте

slashdottir
источник
Прохладно! Что вы использовали для создания этого? Я думаю, что ответ будет улучшен путем включения ссылки на него.
Прифтан
Реализация этого алгоритма на самом деле в этом ответе .
Карлфиллип
Можете ли вы объяснить это немного, так что если ссылка на изображение умирает, ответ не будет бесполезным?
SS Anne
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--;
  }
}
Крис Конвей
источник
Вместо того, чтобы использовать цикл while для поиска указателя конца, вы не можете использовать что-то вроде end_ptr = str + strlen (str); Я знаю, что это сделает практически то же самое, но я нахожу это более ясным.
Питер Кюне
Справедливо. Я пытался (и не смог) избежать однозначной ошибки в ответе @ uvote.
Крис Конвей
Помимо возможного улучшения производительности с возможно int temp, это решение выглядит лучше всего. +1
chux - Восстановить Монику
@ chux-ReinstateMonica Да. Это прекрасно. Лично я бы удалить () из !(*str)хотя.
Прифтан
@ PeterKühne Да или strchr()ищу '\0'. Я думал об обоих. Не нужно для петли.
Прифтан
35

Вы используете std::reverseалгоритм из стандартной библиотеки C ++.

Неманья Трифунович
источник
14
Стандартная библиотека шаблонов - это стандартный термин. Стандарт C ++ не упоминает об этом, и бывшие компоненты STL находятся в стандартной библиотеке C ++.
Неманя Трифунович
16
право. Я всегда удивляюсь, почему так много людей все еще называют это «STL», хотя это просто запутывает вопрос. было бы лучше, если бы больше людей были такими же, как вы, и назвали бы это просто «C ++ Standard Library» или STL и сказали бы «STandard Library» :)
Johannes 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);
karlphillip
источник
Это интересный вариант, да. Технически должно быть size_tхоть.
Прифтан
24

Используйте метод std :: reverse из STL :

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

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

user2628229
источник
1
Какая библиотека должна быть включена?
Дон Круикшанк
#include <алгоритм> Я забыл написать его, а затем отредактировал ответ, но он не был сохранен, поэтому имя отсутствовало ..
user2628229
Хотя это был бы и мой ответ, просто любопытно, потому что ОП спросил "без буфера для хранения обратной строки" (в основном, замена на месте), как можно доказать, что это происходит? Просто объясните, что он внутренне использует 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;.
Прифтан
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 будет работать для двухбайтовых типов юникода, если вы используете wstring.
Затмение
Я не очень знаком с C ++, но я предполагаю, что любая респектабельная стандартная библиотечная функция, работающая со строками, сможет обрабатывать различные кодировки, поэтому я согласен с вами. Под «этими алгоритмами» я имел в виду специальные обратные функции, размещенные здесь.
Хуан Пабло Калифано
К сожалению, в стандарте C ++ не существует понятия «респектабельная функция, работающая со строками».
Джем
@Eclipse Если это перевернет суррогатную пару, результат больше не будет правильным. Unicode на самом деле не является кодировкой фиксированной ширины
phuclv
Что мне нравится в этом ответе, так это то, что он показывает фактическое упорядочение байтов (хотя порядок байтов может быть чем-то - ах, я вижу, вы действительно его учитывали) и как он является правильным или неправильным. Но я думаю, что ответ будет улучшен, если вы включите некоторый код, демонстрирующий это.
Прифтан
11

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

В любой из этих схем кодирования с переменной шириной простые выложенные здесь алгоритмы ( злые , не злые или иные ) не будут работать правильно вообще! Фактически, они могут даже привести к тому, что строка станет неразборчивой или даже недопустимой строкой в ​​этой схеме кодирования. Посмотрите ответ Хуана Пабло Калифано для некоторых хороших примеров.

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

Тим Фарли
источник
6
std :: reverse НЕ принимает это во внимание. Это инвертирует value_type. В случае std :: string он меняет символ. Не персонажи.
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
pprzemek
источник
Я бы использовал, strlenчтобы найти конец строки, если он потенциально длинный. Хорошая реализация библиотеки будет использовать векторы SIMD для поиска быстрее, чем 1 байт за итерацию. Но для очень коротких строк while (*++end);это будет сделано до того, как вызов библиотечной функции начнет поиск.
Питер Кордес
@PeterCordes хорошо, согласен, strlen следует использовать в любом случае для удобства чтения. Для более длинной строки вы всегда должны держать длину в переменной в любом случае. strlen на SIMD обычно приводится в качестве примера с этим заявлением об ограничении ответственности, которое не является реальным приложением, или, по крайней мере, это было 5 лет назад, когда код был написан. ;)
pprzemek
1
Если вы хотите, чтобы это работало быстро на реальных процессорах, вы бы использовали SIMD-тасовки, чтобы сделать обратное в 16-битных блоках. : P Например, в x86, _mm_shuffle_epi8(PSHUFB) может изменить порядок вектора 16B, учитывая правильный вектор управления тасованием. Вероятно, он может работать почти на скорости memcpy с некоторой тщательной оптимизацией, особенно с 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 менее безопасен, так как не гарантирует, что полученная строка завершится нулем. В любом случае, нет ничего плохого в том, что uvote использует здесь strcpy.
Роберт Гэмбл
1
@Onorio Catenacci, strcpy не опасен, если вы знаете, что исходная строка будет помещаться в целевой буфер, как в случаях, приведенных в приведенном выше коде. Кроме того, strncpy заполняется нулями до числа символов, указанного в параметре размера, если есть оставшаяся комната, что может быть нежелательно.
Крис Янг
3
Любой, кто не может правильно использовать strcpy, не должен программировать на C.
Роберт Гэмбл,
2
@ Роберт Гэмбл, я согласен. Тем не менее, поскольку я не знаю способов удержать людей от программирования на C независимо от их компетенции, я обычно рекомендую против этого.
Онорио Катеначчи
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 ++, не требуя отдельного буфера для хранения перевернутой строки?» - не было необходимости менять «на месте». Также в этом решении с самого начала упоминается неправильное использование стека. Неужели за меня голосуют из-за плохого понимания прочитанного?
Саймон
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--);
}
спектральный
источник
1

Если вы используете ATL / MFC CString, просто позвонить CString::MakeReverse().

Майкл Хефрати
источник
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. Я верю, что этот ответ добавляет много, на самом деле. Вы должны быть в состоянии понять этот тип кода, прежде чем добавлять миллиард библиотек в качестве зависимостей, просто чтобы получить какое-то простое текстовое поле (что я вижу слишком часто в современных приложениях, с которыми мне приходится работать)
Стивен Дж.
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");
}
masakielastic
источник
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;
   };
ADEM
источник
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);
    }
}
Alok
источник
Правда, но это только печатает в обратном порядке, верно? (Я не использую 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");
}
Стивен Дж
источник
Я, кажется, единственный ответ, который не имеет буфера или временной переменной. Я использую длину строки, но другие, которые делают это, добавляют еще один для головы (против хвоста). Я предполагаю, что стандартная обратная функция хранит переменную через своп или что-то в этом роде. Таким образом, у меня сложилось впечатление, предполагая, что математика указателей и тип UTF совпадают, что, возможно, это единственный ответ, который действительно отвечает на вопрос. Добавленные printf () можно убрать, я просто сделал это, чтобы выглядеть лучше. Я написал это для скорости. Никаких дополнительных выделений или переменных. Вероятно, самый быстрый алгоритм для отображения обратного str ()
Стивен 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;
}
Hasenbeck
источник
Вызов 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 для обращения строки ... И это просто; просто используйте простой указатель, чтобы сделать работу ...

Нит кт
источник
Это печатает строку по одному символу за раз. Это не меняет его на месте. Это также ждет пользовательского ввода без причины.
Питер Кордес