Een string omkeren in C

Ik heb een reverse-string programma ontwikkeld. Ik vraag me af of er een betere manier is om dit te doen, en of mijn code mogelijke problemen heeft. Ik wil wat geavanceerde functies van C oefenen.

char* reverse_string(char *str)
{
    char temp;
    size_t len = strlen(str) - 1;
    size_t i;
    size_t k = len;
    for(i = 0; i < len; i++)
    {
        temp = str[k];
        str[k] = str[i];
        str[i] = temp;
        k--;
        /* As 2 characters are changing place for each cycle of the loop
           only traverse half the array of characters */
        if(k == (len / 2))
        {
            break;
        }
    }
}

Antwoord 1, autoriteit 100%

Als je geavanceerde functies van C wilt oefenen, wat dacht je van pointers?
We kunnen ook macro’s toevoegen en xor-swappen voor de lol!

#include <string.h> // for strlen()
// reverse the given null-terminated string in place
void inplace_reverse(char * str)
{
  if (str)
  {
    char * end = str + strlen(str) - 1;
    // swap the values in the two given variables
    // XXX: fails when a and b refer to same memory location
#   define XOR_SWAP(a,b) do\
    {\
      a ^= b;\
      b ^= a;\
      a ^= b;\
    } while (0)
    // walk inwards from both ends of the string, 
    // swapping until we get to the middle
    while (str < end)
    {
      XOR_SWAP(*str, *end);
      str++;
      end--;
    }
#   undef XOR_SWAP
  }
}

Een pointer(bijv. char *, gelezen van rechts naar links als een pointer naar een char) is een gegevenstype in C dat wordt gebruikt
om te verwijzen naar de locatie in het geheugen van een andere waarde. In dit geval,
de locatie waar een charis opgeslagen. We kunnen defereren
pointers door ze vooraf te laten gaan door een *, wat ons de waarde geeft
op die locatie opgeslagen. Dus de waarde die is opgeslagen bij stris *str.

We kunnen eenvoudig rekenen met aanwijzers. Wanneer we verhogen (of verlagen)
een aanwijzer, we verplaatsen deze eenvoudig om naar de volgende (of vorige)
geheugenlocatie voor dat type waarde. Aanwijzers van verhogen
verschillende typen kunnen de aanwijzer met een ander aantal . verplaatsen
bytes omdat verschillende waarden verschillende bytegroottes hebben in C.

Hier gebruiken we één aanwijzer om naar de eerste onverwerkte te verwijzen
charvan de tekenreeks (str) en een andere om naar de laatste te verwijzen (end).
We wisselen hun waarden (*stren *end) en verplaatsen de aanwijzers
naar binnen naar het midden van de snaar. Zodra str >= end, ofwel
ze verwijzen allebei naar hetzelfde char, wat betekent dat onze originele string een . had
oneven lengte (en het middelste charhoeft niet omgekeerd te worden), of
we hebben alles verwerkt.

Voor het omwisselen heb ik een macrogedefinieerd. Macro’s zijn tekstvervanging
gedaan door de C-preprocessor. Ze zijn heel verschillend van functies,
en het is belangrijk om het verschil te weten. Als je een functie aanroept,
de functie werkt op een kopie van de waarden die u eraan geeft. Wanneer je belt
een macro, het doet gewoon een tekstuele vervanging – dus de argumenten die je geeft
het wordt direct gebruikt.

Aangezien ik de macro XOR_SWAPmaar één keer heb gebruikt, was het waarschijnlijk te veel werk om deze te definiëren,
maar het maakte meer duidelijk wat ik aan het doen was. Nadat de C-preprocessor de macro heeft uitgebreid,
de while-lus ziet er als volgt uit:

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

Houd er rekening mee dat de macro-argumenten één keer verschijnen voor elke keer dat ze worden gebruikt in de
macro definitie. Dit kan erg handig zijn, maar kan ook je code breken
indien verkeerd gebruikt. Als ik bijvoorbeeld de toename/afname had gecomprimeerd
instructies en de macro-aanroep in een enkele regel, zoals

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

Dan zou dit uitbreiden naar

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

Die heeft drievoudigebewerkingen voor het verhogen/verlagen, en eigenlijk niet
doe de ruil die het hoort te doen.

