web-dev-qa-db-fra.com

Inverser une chaîne en C

J'ai développé un programme de chaîne inversée. Je me demande s'il y a une meilleure façon de le faire et si mon code a des problèmes potentiels. Je cherche à pratiquer certaines fonctionnalités avancées de C.

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

Si vous voulez pratiquer les fonctionnalités avancées de C, que diriez-vous des pointeurs? Nous pouvons également lancer des macros et xor-swap pour le plaisir!

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

Un pointeur (par exemple char *, Lu de droite à gauche comme un pointeur - vers un char) est un type de données en C qui est utilisé pour faire référence à l'emplacement en mémoire d'une autre valeur. Dans ce cas, l'emplacement où un char est stocké. Nous pouvons dereference pointeurs en les préfixant avec un *, Qui nous donne la valeur stockée à cet emplacement. La valeur stockée dans str est donc *str.

Nous pouvons faire de l'arithmétique simple avec des pointeurs. Lorsque nous incrémentons (ou décrémentons) un pointeur, nous le déplaçons simplement pour faire référence à l'emplacement de mémoire suivant (ou précédent) pour ce type de valeur. L'incrémentation de pointeurs de différents types peut déplacer le pointeur d'un nombre différent d'octets car différentes valeurs ont des tailles d'octets différentes en C.

Ici, nous utilisons un pointeur pour faire référence au premier char non traité de la chaîne (str) et un autre pour faire référence à la dernière (end). Nous échangeons leurs valeurs (*str Et *end), Et déplaçons les pointeurs vers l'intérieur au milieu de la chaîne. Une fois str >= end, Soit ils pointent tous les deux vers le même char, ce qui signifie que notre chaîne d'origine avait une longueur impaire (et que le milieu char n'a pas besoin d'être inversé), ou nous avons tout traité.

Pour faire l'échange, j'ai défini une macro . Les macros sont des substitutions de texte effectuées par le préprocesseur C. Ils sont très différents des fonctions et il est important de connaître la différence. Lorsque vous appelez une fonction, la fonction fonctionne sur une copie des valeurs que vous lui donnez. Lorsque vous appelez une macro, elle effectue simplement une substitution textuelle - les arguments que vous lui donnez sont donc utilisés directement.

Comme je n'ai utilisé la macro XOR_SWAP Qu'une seule fois, il était probablement exagéré de la définir, mais cela rendait plus clair ce que je faisais. Une fois que le préprocesseur C a développé la macro, la boucle while ressemble à ceci:

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

Notez que les arguments de macro apparaissent une fois pour chaque fois qu'ils sont utilisés dans la définition de macro. Cela peut être très utile, mais peut également casser votre code s'il n'est pas utilisé correctement. Par exemple, si j'avais compressé les instructions d'incrémentation/décrémentation et l'appel de macro sur une seule ligne, comme

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

Ensuite, cela s'étendrait à

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

Qui a triple les opérations d'incrémentation/décrémentation, et ne fait pas vraiment le swap qu'il est censé faire.

Pendant que nous sommes sur le sujet, vous devez savoir ce que xor (^) Signifie. C'est une opération arithmétique de base - comme l'addition, la soustraction, la multiplication, la division, sauf qu'elle n'est généralement pas enseignée à l'école primaire. Il combine deux entiers petit à petit - comme un ajout, mais nous ne nous soucions pas des reports. 1^1 = 0, 1^0 = 1, 0^1 = 1, 0^0 = 0.

Une astuce bien connue consiste à utiliser xor pour échanger deux valeurs. Cela fonctionne grâce aux trois propriétés de base de xor: x ^ 0 = x, x ^ x = 0 Et x ^ y = y ^ x Pour toutes les valeurs x et y. Supposons donc que nous ayons deux variables a et b qui stockent initialement deux valeurs va Et vb.

 // initialement: 
 // a == vune
 // b == vb
 a ^ = b; 
 // maintenant: a == vune ^ vb
 b ^ = a; 
 // maintenant: b == vb ^ (vune ^ vb) 
 // == vune ^ (vb ^ vb) 
 // == vune ^ 0 
 // == vune
 a ^ = b; 
 // maintenant: a == (vune ^ vb) ^ vune
 // == (vune ^ vune) ^ vb
 // == 0 ^ vb
 // == vb

Les valeurs sont donc échangées. Cela a un bug - lorsque a et b sont la même variable:

 // initialement: 
 // a == vune
 a ^ = a; 
 // maintenant: a == vune ^ vune
 // == 0 
 A ^ = a; 
 // maintenant: a == 0 ^ 0 
 // == 0 
 a ^ = a; 
 // maintenant: a == 0 ^ 0 
 // == 0 

Puisque nous str < end, Cela ne se produit jamais dans le code ci-dessus, donc ça va.

Bien que nous soyons préoccupés par l'exactitude, nous devons vérifier nos cas Edge. La ligne if (str) doit s'assurer que nous n'avons pas reçu de pointeur NULL pour la chaîne. Qu'en est-il de la chaîne vide ""? Eh bien strlen("") == 0, nous allons donc initialiser end comme str - 1, Ce qui signifie que la condition while (str < end) n'est jamais vraie, donc nous ne le faisons pas n'importe quoi. Qui est correct.

