C veya C ++ 'da bir dizeyi nasıl tersine çevirirsiniz?


Yanıtlar:


123

Standart algoritma, başlangıç ​​/ bitiş işaretçileri kullanmak ve ortada buluşuncaya veya geçene kadar onları içeriye doğru yürümektir. Kullandıkça değiştirin.


Ters ASCII dizesi, yani her karakterin 1'e sığdığı 0 sonlu bir dizi char. (Veya diğer çok baytlı olmayan karakter kümeleri).

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

Aynı algoritma, bilinen uzunluğa sahip tamsayı dizileri için çalışır, sadece tail = start + length - 1son bulma döngüsü yerine kullanın .

(Editörün notu: Bu cevap başlangıçta bu basit sürüm için XOR-takasını da kullandı. Bu popüler sorunun gelecekteki okuyucularının yararı için düzeltildi. XOR-takas şiddetle tavsiye edilmez ; okunması zor ve kodunuzu daha az verimli bir şekilde derlemenizi sağlar. görebileceğiniz Godbolt derleyici kaşif üzerinde xor-takas gcc -O3 ile x86-64 için derlenmiş asm döngü gövdesini komplike ne kadar daha fazladır.)


Tamam, hadi, UTF-8 karakterlerini düzeltelim ...

(Bu XOR-takas olayıdır. Benimle takas etmekten kaçınmanız gerektiğine dikkat edin , çünkü eğer *pve*q konumları aynı bir == 0. XOR-takas iki ayrı yerleri olan bağlı olan bir ^ ile sıfır olacak, her birini geçici depolama alanı olarak kullanarak.)

Editörün notu: SWP'yi bir tmp değişkeni kullanarak güvenli bir satır içi işleviyle değiştirebilirsiniz.

#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;
}
  • Neden, evet, giriş borked, bu neşeyle yer dışında takas olacak.
  • UNICODE'da vandalize ederken faydalı bağlantı: http://www.macchiato.com/unicode/chart/
  • Ayrıca, 0x10000 üzerinde UTF-8 test edilmemiştir (bunun için herhangi bir yazı tipine sahip olmadığım gibi bir hexeditor kullanma sabrınız da yok)

Örnekler:

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

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

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

./strrev verrts/.

162
XOR swap'ı gizlenmiş bir kod yarışmasının dışında kullanmak için iyi bir neden yoktur.
Chris Conway

28
"Yerinde" nin "fazladan bellek yok" anlamına geldiğini mi düşünüyorsunuz, geçici geçişler için O (1) bellek bile değil mi? Yığında str ve dönüş adresi için boşluk nedir?
Chris Conway

55
@Bill, “yerinde” nin ortak tanımının anlamı bu değildir. Yerinde algoritmaları olabilir ek bellek kullanır. Ancak, bu ek belleğin miktarı girişe bağlı olmamalıdır - yani sabit olmalıdır. Bu nedenle, ek depolama alanı kullanarak değerlerin değiştirilmesi tamamen yerinde.
Konrad Rudolph

19
Xor swap kaybolana kadar bunu onaylamamak.
Adam Rosenfield

34
XOR takas, modern sipariş dışı işlemcilerde kayıt yoluyla takas etmekten daha yavaştır.
Patrick Schlüter

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

Bu C ++ 'daki en basit yoldur.


6
C ++ 'da bir dize, dize sınıfı ile temsil edilir. "Char star" veya "char parantez" istemedi. Şık kalın, C.
jokoon

10
@fredsbend, seçilen cevabın "gülünç derecede uzun" versiyonu bu basit cevabın olmayan bir durumu ele alıyor - UTF-8 girişi. Sorunu tam olarak belirtmenin önemini gösterir. Ayrıca soru C ile de çalışacak kodla ilgiliydi.
Mark Ransom

5
UTF-8 farkında dize sınıfı (veya muhtemelen std :: basic_string ile bir utf-8 karakter sınıfı) kullanırsanız bu yanıt durumu ele alır. Ayrıca, soru "C ve C ++" değil "C veya C ++" dedi. Yalnızca C ++ "C veya C ++" dır.
Taywee

161

Kernighan ve Ritchie'yi okuyun

#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
Benim iphone test Bu yaklaşık% 15 ham işaretçi adresleri kullanmaktan daha yavaş
jjxtra

3
"C" değişkeni int yerine karakter olmamalı mı?
Lesswire

17
Bu örnekte dizenin sbir dizi biçiminde bildirilmesi gerektiğini belirtmek önemlidir . Başka bir deyişle, char s[] = "this is ok"yerine char *s="cannot do this"değiştirilemez bir dize sabiti içinde son sonuçlara çünkü
user1527227