Terwijl we het toch over het onderwerp hebben, zou je moeten weten wat xor(^) betekent. Het is een basis
rekenkundige bewerking – zoals optellen, aftrekken, vermenigvuldigen, delen, behalve
het wordt meestal niet onderwezen op de basisschool. Het combineert bit voor bit twee gehele getallen
– graag optellen, maar we geven niet om de overdrachten. 1^1 = 0, 1^0 = 1,
0^1 = 1, 0^0 = 0.

Een bekende truc is om xor te gebruiken om twee waarden om te wisselen. Dit werkt vanwege drie basis
eigenschappen van xor: x ^ 0 = x, x ^ x = 0en x ^ y = y ^ xvoor alle waarden xen y. Dus zeg dat we er twee hebben
variabelen aen bdie aanvankelijk twee waarden opslaan
vaen vb.

// aanvankelijk:
 // a == va
 // b == vb
 een ^= b;
 // nu: a == va^ vb
 b^= een;
 // nu: b == vb^ (va^ vb)
 // == va^ (vb^ vb)
 // == va^ 0
 // == va
 een ^= b;
 // nu: a == (va^ vb) ^ va
 // == (va^ va) ^ vb
 // == 0 ^ vb
 // == vb

Dus de waarden zijn verwisseld. Dit heeft één bug – wanneer aen bdezelfde variabele zijn:

// aanvankelijk:
 // a == va
 een ^= een;
 // nu: a == va^ va
 // == 0
 een ^= een;
 // nu: een == 0 ^ 0
 // == 0
 een ^= een;
 // nu: een == 0 ^ 0
 // == 0

Aangezien we str < end, dit gebeurt nooit in de bovenstaande code, dus we zijn in orde.

Hoewel we ons zorgen maken over de juistheid, moeten we onze randgevallen controleren. De regel if (str)moet ervoor zorgen dat we geen NULL-aanwijzer voor string hebben gekregen. Hoe zit het met de lege string ""? Nou strlen("") == 0, dus we initialiseren endals str - 1, wat betekent dat de while (str < end)voorwaarde is nooit waar, dus we doen niets. Wat juist is.

Er is een heleboel C om te verkennen. Veel plezier ermee!

Update:mmwbrengt een goed punt naar voren, namelijk dat je een beetje voorzichtig hoe je dit aanroept, aangezien het ter plekke werkt.

char stack_string[] = "This string is copied onto the stack.";
 inplace_reverse(stack_string);

Dit werkt prima, aangezien stack_stringeen array is waarvan de inhoud wordt geïnitialiseerd op de gegeven stringconstante. Maar

char * string_literal = "This string is part of the executable.";
 inplace_reverse(string_literal);

Zorgt ervoor dat je code vlamt en sterft tijdens runtime. Dat komt omdat string_literalalleen verwijst naar de string die is opgeslagen als onderdeel van je uitvoerbare bestand – wat normaal gesproken geheugen is dat je niet mag bewerken door het besturingssysteem. In een gelukkiger wereld zou je compiler dit weten, en een foutmelding geven wanneer je probeerde te compileren, die je vertelt dat string_literalvan het type char const *moet zijn omdat je dat kunt wijzig de inhoud niet. Dit is echter niet de wereld waarin mijn compiler leeft.

Er zijn enkele hacks die je zou kunnen proberen om ervoor te zorgen dat er wat geheugen op de stapel of in de heap staat (en dus bewerkbaar is), maar ze zijn niet per se draagbaar, en het kan behoorlijk lelijk zijn. Ik leg de verantwoordelijkheid hiervoor echter graag bij de functie-aanroeper. Ik heb ze verteld dat deze functie geheugenmanipulatie uitvoert, het is hun verantwoordelijkheid om me een argument te geven dat dat toelaat.


Antwoord 2, autoriteit 33%

Gewoon een herschikking en veiligheidscontrole. Ik heb ook je niet-gebruikte retourtype verwijderd. Ik denk dat dit een veilige en schone zaak is:

