A passagem de parâmetros é a forma como funções recebem dados para processamento. Em C, existem duas maneiras principais: por valor e por referência (simulada com ponteiros). Compreender a diferença é essencial para evitar erros e escrever código eficiente.

Nesta aula, veremos como cada método funciona, suas vantagens e limitações, e exemplos práticos que ilustram o comportamento na prática.

Por valor

Na passagem por valor, o argumento passado para a função é copiado para o parâmetro formal. A função trabalha com essa cópia, e qualquer modificação feita nela não afeta a variável original. Isso é o padrão em C para tipos básicos como int, float, char etc.

Exemplo: considere uma função que tenta trocar dois valores. Usando passagem por valor, a troca só ocorre dentro da função.

#include <stdio.h>

void trocar(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 10, y = 20;
    printf("Antes: x=%d, y=%d\n", x, y);
    trocar(x, y);
    printf("Depois: x=%d, y=%d\n", x, y);
    return 0;
}

A saída será: Antes: x=10, y=20 e Depois: x=10, y=20. As variáveis originais não são alteradas.

Limitação (não altera o original)

A principal limitação da passagem por valor é a impossibilidade de modificar variáveis do escopo chamador. Isso é útil quando queremos proteger os dados originais, mas problemático quando precisamos que a função altere o estado externo (como em trocas, leitura de dados, etc.).

Outra limitação é a cópia de grandes estruturas (structs, arrays), que pode ser ineficiente. Arrays, na verdade, são passados como ponteiros (decaindo para ponteiro), mas o conteúdo não é copiado — veremos isso mais adiante.

Introdução a passar por referência

C não possui passagem por referência nativa como C++ ou Java. No entanto, podemos simular esse comportamento usando ponteiros. Passamos o endereço da variável (&var) e a função recebe um ponteiro. Assim, a função pode alterar o valor original através do operador de dereferência (*).

Exemplo: a função trocar reescrita com ponteiros.

#include <stdio.h>

void trocar(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    printf("Antes: x=%d, y=%d\n", x, y);
    trocar(&x, &y);
    printf("Depois: x=%d, y=%d\n", x, y);
    return 0;
}

Saída: Antes: x=10, y=20 e Depois: x=20, y=10. Agora a troca é efetiva.

É importante lembrar que, mesmo usando ponteiros, a passagem ainda é por valor (o valor do ponteiro é copiado), mas o efeito é de referência porque podemos alterar o dado apontado.

Exemplos

Vejamos mais exemplos práticos para consolidar.

Exemplo 1: Função que dobra um valor

#include <stdio.h>

void dobrar(int *num) {
    *num *= 2;
}

int main() {
    int a = 5;
    dobrar(&a);
    printf("a = %d\n", a); // 10
    return 0;
}

Exemplo 2: Lendo múltiplos valores via função

#include <stdio.h>

void ler_valores(int *x, int *y) {
    printf("Digite dois inteiros: ");
    scanf("%d %d", x, y); // x e y já são endereços
}

int main() {
    int a, b;
    ler_valores(&a, &b);
    printf("Você digitou: %d e %d\n", a, b);
    return 0;
}

Exemplo 3: Passagem de array (decai para ponteiro)

#include <stdio.h>

void modificar_array(int arr[], int n) {
    for (int i = 0; i < n; i++)
        arr[i] *= 2;
}

int main() {
    int v[] = {1, 2, 3, 4};
    modificar_array(v, 4);
    for (int i = 0; i < 4; i++)
        printf("%d ", v[i]); // 2 4 6 8
    return 0;
}

Aqui, o array é passado como ponteiro, então as alterações afetam o original.

Boas práticas

Use passagem por valor para tipos pequenos quando não precisar modificar o original. Use ponteiros (referência simulada) quando precisar alterar ou para evitar cópias de grandes estruturas. Sempre documente se a função modifica os parâmetros.

Referências

Exercícios

  1. Escreva uma função que receba dois inteiros por valor e retorne a soma. Teste no main.
  2. ✓ Resposta:
    #include <stdio.h>
    
    int soma(int a, int b) {
        return a + b;
    }
    
    int main() {
        int x = 3, y = 7;
        printf("%d\n", soma(x, y)); // 10
        return 0;
    }
  3. Crie uma função que receba um float por referência e o eleve ao quadrado.
  4. ✓ Resposta:
    #include <stdio.h>
    
    void quadrado(float *p) {
        *p = (*p) * (*p);
    }
    
    int main() {
        float val = 4.5;
        quadrado(&val);
        printf("%.2f\n", val); // 20.25
        return 0;
    }
  5. Implemente uma função que troque três variáveis inteiras de forma circular (a recebe b, b recebe c, c recebe a). Use ponteiros.
  6. ✓ Resposta:
    #include <stdio.h>
    
    void trocar_circular(int *a, int *b, int *c) {
        int temp = *a;
        *a = *b;
        *b = *c;
        *c = temp;
    }
    
    int main() {
        int x=1, y=2, z=3;
        trocar_circular(&x, &y, &z);
        printf("%d %d %d\n", x, y, z); // 2 3 1
        return 0;
    }
  7. Explique por que o código abaixo não funciona como esperado e corrija-o usando passagem por referência: void inc(int x) { x++; }
  8. ✓ Resposta:

    O código usa passagem por valor, então x++ incrementa a cópia local, não a original. Correção:

    void inc(int *x) {
        (*x)++;
    }
    
    int main() {
        int a = 5;
        inc(&a);
        printf("%d\n", a); // 6
        return 0;
    }
  9. Crie uma função que receba um array de inteiros e seu tamanho, e retorne a média (como double). Use passagem por referência (ponteiro) para o array.
  10. ✓ Resposta:
    #include <stdio.h>
    
    double media(int *arr, int n) {
        int soma = 0;
        for (int i = 0; i < n; i++)
            soma += arr[i];
        return (double)soma / n;
    }
    
    int main() {
        int v[] = {10, 20, 30};
        printf("%.2f\n", media(v, 3)); // 20.00
        return 0;
    }