3
"Vaftiz babası" için özür diler .... "Silahları bırakın, K & R getir". Bu sorun için daha düz ileri basit ve konum olarak C yobaz olarak ben, işaretçileri kullanmak istiyorum, ama kod C #, Java, vb daha az taşınabilir olacaktır

2
@Eric O (log (n)) zamanında çalışmaz. Kodun gerçekleştirdiği karakter değiş tokuş sayısına atıfta bulunuyorsanız, dize uzunluğu n için n takas yapılırsa O (n) ile çalışır. Gerçekleştirilen döngülerin miktarından bahsediyorsanız, o zaman O (n) - O (n / 2) olsa da, sabitleri Big O notasyonuna bırakırsınız.
Stephen Fox

62

Bir dizgiyi ters çevirin (görselleştirme):

Bir dizeyi yerinde ters çevirme


Güzel! Bunu oluşturmak için ne kullandınız? Cevabın buna bir bağlantı ekleyerek daha iyi olacağını düşünüyorum.
Pryftan

Bu algoritmanın uygulanması aslında bu cevabın içindedir .
karlphillip

Bunu biraz açıklayabilir misiniz, böylece resim bağlantısı ölürse cevap işe yaramaz mı?
SS Anne

41

Dizenin boş sonlandırılmış bir chardizi olduğu yaygın durum varsayılırsa kötü olmayan C :

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

Bitiş işaretçisini bulmak için while döngüsü kullanmak yerine end_ptr = str + strlen (str); Bunun neredeyse aynı şeyi yapacağını biliyorum, ama daha net buluyorum.
Peter Kühne

Yeterince adil. @ Uvote cevabında off-by-one hata önlemek için çalışıyordu (ve başarısız).
Chris Conway

Belki de potansiyel bir performans geliştirmesinin yanı sıra int temp, bu çözüm en iyi görünüyor. +1
chux - Monica'yı geri yükle

@ chux-ReinstateMonica Evet. Bu güzel. Şahsen ben () s !(*str)olsa da kaldırmak .
Pryftan

@ PeterKühne Evet ya da strchr()arıyor '\0'. İkisini de düşündüm. Döngü gerekmez.
Pryftan

35

std::reverseC ++ Standart Kitaplığı'ndan algoritma kullanın .


14
Standart Şablon Kütüphanesi standart öncesi bir terimdir. C ++ standardı bundan bahsetmez ve eski STL bileşenleri C ++ Standart Kütüphanesi'ndedir.
Nemanja Trifunovic

16
sağ. ben her zaman merak neden bu kadar kafa karıştırıcı hizmet olmasına rağmen neden hala "STL" diyoruz. daha fazla kişi sizin gibi olsaydı daha güzel olurdu ve sadece "C ++ Standart Kütüphane" veya STL deyin ve "STandard Kütüphanesi" deyin :)
Johannes Schaub - litb

27

Bir süre oldu ve hangi kitabın bana bu algoritmayı öğrettiğini hatırlamıyorum, ama bunun oldukça ustaca ve anlaşılması kolay olduğunu düşündüm:

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

İlginç bir varyasyon evet. Teknik olarak size_tolsa gerekir.
Pryftan

24

Kullanım std :: ters yönteminden STL :

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

"Algoritma" kütüphanesini eklemeniz gerekecek #include<algorithm>,.


1
Hangi kütüphanenin dahil edilmesi gerekiyor?
Don Cruickshank

#include <algoritma>
Yazmayı