#include <stdio.h>
#include <string.h>
void reverse_string(char *str)
{
    /* skip null */
    if (str == 0)
    {
        return;
    }
    /* skip empty string */
    if (*str == 0)
    {
        return;
    }
    /* get range */
    char *start = str;
    char *end = start + strlen(str) - 1; /* -1 for \0 */
    char temp;
    /* reverse */
    while (end > start)
    {
        /* swap */
        temp = *start;
        *start = *end;
        *end = temp;
        /* move */
        ++start;
        --end;
    }
}
int main(void)
{
    char s1[] = "Reverse me!";
    char s2[] = "abc";
    char s3[] = "ab";
    char s4[] = "a";
    char s5[] = "";
    reverse_string(0);
    reverse_string(s1);
    reverse_string(s2);
    reverse_string(s3);
    reverse_string(s4);
    reverse_string(s5);
    printf("%s\n", s1);
    printf("%s\n", s2);
    printf("%s\n", s3);
    printf("%s\n", s4);
    printf("%s\n", s5);
    return 0;
}

Bewerkt zodat het einde niet verwijst naar een mogelijk slechte geheugenlocatie wanneer strlen 0 is.


Antwoord 3, autoriteit 25%

U kunt uw (len/2)-test in de for-lus plaatsen:

for(i = 0,k=len-1 ; i < (len/2); i++,k--)
{
        temp = str[k];
        str[k] = str[i];
        str[i] = temp;
}

Antwoord 4, autoriteit 19%

Dit complete programma laat zien hoe ik het zou doen. Houd in gedachten dat ik C aan het schrijven was toen de meeste van jullie whippersnappers een glinstering in de ogen van je moeder waren, dus het is ouderwets, doe-het-werk, lange-var-namen-zijn-voor-watjes. Corrigeer dat als je wilt, ik ben meer geïnteresseerd in de juistheid van de code.

Het verwerkt NULL’s, lege tekenreeksen en alle tekenreeksen. Ik heb het niet getest met strings van maximale grootte (max(size_t)) maar het zou moeten werken, en als je zo grote strings hanteert, ben je sowieso gek 🙂

#include <stdio.h>
#include <string.h>
char *revStr (char *str) {
    char tmp, *src, *dst;
    size_t len;
    if (str != NULL)
    {
        len = strlen (str);
        if (len > 1) {
            src = str;
            dst = src + len - 1;
            while (src < dst) {
                tmp = *src;
                *src++ = *dst;
                *dst-- = tmp;
            }
        }
    }
    return str;
}
char *str[] = {"", "a", "ab", "abc", "abcd", "abcde"};
int main(int argc, char *argv[]) {
    int i;
    char s[10000];
    for (i=0; i < sizeof(str)/sizeof(str[0]); i++) {
        strcpy (s, str[i]);
        printf ("'%s' -> '%s'\n", str[i], revStr(s));
    }
    return 0;
}

De output daarvan is:

'' -> ''
'a' -> 'a'
'ab' -> 'ba'
'abc' -> 'cba'
'abcd' -> 'dcba'
'abcde' -> 'edcba'

Antwoord 5, autoriteit 10%

Probeer dit:

reverse_string(NULL);
reverse_string("");

Antwoord 6, autoriteit 8%

Je zou je for loop-declaratie kunnen wijzigen om de code korter te maken:

char* reverse_string(char *str)
{
    char temp;
    size_t len = strlen(str) - 1;
    size_t stop = len/2;
    size_t i,k;
    for(i = 0, k = len; i < stop; i++, k--)
    {
        temp = str[k];
        str[k] = str[i];
        str[i] = temp;
    }
    return str;
}

Antwoord 7, autoriteit 8%

Gebruikt niemand meer pointers?

void inplace_rev( char * s ) {
  char t, *e = s + strlen(s);
  while ( --e > s ) { t = *s;*s++=*e;*e=t; }
}

EDIT: Sorry, zag zojuist het bovenstaande XOR-voorbeeld…


Antwoord 8, autoriteit 6%

Ik zie geen return-instructie en u wijzigt de invoerreeks, wat een probleem kan zijn voor de programmeur. Misschien wilt u dat de invoerreeks onveranderlijk is.

Dit kan ook kieskeurig zijn, maar len/2 moet maar één keer worden berekend, IMO.

Anders dan dat, zal het werken, zolang je de probleemgevallen oplost die door rossfabricant worden genoemd.


Antwoord 9, autoriteit 6%

void reverse(char *s)
{
  char *end,temp;
  end = s;
  while(*end != '\0'){
    end++;
  }
  end--;  //end points to last letter now
  for(;s<end;s++,end--){
    temp = *end;
    *end = *s;
    *s = temp; 
  }
}

Antwoord 10, autoriteit 3%

rev {
int len = strlen(str)-1;
for ( int i =0; i< len/2 ; i++ ) {
        char t = str[i];
        str[i] = str[len-i];
        str[len-i] = t;
        }
}

Antwoord 11, autoriteit 3%

/* Author: Siken Dongol */
#include <stdio.h>
int strLength(char *input) {
    int i = 0;
    while(input[i++]!='\0');
    return --i;
}
int main()
{
    char input[] = "Siken Man Singh Dongol";
    int len = strLength(input);
    char output[len];
    int index = 0;
    while(len >= 0) {
        output[index++] = input[--len];
    }
    printf("%s\n",input);
    printf("%s\n",output);
    return 0;
}

Antwoord 12, autoriteit 3%

De code ziet er onnodig ingewikkeld uit. Hier is mijn versie:

void strrev(char* str) { 
    size_t len = strlen(str);
    char buf[len]; 
    for (size_t i = 0; i < len; i++) { 
        buf[i] = str[len - 1 - i]; 
    }; 
    for (size_t i = 0; i < len; i++) { 
        str[i] = buf[i]; 
    }
}

Antwoord 13, autoriteit 3%

Hier is mijn kans. Ik vermijd ruilen door het standaard strcpypatroon te gebruiken:

char *string_reverse(char *dst, const char *src)
{
    if (src == NULL) return NULL;
    const char *src_start = src;
    char *dst_end = dst + strlen(src);
    *dst_end = '\0';
    while ((*--dst_end = *src_start++)) { ; }
    return dst;
}

en hier een doorlopend voorbeeld.


Antwoord 14, autoriteit 3%

bool reverse_string(char* str) {
    if(str == NULL){
        return false;
    }
    if(strlen(str) < 2){
        return false;
    }
    char* first = str;
    char* last = str + strlen(str) - 1; // Minus 1 accounts for Index offset
    char temp;
    do{
        temp = *first;
        *first = *last;
        *last = temp;
    }
    while (++first < --last); // Update Pointer Addresses and check for equality
    return true;
}

Deze oplossing is gebaseerd op de post van GManNickG met een paar aanpassingen. De eerste logische instructie kan gevaarlijk zijn als !str niet wordt geëvalueerd vóór de strlen-bewerking (voor een NULL ptr). Dit was niet het geval met mijn compiler. Ik dacht dat ik deze code zou toevoegen omdat het een mooi voorbeeld is van een do-while-lus.


Antwoord 15

Aangezien je zegt dat je zin hebt, wil je misschien je karakters uitwisselen met een XOR ruilen.


Antwoord 16

In plaats van halverwege te breken, moet je gewoon je lus inkorten.

size_t length = strlen(str);
size_t i;
for (i = 0; i < (length / 2); i++)
{
    char temp = str[length - i - 1];
    str[length - i - 1] = str[i];
    str[i] = temp;
}

Antwoord 17

#include <stdio.h>
#include <string.h>
int main() 
{
    char *data = "hello world";
    int length=strlen(data);
    char bytes[length];
    int n=0;
    while(n<=length)
    {
       bytes[n] = data[length-n-1];
       n++;
    }
    printf("%s\n", bytes);
    return 0;   
}

Antwoord 18

#include <stdio.h>
int main()    
{
    char string[100];
    int i;
    printf("Enter a string:\n");
    gets(string);
    printf("\n");
    for(i=strlen(string)-1;i>-1;i--)
    printf("%c",string[i]);
}

Antwoord 19

Here is my shot which will handle all the cases 
char *p ="KDLAKDADKADAD"
char p[] = "lammdlamldaldladadada"
also empty string 
#include<stdio.h>
#include<string.h>enter code here
#include<stdlib.h>
char *string_reverse(char *p);
int main()
{
        char *p = " Deepak@klkaldkaldkakdoroorerr";
        char *temp = string_reverse(p);
        printf("%s", temp);
}
char *  string_reverse( char *p )
{
        if(*p == '\0')
        {
                printf("No charecters are present \n");
                return 0;
        }
        int count = strlen(p)+1;
        int mid = strlen(p)/2;
        char *q  = (char *)malloc(count * sizeof(char));
        if( q )
        {
                strcpy(q,p);
                char *begin,*end,temp;
                begin = q ;
                end = q+strlen(p)-1  ;
                int i = 0;
                while( i < mid/2 )
                {
                        temp = *end;
                        *end = *begin;
                        *begin = temp;
                        begin++;
                        end--;
                        i++;
                }
                return q;
        }
        else
        {
                printf("Memory Not allocated ");
        }
        free(q);
}

