Invertendo uma string em C

Eu desenvolvi um programa de seqüência reversa. Eu estou querendo saber se existe uma maneira melhor de fazer isso, e se o meu código tem algum problema em potencial. Eu estou olhando para praticar alguns resources avançados 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; } } } 

Se você quiser praticar resources avançados de C, que tal pointers? Podemos jogar macros e xor-swap por diversão também!

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

Um ponteiro (por exemplo, char * , lido da direita para a esquerda como um ponteiro para um char ) é um tipo de dado em C que é usado para se referir à localização na memory de outro valor. Nesse caso, o local onde um char é armazenado. Podemos cancelar a referência de pointers, prefixando-os com um * , o que nos dá o valor armazenado nesse local. Portanto, o valor armazenado em str é *str .

Nós podemos fazer aritmética simples com pointers. Quando incrementamos (ou diminuímos) um ponteiro, simplesmente o movemos para nos referirmos ao próximo (ou anterior) local de memory para esse tipo de valor. Incrementar pointers de tipos diferentes pode mover o ponteiro por um número diferente de bytes porque valores diferentes têm diferentes tamanhos de bytes em C.

Aqui, usamos um ponteiro para se referir ao primeiro char não processado da string ( str ) e outro para referir-se ao último ( end ). Nós trocamos seus valores ( *str e *end ) e movemos os pointers para o meio da string. Uma vez str >= end , ambos apontam para o mesmo char , o que significa que nossa string original tem um comprimento ímpar (e o char do meio não precisa ser invertido), ou nós processamos tudo.

Para fazer a troca, eu defini uma macro . Macros são substituições de texto feitas pelo pré-processador C. Eles são muito diferentes das funções e é importante saber a diferença. Quando você chama uma function, a function opera em uma cópia dos valores que você fornece. Quando você chama uma macro, ela simplesmente faz uma substituição textual - então os argumentos que você dá são usados ​​diretamente.

Como eu usei apenas a macro XOR_SWAP uma vez, provavelmente foi um exagero para defini-la, mas deixou mais claro o que eu estava fazendo. Depois que o pré-processador C expande a macro, o loop while fica assim:

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

Observe que os argumentos de macro são exibidos uma vez para cada vez que são usados ​​na definição de macro. Isso pode ser muito útil - mas também pode quebrar seu código se usado incorretamente. Por exemplo, se eu tivesse comprimido as instruções de incremento / decremento e a chamada de macro em uma única linha, como

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

Então isso se expandiria para

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

Que tem o triplo das operações de incremento / decremento, e não faz a troca que deveria fazer.

Enquanto estamos no assunto, você deve saber o que xor ( ^ ) significa. É uma operação aritmética básica - como adição, subtração, multiplicação, divisão, exceto que geralmente não é ensinada na escola primária. Ele combina dois inteiros, bit por bit - como a adição, mas não nos importamos com os carry-overs. 1^1 = 0 , 1^0 = 1 , 0^1 = 1 , 0^0 = 0 .

Um truque bem conhecido é usar xor para trocar dois valores. Isso funciona devido a três propriedades básicas de xor: x ^ 0 = x , x ^ x = 0 e x ^ y = y ^ x para todos os valores x e y . Então digamos que temos duas variables b que estão inicialmente armazenando dois valores v a e v b .

   // inicialmente:
   // a == v a
   // b == v b
   a ^ = b;
   // now: a == v a ^ v b
   b ^ = a;
   // now: b == v b ^ (v a ^ v b )
   // == v a ^ (v b ^ v b )
   // == v a ^ 0
   // == v a
   a ^ = b;
   // agora: a == (v a ^ v b ) ^ v a
   // == (v a ^ v a ) ^ v b
   // == 0 ^ v b
   // == v b

Então os valores são trocados. Isso tem um bug - quando b são a mesma variável:

   // inicialmente:
   // a == v a
   um ^ = a;
   // now: a == v a ^ v a
   // == 0
   um ^ = a;
   // now: a == 0 ^ 0
   // == 0
   um ^ = a;
   // now: a == 0 ^ 0
   // == 0

Desde que nós str < end , isso nunca acontece no código acima, então estamos bem.