Her ne kadar bu benim cevabım olsa da, merak ediyorum, çünkü OP "ters dizeyi tutmak için bir tampon olmadan" (temel olarak, yerinde takas) istedi, bunun nasıl yapıldığını nasıl kanıtlayabilirim? Sadece dahili olarak std :: iter_swap <> kullandığını, her iki uç noktadan arabellek orta noktasına kadar yineleyerek açıklıyor mu? Ayrıca, OP hem C hem de C ++ istedi, bu sadece C ++ içindir (sadece <string.h> 'in strrev () yöntemi olduğunu umabilir miyiz?)
HidekiAI

21

Std :: reverse'ın güzelliğinin, char *dizelerin ve std::wstrings'nin yanı sıra std::strings

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

1
Bir yandan kusmak istiyorum çünkü C ++ kullandınız, ancak diğer yandan, birinin *tipine göre koymayan ve adıyla koyan C ++ kullandığını görmek güzel, güzel, kesinlikle güzel bir şey . Olağanüstü. Ben saf bir şey olduğumu itiraf ediyorum ama her kod gördüğümde küfrediyorum char* str;.
Pryftan

11

NULL sonlandırılmış arabellekleri tersine çevirmek istiyorsanız, burada yayınlanan çözümlerin çoğu TAMAM. Ancak, Tim Farley'in daha önce işaret ettiği gibi, bu algoritmalar sadece bir dizenin anlamsal olarak bir bayt dizisi (yani tek bayt dizeler) olduğunu varsaymak için geçerliyse işe yarayacaktır, bence yanlış.

Örneğin, "año" dizesini (İspanyolca yıl) ele alalım.

Unicode kod noktaları 0x61, 0xf1, 0x6f'dir.

En çok kullanılan kodlamalardan bazılarını düşünün:

Latin1 / iso-8859-1 (tek bayt kodlama, 1 karakter 1 bayt ve tam tersi):

Orijinal:

0x61, 0xf1, 0x6f, 0x00

Tersine çevirmek:

0x6f, 0xf1, 0x61, 0x00

Sonuç iyi

UTF-8:

Orijinal:

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

Tersine çevirmek:

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

Sonuç anlamsız ve geçersiz bir UTF-8 dizisidir

UTF-16 Büyük Endian:

Orijinal:

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

İlk bayt, bir NUL sonlandırıcı olarak kabul edilecektir. Tersine çevirme gerçekleşmez.

UTF-16 Küçük Endian:

Orijinal:

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

İkinci bayt, bir NUL sonlandırıcı olarak işlem görecektir. Sonuç, 'a' karakterini içeren bir dize olan 0x61, 0x00 olur.


std :: reverse, wstring kullandığınız sürece iki baytlık unicode tipleri için çalışır.
Eclipse

C ++ ile çok aşina değilim, ama benim tahminim, dizelerle ilgilenen saygın herhangi bir standart kütüphane işlevinin farklı kodlamaları işleyebileceği, bu yüzden size katılıyorum. "Bu algoritmalar" ile, burada yayınlanan geçici ters işlevleri kastediyorum.
Juan Pablo Califano

Ne yazık ki, standart C ++ 'da "dizelerle ilgilenen saygın işlev" diye bir şey yoktur.
Jem

@Eclipse Bir vekil çifti tersine çevirirse, sonuç artık doğru olmayacaktır. Unicode aslında sabit genişlikli bir karakter
takımı

Bu cevapla ilgili sevdiğim şey, baytların gerçek sırasını göstermesidir (endianness bir şey olsa da - ah, gerçekten bunu düşündüğünüzü görüyorum) ve nasıl doğru veya yanlış olduğunu. Ama bunu gösteren bir kod eklerseniz cevap geliştirileceğini düşünüyorum.
Pryftan

11

Bütünlük açısından, karakter başına bayt sayısının karaktere bağlı olarak değiştiği çeşitli platformlarda dizelerin temsili olduğuna dikkat edilmelidir . Eski okul programcıları buna DBCS (Çift Bayt Karakter Kümesi) denir . Modern programcılar bununla birlikte UTF-8'de ( UTF-16 ve diğerleri) daha sık karşılaşırlar . Bunun gibi başka kodlamalar da vardır.

Bu değişken genişlik kodlama şemalarının herhangi birinde, burada yayınlanan basit algoritmalar ( kötü , kötü olmayan veya başka şekilde ) doğru şekilde çalışmaz! Aslında, dizenin okunaksız olmasına ve hatta kodlama şemasında yasadışı bir dizeye bile neden olabilirler. Bazı iyi örnekler için Juan Pablo Califano'nun cevabına bakınız .

std :: reverse (), platformunuzun Standart C ++ Kitaplığı'nı (özellikle dize yineleyicileri) düzgün bir şekilde hesaba kattığı sürece bu durumda potansiyel olarak hala çalışır.


6
std :: reverse bunu dikkate almaz. Değer_türü tersine çevirir. Std :: string durumunda char'ları tersine çevirir. Karakter değil.
MSalters

Daha ziyade eski okul programcılarının DBCS'yi bildiğimizi, ancak UTF-8'i de bildiğimizi söyleyin: çünkü hepimiz programcıların 'bir satır daha ve bırakacağım' derken bağımlılar gibi olduklarını biliyoruz. Eminim bazı programcılar sonunda bıraktı ama açıkçası programlama gerçekten benim için bir bağımlılık gibi; Programlamamaktan para çekiyorum. Buraya eklediğiniz iyi bir nokta. C ++ sevmiyorum (hatta biraz yazmayı sevmek gerçekten çok çalıştım ama yine de benim için estetik açıdan en azını söylemek zordur) bu yüzden orada yorum yapamam ama yine de iyi bir noktaya değindin +1.
Pryftan

5

Başka bir C ++ yolu (muhtemelen daha anlamlı ve daha hızlı olarak std :: reverse () kendimi :) kullanmak istiyorsunuz)

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

C yolu (az ya da çok :)) ve lütfen, XOR takas için hile konusunda dikkatli olun, derleyiciler bazen bunu optimize edemez.

Bu durumda genellikle çok daha yavaştır.

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

strlenEğer potansiyel olarak uzunsa, ipin sonunu bulmak için kullanırdım . İyi bir kütüphane uygulaması, yineleme başına 1 bayttan daha hızlı arama yapmak için SIMD vektörlerini kullanır. Ancak çok kısa dizeler için, while (*++end);bir kütüphane fonksiyon çağrısı aramaya başlamadan önce yapılacaktır.
Peter Cordes

@PeterCordes iyi anlaşılmış, okunabilirlik için yine de strlen kullanılmalıdır. Daha uzun dize için uzunluğu her zaman bir varialbe içinde tutmalısınız. SIMD'de strlen genellikle bu feragatname ile bir örnek olarak verilir, bu gerçek hayat uygulaması değildir veya en azından 5 yıl önce, kodun yazıldığı zamandır. ;)
pprzemek

1
Bunun gerçek CPU'larda hızlı çalışmasını istiyorsanız, 16B yığınlarında tersini yapmak için SIMD karmalarını kullanırsınız. : P, örneğin _mm_shuffle_epi8x86'da , (PSHUFB), sağ karıştırma kontrol vektörü verildiğinde bir 16B vektörünün sırasını tersine çevirebilir. Özellikle AVX2 ile bazı dikkatli optimizasyonlarla muhtemelen neredeyse memcpy hızında çalışabilir.
Peter Cordes

char* beg = s-1tanımlanmamış bir davranışı vardır (en azından sbir dizinin en yaygın örneği olan ilk öğesini işaret ediyorsa ). boş bir dize while (*++end);ise tanımsız davranışa sahiptir s.
melpomene

@pprzemek Pekala, bir dizinin ilk öğesine işaret s-1etse bile, davranışı tanımlayan bir iddiada sbulunuyorsunuz, bu yüzden standardı destekleyecek olan sizsiniz.
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;
}

Bu kod bu çıktıyı üretir:

gnitset
a
cba

2
@uvote, strcpy kullanmayın. Hiç. Strcpy gibi bir şey kullanmanız gerekiyorsa strncpy kullanın. strcpy tehlikelidir. Bu arada C ve C ++ ayrı olanaklara sahip iki ayrı dildir. Sadece C ++ 'da bulunan başlık dosyalarını kullandığınızı düşünüyorum, bu yüzden gerçekten C'de bir cevaba ihtiyacınız var mı?
Onorio Catenacci

6
Programcı dizilerinin boyutunu takip edebiliyorsa strcpy mükemmel bir şekilde güvenlidir, birçoğu strncpy'ın sonuçta elde edilen dizgenin boş bırakıldığını garanti etmediği için daha az güvenli olduğunu iddia eder. Her durumda, uvote'un burada strcpy kullanımıyla ilgili yanlış bir şey yoktur.
Robert Gamble

1
@Onorio Catenacci, yukarıdaki kodda verilen durumlarda olduğu gibi, kaynak dizenin hedef arabelleğe sığacağını biliyorsanız, strcpy tehlikeli değildir. Ayrıca strncpy, boş bırakılan bir oda varsa, boyut parametresinde belirtilen karakter sayısına kadar sıfır doldurur ve bu istenmeyebilir.
Chris Young

3
Strcpy'yi düzgün kullanamayan herkes C'de programlama yapmamalıdır
Robert Gamble

2
@Robert Gamble, katılıyorum. Bununla birlikte, yeterlilikleri ne olursa olsun, insanların C'de programlamalarını engellemenin bir yolunu bilmediğim için, genellikle buna karşı öneriyorum.
Onorio Catenacci


3

Evgeny'nin K&R cevabını seviyorum. Ancak, işaretçiler kullanarak bir sürüm görmek güzel. Aksi takdirde, aslında aynıdı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

Bir dizgiyi yerinde tersine çevirmek için özyinelemeli fonksiyon (ekstra tampon yok, malloc).

Kısa, seksi kod. Kötü, kötü yığın kullanımı.

#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
Bu oldukça eğlenceli bir çözüm, printf ("% * s> [% d] reverse_r ('% c',% p = \"% s \ ",% p = \"% s \ ") gibi bazı izlemeler eklemelisiniz. \ n ", derinlik," ", derinlik, val, s, (s? s:" null "), n, (n? n:" null ")); başında ve <sonunda.
Benoît

