segunda-feira, 1 de outubro de 2012

Passagem de parâmetros em C


Ao criamos uma função podemos passar parâmetros. Estes parâmetros podem ser passado de algumas formas mas as mais clássicas são por valor ou por referência. Para entender o que é isto teremos de dar uma olhadinha na pilha memória do computador. Começamos definindo funções que recebem inteiros. Serei um tanto simplista no meu exemplo e espero que isto não atrapalhe o entendimento.

Definiremos 3 funções: uma recebe o parâmetro por valor e retorna o resultado e outra por referência, ou seja, ponteiros, e retorna o valor e outra recebe por referência e não retorna nada.

int soma_valor(int x, int y){
   return x + y;
}

int soma_referência1(int * x, int * y){
   return *x + *y;
}

void soma_referência2(int * x, int * y){
   *x = *x + *y;
}

main(){
   int a = 3;
   int b = 5;

   soma_valor(a,b);
   soma_referência1(&a, &b);
   soma_referência2(&a, &b);
}

Bem, vamos ver o que acontece na nossa pilha quando executamos o programa main.

O Sistema operacional (SO) irá alocar espaço para a variável a e vai guardar o valor 3. Depois ele vai guardar espaço para a variável b e vai guardar o valor 5.

Ao chamarmos a função soma_valor, ele vai alocar outros espaços para outras variáveis x e y (que também poderiam chamar a e b ou qualquer outro nome. Mudei o nome só para facilitar a explicação) e vai copiar o valor de a para x e de b para y. O sistema vai alocar também uma variável sem nome para o retorno da função. Temos neste ponto alocados na memória 5 inteiros: a, b, x, y e retorno. A função executa, libera a memória das variáveis x, y e retorno e retorna o valor. Não estamos fazendo nada com o retorno dela mas ela retorna.
Custo total: alocação de 5 variáveis, calcular o resultado da soma e cópia de memória de a para x, b para y e resultado da soma para o retorno.

Na próxima função, alocamos apenas o inteiro para o retorno. A chamada da função irá receber um ponteiro, ou seja, o endereço alocado previamente (indicado pelo &) das variáveis a e b e não precisará alocar espaço para seus valores e nem copiar os valores de uma região da memória para outro. Internamente ela pega o valor que está neste endereço (indicado pelo *), soma, copia o valor da soma para o retorno e retorna. Com certeza isto é mais barato pois no final teremos alocado apenas o valor de um inteiro para o retorno.
Custo total: Alocação de 1 variável retorno, calcular o resultado da soma e cópia do resultado da soma para o retorno.

Na terceira e última função, não alocamos valor nenhum da memória. A função receberá o endereço das variáveis previamente alocadas, irá copiar o valor da soma para o endereço de memória da variável a (que internamente na função é chamado de x) e não precisa retornar. Neste caso, no término da função o valor da variável a será 8 e não mais 3. Isto evita a alocação da variável de retorno mas irá perder para sempre o valor inicial de a. Isto pode ser bom ou ruim, dependendo do objetivo do programa.
Custo total: Calcular o resultado da soma.

Agora, imagine que este programa seja o processamento de um vídeo e vc esteja somando 2 vídeos de 2 GB cada. Na primeira função, temos a alocação de 5 variáveis que somadas ocuparão 10GB da memória. No segundo exemplo ocuparemos apenas 6GB de memória e no terceiro apenas 4GB de memória, o que é o custo mínimo já que os mesmos precisarão ser carregados. Acho que dá para começar a sentir a diferença de desempenho destas funções que fazem a mesma coisa mas que fazem de forma diferente...

No primeiro exemplo temos a cópia da memória de 4GB das variáveis a e b para x e y. Quanto tempo isto leva? E se esta função for chamada 10 vezes em um segundo? Dá para fazer isto em "tempo real"? No segundo exemplo não perdemos tempo copiando os valores de entrada mas precisamos copiar o valor para a variável de retorno. Já é mais eficiente. No terceiro podemos fazer a soma diretamente no endereço de a (que internamente na função é chamado de x) e esta soma pode ser feita "in loco". Com certeza mais eficiente.

Alguns textos que tem exemplos mais bonitos que o meu:



Agradeço ao Rael que me mandou a dúvida e que rendeu este texto.

Nenhum comentário: