Strings em C

Um item que vejo muitos novatos na linguagem tendo dificuldades são as strings em C, por isso resolvi escrever alguns posts e tentar resolver muitas das duvidas que vejo por ai.

Para começo de conversa strings em C praticamente não existem, o compilador tem apenas uma vaga noção do que é uma string, pois elas são uma convenção onde um vetor de caracteres terminado com 0 (zero) representa uma string. Sendo assim, podemos criar uma string usando:


#include <stdio.h>

int main(int, char **)
{
    char ola[4];

    ola[0] = 'o';
    ola[1] = 'l';
    ola[2] = 'a';
    ola[3] = 0;

    printf(ola);

    return 0;
}

Este código simplesmente imprime “ola” na tela, mas note que ao inicializar ola colocamos o caracter ‘\ 0′ (que nada mais é que o numero 0) no final do array, ele é necessário pois é a única forma do printf saber onde termina a string. Agora escrever esse código toda vez que precisarmos criar uma string é bem chato, por isso, uma das poucas coisas que o compilador C sabe sobre strings é:

#include <stdio.h>
 
int main(int, char **)
{
    char ola[] = "ola";

    printf(ola);

    return 0;
}

Este código é idêntico ao anterior, mas escrito de uma maneira bem mais simples. Note que o compilador reconhece a string “ola” e declara um array de 4 chars e já inicializa ela com a string “ola” (incluindo o ). Também podemos escrever:

#include <stdio.h>

int main(int, char **)
{
    const char *ola = "ola";

    printf(ola);

    return 0;

}

Este código já é um pouco diferente do anterior, aqui não criamos um vetor de caracteres, e sim um ponteiro para uma região de memória constante do tipo char, trocando por miúdos, criamos um ponteiro que aponta para string. Mas onde diabos foi parar a string? O compilador alocou um trecho de memória constante na seção de dados do código (uma variável global). Por isso usamos const, pois esta string não deve ser modificada pelo código.

Contando Caracteres

Agora que já sabemos como uma string funciona em C, vamos fazer uma função para contar quantos caracteres uma string tem, assim podemos ver como o 0 no final dela é usado:

#include <stdio.h>

int contaChar(const char *str)
{
    int i = 0;

    for(;str[i] != 0; ++i);

    return i;
}

int main(int, char **)
{
    char ola[] = "ola";

    printf("A string %s possui %d caracteres\n", ola, contaChar(ola));

    return 0;
}

 
Note que sempre que precisamos saber quantos caracteres uma string possui, precisamos percorrer toda a string, isso pode trazer problemas de performance em algumas aplicações, e isto ocorre com quase todas as operações com string.

 

Outro detalhe, não é preciso criar a função contaChar, quando precisar saber o tamanho de uma string basta usar a strlen, que é declarada no string.h:

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

int main(int, char **)
{
    char ola[] = "ola";

    printf("A string %s possui %d caracteres\n", ola, strlen(ola));

    return 0;
}

Neste mesmo arquivo existem varias outras funções para se trabalhar com strings, é recomendável dar uma olhada na documentação antes de escrever a sua própria função para verificar se ela já não existe (alias, isso é recomendável para qualquer coisa, não apenas strings).
 

Comparando Strings

Agora chegamos ao ponto onde a maioria tem dificuldades, como saber se uma string é igual a outra? A idéia inicial é escrever:

#include <stdio.h>

int main(int, char **)
{
    char ola[] = "ola";
    char ola2[] = "ola";

    if(ola == ola2)
        printf("Iguais");
    else
        printf("Nao sao iguais");

    return 0;
}

Se você entendeu tudo até aqui já deve imaginar qual vai ser a saída do programa acima, se não, tente ler novamente e execute o programa. Muitos ficam surpresos ao ver o programa imprimir “Não são iguais”. Isto ocorre porque o if esta na verdade comparando dois ponteiros (lembre-se, arrays sem índice são ponteiros).

Então como fazer para saber se duas strings são iguais? É necessário comparar todos os caracteres das strings:

#include <stdio.h>

bool saoIguais(const char *s1, const char *s2)
{        
    for(int i = 0;s1[i] == s2[i]; ++i)
    {                
        if(s1[i] == 0)
            return true;
    }
    return false;
}

int main(int, char **)
{
    char ola[] = "ola";
    char ola2[] = "ola";

    if(saoIguais(ola, ola2))
        printf("Iguais");
    else
        printf("Nao sao iguais");

    return 0;
}

Agora já sabemos como identificar se duas strings são iguais ou não, um pouco trabalhoso, mas é o único jeito. Para simplificar um pouco a vida, a biblioteca padrão já vem com uma função de comparação de strings chamada strcmp, que funciona de maneira similar a função saoIguais, a diferença é que essa função não verifica apenas se as duas strings são iguais, ela compara as duas, retornando 0 quando são iguais, -1 quando a primeira string vem antes da segunda, ou 1 caso contrário:

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