Antwoord 20

Eenvoudige en eenvoudige code XD

void strrev (char s[]) {
int i;
int dim = strlen (s);
char l;
for (i = 0; i < dim / 2; i++) {
    l = s[i];
    s[i] = s[dim-i-1];
    s[dim-i-1] = l;
    }
}

Antwoord 21

U kunt deze aanwijzer rekenkunde proberen:

void revString(char *s)
{
  char *e = s; while(*e){ e++; } e--;
  while(e > s){ *s ^= *e; *e ^= *s; *s++ ^= *e--; }
}

Antwoord 22

Mijn twee cent:

/* Reverses n characters of a string and adds a '\0' at the end */
void strnrev (char *txt, size_t len) {
    size_t idx;
    for (idx = len >> 1; idx > 0; idx--) {
        txt[len] = txt[idx - 1];
        txt[idx - 1] = txt[len - idx];
        txt[len - idx] = txt[len];
    }
    txt[len] = '\0';
}
/* Reverses a null-terminated string */
void strrev (char *txt) {
    size_t len = 0;
    while (txt[len++]);
    strnrev(txt, --len);
}

Test # 1 strrev():

char string[] = "Hello world!";
strrev(string);
printf("%s\n", string); // Displays "!dlrow olleH"

Test # 2 strnrev():

char string[] = "Hello world!";
strnrev(string, 5);
printf("%s\n", string); // Displays "olleH"

Antwoord 23

Je kunt eigenlijk zoiets als dit doen:

#include <string.h>
void reverse(char *);
int main(void){
 char name[7] = "walter";
 reverse(name);
 printf("%s", name);
}
void reverse(char *s) {
  size_t len = strlen(s);
  char *a = s;
  char *b = &s[(int)len - 1];
  char tmp;
  for (; a < b; ++a, --b) {
    tmp = *a;
    *a = *b;
    *b = tmp;
  }
}

Antwoord 24

Een klein programma gemaakt dat dat voor elkaar krijgt:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
    char str[8192] = "string"; // string
    size_t len = strlen(str)-1; // get string length and reduce 1
    while(len+1 > 0) // Loop for every character on string
    {
        printf("%c",str[len--]); // Print string reversed and reducing len by one
    }
    return 0; // Quit program
}

Uitleg:
We nemen de lengte van de string, en dan beginnen we te lussen bij de laatste positie totdat we aankomen bij index 0 stop programma.


Antwoord 25

U kunt gemakkelijk doen met behulp van de aanwijzer in een paar stappen:

  1. wijs eerst een pointer naar de laatste char van de string

  2. lees de inhoud waarnaar wordt verwezen door de aanwijzer in omgekeerde volgorde

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

        int main()
         {
             char str[] = "This is an example";
             char *p = str + strlen(str); /* point to the end of the string */
             p--; /* points to the last char of the string */
             for (int i = 0; i < strlen(str); i++, p--)
             {
                printf("%c", *p);
             }
             return 0;
         }
    

Antwoord 26

Dat is een goede vraag ant2009 . U kunt een stand-alone functie gebruiken om de tekenreeks om te keren. De code is …

#include <stdio.h>
#define MAX_CHARACTERS 99
int main( void );
int strlen( char __str );
int main() {
    char *str[ MAX_CHARACTERS ];
    char *new_string[ MAX_CHARACTERS ];
    int i, j;
    printf( "enter string: " );
    gets( *str );
    for( i = 0; j = ( strlen( *str ) - 1 ); i < strlen( *str ), j > -1; i++, j-- ) {
        *str[ i ] = *new_string[ j ];
    }
    printf( "Reverse string is: %s", *new_string" );
    return ( 0 );
}
int strlen( char __str[] ) {
    int count;
    for( int i = 0; __str[ i ] != '\0'; i++ ) {
         ++count;
    }
    return ( count );
}

Other episodes