sexta-feira, 13 de novembro de 2015

C: Strings e ponteiros, um caso para pensar

O que está errado com função abaixo:

char mensagem[100] ;

processa()
{
    /* processa */
    if( /* uma condição de erro */ )
        strcpy( mensagem,"Deu erro 1" );
    else
    {
        /* processa */
        if( /* uma condição de erro */ )
            strcpy( mensagem,"Deu erro 2" );
        else
        {
            /* processa */
            if( /* uma condição de erro */ )
                strcpy( mensagem,"Deu erro 3" );
            else
                strcpy( mensagem,"Funcionou direitinho" );
        }
    }
}

Antes de prosseguir lendo, pare e pense um pouco.

O programa acima é ingênuo, feito com o conceito de strings em mente, com o conceito de atribuição de strings em mente, e como o C não existe um tipo string formal, então se faz o equivalente, que é copiar. Aliás, linguagens de alto nível fazem isto, copiam a string. Mas a grande pergunta é: "Realmente é necessário copiar?".

A resposta é não.

Qualquer string que exista no programa, seja como parâmetro da printf, na inicialização de um array de char, no strcpy como no exemplo acima etc, será criada e colocada na área de dados do programa, e portanto estará um lugar, uma posição, de memória. O array mensagem usará 100 Bytes da memória. E ainda existem dois riscos. Um, de pouco impacto no caso, de desperdício de memória com um array superdimensionado, e um de grande impacto, o array ser sub-
dimensionado, caso surja uma mensagem maior que ele.

No conceito do tipo de variável string, tal como algumas linguagens trabalham, o programa acima está corretíssimo, mas com forte conceito de ponteiros que a linguagem C tem, e a tipagem fraca e mais livre, o programa pode ser considerado conceitualmente, e em matéria de eficiência, um desastre.

Veja a nova forma abaixo:

char *mensagem ;

processa()
{
    /* processa */
    if( /* uma condição de erro */ )
        mensagem = "Deu erro 1" ;
    else
    {
        /* processa */
        if( /* uma condição de erro */ )
            mensagem = "Deu erro 2" ;
        else
        {
            /* processa */
            if( /* uma condição de erro */ )
                mensagem = "Deu erro 3" ;
            else
                mensagem = "Funcionou direitinho" ;
        }
    }
}

As chamadas da strcpy gastam código e tempo de processamento empilhando os parâmetros, chamando a função, retornando da função, executando a função etc. Alguns compiladores, com opções de otimização ligadas, inserem a função strcpy no lugar de chamada, poupando muito processamento, mas mesmo assim nem chega perto da atribuição de um valor constante a um ponteiro. Como o endereço onde está a string é fixo (ou resolvido a tempo de carga do programa em alguns sistemas operacionais), só resta um valor constante a ser colocado no ponteiro. Agora não existe mais o gasto de 100 bytes na criação da variável, sendo bem menor com o ponteiro, e nem mais o risco de estar com o tamanho pequeno demais e causar um desastre.

Este caso pode causar estranheza entre os que não estão acostumados à forma com o que o C lida com strings, mas esta é a forma correta e eficiente de tratar strings em C, e é bem mais eficiente do que em muitas outras linguagens.

Só tem um cuidado a tomar. A string apontada pelo ponteiro não pode sofrer alteração, pois assim alterará o conteúdo original em memória.

Nenhum comentário:

Postar um comentário