Her birini charyığının üzerine itmek "yerinde" olarak sayılmaz. Özellikle karakter başına 4 * 8B bastığınızda değil (64 bit makinede: 3 args + bir dönüş adresi).
Peter Cordes

Orijinal soru, "Ters dizeyi tutmak için ayrı bir arabellek gerekmeden C veya C ++ 'da bir dizeyi nasıl ters çevirirsiniz?" - 'yerinde' takas gerekmedi. Ayrıca, bu çözüm en başından itibaren yığın istifinden bahsetmektedir. Başkalarının zayıf okuduğunu anlama nedeniyle oy kullanılıyor muyum?
Simon Peverett

1
Ben buna 'seksi' demezdim ama gösterişli ve öğretici olduğunu söyleyebilirim. Özyineleme düzgün bir şekilde kullanılırsa çok değerli olabilir. Ancak şunu belirtmek gerekir ki - son bildiğim - C'nin bile bir yığın gerektirmediği; ancak özyineleme yapar. Her iki durumda da, düzgün kullanıldığında çok yararlı ve değerli olabilecek bir özyineleme örneğidir. Ben hiç bir dize tersine olsa kullanılan gördüm sanmıyorum.
Pryftan


1

Kodumu paylaş. Bir C ++ öğrenicisi olarak, swap () kullanmak için bir seçenek olarak, alçakgönüllü yorum istiyorum.

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

Yine bir başka:

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

}

Bu, cevabım gibi işaretçi aritmetiğini gösterir, ancak Swap ile birleştirir. Aslında bu cevabın çok şey kattığına inanıyorum. Sadece basit bir metin kutusu almak için bağımlılık olarak bir milyar kitaplık eklemeden önce bu tür bir kodu anlayabilmelisiniz (modern uygulamalarda çok sık gördüğüm bir şey)
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 ++ lambda ile:

 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

Cevabım çoğuna benzer, ancak lütfen kodumu burada bulabilirsiniz.

//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

Dize tersine çevirmek için başka bir yol olduğunu düşünüyorum. girdiyi kullanıcıdan alır ve tersine çevirir.

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

Doğru ama bu sadece ters sırayı yazdırıyor, değil mi? (C ++ kullanmayın belki .put () ne düşündüğümü yapmaz).
Pryftan

-1

Depolamanıza gerek yoksa, bu şekilde harcanan zamanı azaltabilirsiniz:

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

Ben bir tampon veya geçici değişken olmayan tek cevap gibi görünüyor. Dize uzunluğunu kullanıyorum, ancak bunu yapan diğerleri kafa için başka bir tane ekler (kuyruk). Standart ters fonksiyonun bir değiş tokuş ya da bir şey yoluyla değişkeni sakladığını varsayıyorum. Böylece, işaretçi matematik ve UTF türü sıralaması varsayarak, bu muhtemelen soruyu cevaplayan tek cevap bu izlenim altındayım. Eklenen printf () çıkarılabilir, sadece sonuç için daha güzel görünmek için yaptım. Bunu hız için yazdım. Hiçbir ekstra tahsis veya vars. Muhtemelen en hızlı algoritmayı görüntülemek için en hızlı algoritma str ()
Stephen J

-3

İşte benim C ile benim almak. Bunu pratik için yaptı ve mümkün olduğunca özlü olmaya çalıştı! Komut satırı üzerinden bir dize girersiniz, yani ./program_name "buraya dizeyi girin"

#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
Bu okunamayan bir karmaşa, ancak görünüşe göre mümkün olan en kısa kodu ( kod golf ) için gitmiyorsunuz, çünkü bazı değişken isimleri birden fazla karakterdir.
Peter Cordes

-4

Ama XOR takas algoritmasının en iyisi olduğunu düşünüyorum ...

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()Döngü koşulunda ve döngü gövdesinde yapılan bir çağrı optimize edilemeyebilir.
Peter Cordes

-5

İşte bir dize C ++ (bence) tersine çevirmek için en temiz, en güvenli, en kolay yolu:

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

}

Bir alternatif kullanmaktır std::swap, ancak kendi işlevlerimi tanımlamayı seviyorum - ilginç bir egzersiz ve includeekstra bir şeye ihtiyacınız yok .


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

Bu, bir dize tersine çevirmek için C dilinde optimize edilmiş koddur ... Ve basit; işi yapmak için basit bir işaretçi kullanın ...


Bu, dizeyi her seferinde bir karakter yazdırır. Yerinde tersine çevirmez. Ayrıca kullanıcı girişini de sebepsiz bekler.
Peter Cordes
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.