Problema ao passar arquivo como argumento de função.

Re: Problema ao passar arquivo como argumento de função.

por Francisco Reverbel -
Número de respostas: 0
Não há nenhum problema em passar um parâmetro FILE *arquivo para uma função, mas se a função alterar o valor desse parâmetro, executando uma atribuição como

   arquivo = fopen(string, "w");

a alteração só será válida dentro da função! Esse é o problema com o código que o Caio postou na primeira mensagem desta thread.

Analisando em detalhes... A linha

    FILE *arquivo;

declara uma variável tipo ponteiro que não está inicializada. Essa variável serve para apontar para um FILE (seja lá o que isso for), mas (por enquanto) ela não aponta para nada, ou seja, ela contém "lixo", em vez de conter o endereço de algum FILE. Uma variável como essa é geralmente inicializada por uma atribuição


   arquivo = fopen(string, "w");


É essa atribuição que coloca na variável arquivo o endereço (devolvido pela chamada a fopen) de um FILE!

Agora vejamos este trecho do programa que o Caio postou:

    FILE *arquivo;

    scanf("%s", string);  
    funcao(arquivo, string);
    fprintf(arquivo, "%c", c);


Lembrem-se que na passagem de parâmetros o que ocorre é uma cópia dos valores passados como parâmetros. Ou seja, a funcao do Caio recebe uma cópia do lixo contido na variável arquivo. Embora ela atualize essa cópia com o endereço devolvido pela chamada fopen, a atualização só vale dentro da funcao! O endereço é perdido quando a funcao volta para quem a chamou.  Quando é executado o fprintf(arquivo, "%c", c), a variável arquivo contém exatamente o mesmo lixo que ela continha antes da chamada à funcao!

A maneira mais fácil de resolver esse problema é a que o Rodrigo indicou. Outra forma (mais complicada, que é bom conhecer mas não vale a pena usar neste caso) é usar um ponteiro para um ponteiro, ou seja passar à funcao o endereço de um ponteiro para que ela possa inicializar o ponteiro:

void funcao(FILE **arq, char string[])
{
    *arq = fopen(string, "w");
}

int main()
{
    char c;
    char string[100];
    FILE *arquivo;
    scanf("%s", string);  
    funcao(&arquivo, string);
    fprintf(arquivo, "%c", c);
    return 0;
}

Agora a declaração do primeiro parâmetro da função (FILE **arq) diz que ele é um ponteiro (o * mais à direita) para um FILE *. Em outras palavras, arq é o endereço de um FILE *, e *arq é um FILE *. A função agora usa esse ponteiro para inicializar o FILE * apontado por ele com o endereço devolvido por fopen. Notem que, na chamada a função, é passado o endereço (&) da variável arquivo, cujo tipo é FILE *.

Como ponteiros para ponteiros são uma coisa mais complicada e difícil de entender, eles só devem ser usados quando realmente houver necessidade. Quando a solução do Rodrigo (devolver o FILE * como valor da função) for aplicável, ela deve ser preferida. Usem ponteiros para ponteiros apenas quando essa solução não for aplicável: caso vocês precisem de uma função que devolva mais de um ponteiro (um FILE *f1 e um FILE *f1, por exemplo -- só um deles pode ser devolvido como valor da função, o outro terá que ser devolvido através de um argumento como FILE **endereco_de_FILE_ptr).