Enquanto estamos preocupados com a correção, devemos verificar nossos casos de limites. A linha if (str) deve certificar-se de que não recebemos um ponteiro NULL para string. E quanto à string vazia "" ? Bem, strlen("") == 0 , então vamos inicializar end como str - 1 , o que significa que a condição while (str < end) nunca é verdadeira, então não fazemos nada. Qual é correto.

Há um monte de C para explorar. Divirta-se com isso!

Update: mmw traz um bom ponto, que é que você tem que ser um pouco cuidadoso com a forma como invoca isso, pois ele opera no local.

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

Isso funciona bem, já que stack_string é um array, cujo conteúdo é inicializado para a constante de string dada. Contudo

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

Faz com que seu código seja acionado e morra em tempo de execução. Isso porque string_literal apenas aponta para a string que é armazenada como parte do seu executável - que normalmente é a memory que você não tem permissão para editar pelo sistema operacional. Em um mundo mais feliz, seu compilador saberia disso e tossiria um erro quando você tentasse compilar, dizendo que o string_literal precisa ser do tipo char const * já que você não pode modificar o conteúdo. No entanto, este não é o mundo em que meu compilador vive.

Existem alguns hacks que você pode tentar certificar-se de que alguma memory está na pilha ou no heap (e, portanto, é editável), mas eles não são necessariamente portáveis, e pode ser bem feio. No entanto, estou mais do que feliz em jogar a responsabilidade por isso para o invocador de funções. Eu disse a eles que essa function faz a manipulação de memory, é responsabilidade deles me dar um argumento que permita isso.

Apenas um rearranjo e verificação de segurança. Eu também removi seu tipo de retorno não usado. Eu acho que isso é seguro e limpo como fica:

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

Editado para que end não aponte para um local de memory possivelmente ruim quando o strlen for 0.

Você pode colocar seu teste (len/2) no loop for:

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

Este programa completo mostra como eu faria isso. Tenha em mente que eu estava escrevendo C quando a maioria de vocês whippersnappers era um brilho nos olhos de sua mãe, então é old-school, fazer o trabalho, long-var-nomes-são-para-fracos. Conserte que, se desejar, estou mais interessado na correção do código.

Ele lida com NULLs, strings vazias e todos os tamanhos de string. Eu não testei com strings de tamanho máximo (max (size_t)), mas deve funcionar, e se você está lidando com strings tão grandes, você está louco de qualquer maneira 🙂

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

A saída disso é:

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

Tente isto:

 reverse_string(NULL); reverse_string(""); 

Você pode alterar sua declaração de loop para tornar o código mais curto:

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

Ninguém mais usa pointers?

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

EDIT: Desculpe, só notei o exemplo acima XOR …

Eu não vejo uma declaração de retorno, e você está mudando a string de input, o que pode ser um problema para o programador. Você pode querer que a string de input seja imutável.

Além disso, isso pode ser exigente, mas len / 2 deve ser calculado apenas uma vez, IMO.

Além disso, funcionará, desde que você cuide dos casos de problemas mencionados pelo rossfabricant.

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

Esta solução é baseada no post do GManNickG com algumas modificações. A declaração lógica inicial pode ser perigosa se! Str não for avaliada antes da operação strlen (para um pulo NULL). Este não foi o caso do meu compilador. Eu pensei em adicionar esse código porque é um bom exemplo de um loop do-while.

Já que você diz que quer ficar chique, talvez queira trocar seus personagens usando uma troca XOR .

Em vez de quebrar a meio caminho, você deve simplesmente encurtar seu ciclo.

 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; } 
 #include  #include  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; } 
 #include  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]); } 
 Here is my shot which will handle all the cases char *p ="KDLAKDADKADAD" char p[] = "lammdlamldaldladadada" also empty string #include #includeenter code here #include 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); } 
 /* Author: Siken Dongol */ #include  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; } 

O código parece desnecessariamente complicado. Aqui está a minha versão:

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

código simples e fácil 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; } } 

Aqui está o meu tiro. Eu evito trocar apenas usando o padrão padrão 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; } 

e aqui um exemplo em execução .

Meus dois centavos:

 /* 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); } 

Teste nº 1strrev() :

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

Teste # 2strnrev() :

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

Essa é uma boa pergunta ant2009 . Você pode usar uma function autônoma para reverter a string. O código é …

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

Você pode tentar essa aritmética de pointers:

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