int main(int, char **)
{
    char ola[] = "ola";
    char ola2[] = "ola";

    if(strcmp(ola, ola2) == 0)
        printf("Iguais\n");
    else if(strcmp(ola, ola2) != 0)
        printf("Nao sao iguais");

    return 0;
}

Pronto, agora já temos uma maneira simples de verificar se duas strings são iguais ou não. 

No próximo post, vamos dar uma olhada em mais algumas operações com strings em C.

10 Respostas para “Strings em C”

  1. Murilo Adriano Disse:

    Legal… eu não sabia que o ” é o mesmo que ” ou o (char)0.
    Lembrando que aquela função de comparação de strings só funciona se as duas strings tiverem o mesmo tamanho. Se tivermos str1 == “asda” e str2 == “asdajoja” por exemplo, a função saoIguais retornaria verdadeiro.

    Abraço!

  2. bcsanches Disse:

    Murilo,

    o ‘\ 0′ nao eh o mesmo que char 0, o wordpress comeu o “barra zero”! Tenho que colocar um espaco entre a \ e o 0, senao some…mas o certo é tudo junto.

    A funcao saoIguais funciona nesse caso que voce comentou, deu erro com voce?

  3. Tasso Evangelista Disse:

    Muito bom não só o artigo, mas todo o blog! Parabéns!

    Mas confirmando o que foi dito, na função saoIguais acredito que a condicional correta seria (s1[i] == 0 && s2[i] == 0); assim, verifica-se que ambas terminam com o mesmo tamanho. Também acho que é interessante salientar que o uso das funções da biblioteca string.h é recomendado pois seu código foi otimizado pela escrita com assembly. Deste modo, em qualquer grau de otimização. as funções strlen, strcmp, etc. já estam otimizadas ao máximo. Isso não impede que o usuário faça suas funções com otimização idêntica, mas o linker já coloca essas funções por default (acho), o que implicaria em duplicação de código. Tá, também é verdade que estaríamos reinventando a roda : D

    Abraços e muito sucesso!

  4. bcsanches Disse:

    Tasso,

    obrigado pelos elogios!

    sobre seus comentarios, ai vai:

    o if somente vai ser executado se o bloco do for for executado, e a condicao do for:
    s1[i] == s2[i]

    Ou seja, quando o if eh executado s1[i] e s2[i] sao iguais, entao nao existe necessidade de testar ambas.

    Eu comento em algum lugar que deve-se usar as funcoes da string.h ou de qualquer outra lib padrao ao inves de tentar re-escrever a sua. Nao somente por questao de evitar re-inventar a roda, mas bugs que vc pode causar (nao confie tanto na sua capacidade, a gente sempre erra).

    Sobre assembly, nao existe garantia, e o codigo em assembly nao eh garantia de ser mais rapido. Mas sim, a maioria dos compiladores vem com versoes bem otimizadas dessas funcoes.

    voce pode ate conseguir fazer uma versao mais otimizada, mas é dificil de se fazer e na maoria das vezes nao vale a pena o ganho que voce tem.

    Abracos

  5. Tasso Evangelista Disse:

    Ops! Falha minha! : )

    Quanto a questão das funções nativas do compilador, se eu desejasse somente verificar se duas strings são iguais, ainda assim a strcmp seria mais veloz, sendo que ela também compara a ordem das strings?

  6. bcsanches Disse:

    Tasso nao posso garantir se seria mais rapido ou nao que a saoIguais. Acredito que sim porque no visual por exemplo a funcao strcmp eh bem otimizada, comparando uma palavra (WORD, nao palavra da string) inteira, e nao char a char…

    Quanto a comparar apenas se sao iguais ou nao, com o pouco que sei de assembly, acredito que nao faria diferenca, pois para ver se sao iguais, voce usaria a instrucao cmp, que tambem seria usada para se verificar se vem antes ou depois. Pelo que me lembro, voce pode checar se sao iguais, menor ou maior com apenas uma chamada a cmp, depois usando jumps com os flags, ou seja a diferenca seria insignificante.

    A minha regra é sempre usar o que tiver na lib padrao, somente re-escrevo algo se eu tiver algum problema de performance e existir um caso bem especifico para qual eu poderia otimizar e tivesse algum ganho sginificativo, fora isso lib padrao.

  7. Tasso Evangelista Disse:

    Tá certo, obrigado pelos esclarecimentos e pelo ótimo tutorial!

  8. Caloni.com.br » Blog Archive » Últimas pesquisas na blogosfera nacional Disse:

    [...] Strings [...]

  9. imperador virtual Disse:

    UoU… Isso foi bem útil cara. Estou criando um jogo de aventura em modo texto, ascii-art, e precisei disso por causa duma sequencia que tem que acertar para abrir uma porta. Quando terminar o jogo volto a comentar aqui.

Deixe uma resposta