Il y a un tas de C à explorer. Aie du plaisir avec ça!

Mise à jour: mmw soulève un bon point, à savoir que vous devez faire un peu attention à la façon dont vous invoquez cela, car il fonctionne sur place.

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

Cela fonctionne très bien, car stack_string Est un tableau dont le contenu est initialisé à la constante de chaîne donnée. toutefois

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

Fera flamber et mourir votre code lors de l'exécution. C'est parce que string_literal Pointe simplement vers la chaîne qui est stockée dans le cadre de votre exécutable - qui est normalement de la mémoire que vous n'êtes pas autorisé à modifier par le système d'exploitation. Dans un monde plus heureux, votre compilateur le saurait et crache une erreur lorsque vous essayez de compiler, vous indiquant que string_literal Doit être de type char const * Car vous ne pouvez pas modifier le contenu. Cependant, ce n'est pas le monde dans lequel vit mon compilateur.

Il y a des hacks que vous pouvez essayer pour vous assurer qu'il y a de la mémoire sur la pile ou dans le tas (et donc modifiable), mais ils ne sont pas nécessairement portables, et cela peut être assez moche. Cependant, je suis plus qu'heureux d'en confier la responsabilité à l'invocateur de fonction. Je leur ai dit que cette fonction permet de manipuler la mémoire, c'est leur responsabilité de me donner un argument qui le permet.

61
rampion

Juste un réarrangement et un contrôle de sécurité. J'ai également supprimé votre type de retour non utilisé. Je pense que c'est un coffre-fort et propre car il obtient:

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

Modifié de sorte que la fin ne pointe pas vers un emplacement de mémoire potentiellement mauvais lorsque strlen est 0.

24
GManNickG

Vous pouvez mettre votre (len/2) test dans la boucle for:

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

}
16
Michael Burr

Ce programme complet montre comment je le ferais. Gardez à l'esprit que j'écrivais C lorsque la plupart d'entre vous, les whippersnappers, étaient une lueur dans les yeux de votre mère, donc c'est old-school, do-the-job, long-var-names-are-for-wimps. Corrigez cela si vous le souhaitez, je suis plus intéressé par l'exactitude du code.

Il gère les valeurs NULL, les chaînes vides et toutes les tailles de chaîne. Je ne l'ai pas testé avec des chaînes de taille maximale (max (size_t)) mais cela devrait fonctionner, et si vous manipulez des chaînes aussi grandes, vous êtes fou de toute façon :-)

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

La sortie de cela est:

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

Essaye ça:

reverse_string(NULL);
reverse_string("");
7
RossFabricant

Personne n'utilise plus de pointeurs?

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

EDIT: Désolé, je viens de remarquer l'exemple ci-dessus XOR exemple ...

6
Sanjaya R

Vous pouvez modifier votre déclaration de boucle for pour raccourcir le code:

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;
}
6
Ben Straub

Je ne vois pas de déclaration de retour et vous modifiez la chaîne d'entrée, ce qui peut être un problème pour le programmeur. Vous pouvez souhaiter que la chaîne d'entrée soit immuable.

En outre, cela peut être difficile, mais len/2 ne doit être calculé qu'une seule fois, OMI.

En dehors de cela, cela fonctionnera, tant que vous vous occupez des cas problématiques mentionnés par rossfabricant.

4
James Black
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; 
  }
}
4
kevin
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;
        }

}
2
resultsway
bool reverse_string(char* str)
{
    // Make sure str is reversible
    if (!str ||  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;
}

Cette solution est basée sur le post de GManNickG avec quelques modifications. L'instruction logique initiale peut être dangereuse si! Str n'est pas évalué avant l'opération strlen (pour un ptr NULL). Ce n'était pas le cas avec mon compilateur. J'ai pensé que j'ajouterais ce code parce que c'est un bel exemple de boucle do-while.

2
TheRealSheldon

Mes deux centimes:

/* 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"
1
madmurphy

code simple et simple 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;
    }

}
1
Dev_God

Puisque vous dites que vous voulez devenir fantaisiste, vous voudrez peut-être échanger vos personnages en utilisant un échange XOR .

1
chaos

Plutôt que de vous interrompre à mi-chemin, vous devriez simplement raccourcir votre boucle.

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;
}
1
dreamlax
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);
}
1
deepak kumar rout
#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]);
}
1
Kevin

Le code semble inutilement compliqué. Voici ma version:

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]; 
    }
}
1
Alex Richardson

Vous pouvez essayer l'arithmétique du pointeur:

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

C'est une bonne question ant2009 . Vous pouvez utiliser une fonction autonome pour inverser la chaîne. Le code est ...

#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 );
}
1
SDAH

Voici ma photo. J'évite de permuter simplement en utilisant le modèle standard strcpy:

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

et voici un exemple courant .

1
gon1332
#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;   
}
1
kyle k
/* 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;
}
1
siken.dongol