Em C, a forma como passamos argumentos para funções determina se as modificações dentro da função afetam as variáveis originais. A linguagem oferece duas maneiras: passagem por valor (padrão) e passagem por referência (simulada com ponteiros). Compreender essa diferença é essencial para escrever código correto e eficiente.

Nesta aula, vamos detalhar cada método, mostrar suas limitações e apresentar exemplos práticos. Ao final, você saberá quando usar cada abordagem e como implementar a passagem por referência em C.

Por valor

Na passagem por valor, o valor da variável é copiado para o parâmetro da função. Dentro da função, trabalhamos com uma cópia independente; alterações no parâmetro não afetam a variável original. Esse é o comportamento padrão em C para todos os tipos de dados (int, float, char, etc.).

Exemplo: uma função que tenta trocar dois números usando passagem por valor não consegue alterar os valores originais.

#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. A troca não ocorreu porque as variáveis a e b são cópias locais.

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

A principal limitação da passagem por valor é que funções não podem modificar variáveis do escopo chamador. Isso é intencional: protege os dados originais de alterações acidentais. Porém, quando precisamos que a função altere o valor original (por exemplo, em funções de troca, incremento ou leitura de dados), a passagem por valor se torna insuficiente.

Outra limitação: para tipos grandes (como structs grandes), copiar todo o valor pode ser ineficiente em termos de tempo e memória. Nesses casos, passar um ponteiro (referência) é mais eficiente.

Introdução a passar por referência

C não possui passagem por referência como C++ ou Java. Em vez disso, usamos ponteiros para simular esse comportamento. Passamos o endereço da variável (usando o operador &) e a função recebe um ponteiro. Dentro da função, desreferenciamos o ponteiro (com *) para acessar e modificar o valor original.

Exemplo: a função trocar agora recebe ponteiros para inteiros.

#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;
}

Agora a saída será: Depois: x = 20, y = 10. A função alterou as variáveis originais porque trabalhou com seus endereços.

Exemplos

Vamos ver mais exemplos práticos de passagem por referência.

Exemplo 1: Função que dobra o valor de uma variável

#include <stdio.h>

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

int main() {
    int valor = 5;
    printf("Antes: %d\n", valor);
    dobrar(&valor);
    printf("Depois: %d\n", valor);
    return 0;
}

Saída: Antes: 5 e Depois: 10.

Exemplo 2: Função que retorna múltiplos valores via parâmetros

#include <stdio.h>

void dividir(int dividendo, int divisor, int *quociente, int *resto) {
    *quociente = dividendo / divisor;
    *resto = dividendo % divisor;
}

int main() {
    int a = 17, b = 5;
    int q, r;
    dividir(a, b, &q, &r);
    printf("%d dividido por %d: quociente = %d, resto = %d\n", a, b, q, r);
    return 0;
}

Saída: 17 dividido por 5: quociente = 3, resto = 2.

Exemplo 3: Passagem de arrays (sempre por referência)

Em C, arrays são passados por referência automaticamente (o nome do array é um ponteiro).

#include <stdio.h>

void preencher(int arr[], int tamanho) {
    for (int i = 0; i < tamanho; i++) {
        arr[i] = i * 10;
    }
}

int main() {
    int vetor[5];
    preencher(vetor, 5);
    for (int i = 0; i < 5; i++) {
        printf("%d ", vetor[i]);
    }
    return 0;
}

Saída: 0 10 20 30 40.

Boas práticas

Use passagem por valor para tipos simples quando a função não precisa modificar o original. Use ponteiros (passagem por referência) quando precisar alterar o valor ou quando o tipo for grande. Sempre documente se um parâmetro ponteiro é usado para modificar o valor ou apenas para leitura (use const quando apropriado).

Referências

Exercícios

  1. Crie uma função que recebe um inteiro por valor e tenta somar 10 a ele. Mostre que o valor original não é alterado.

    ✓ Resposta:
    #include <stdio.h>
    
    void somar10(int n) {
        n += 10;
    }
    
    int main() {
        int x = 5;
        printf("Antes: %d\n", x);
        somar10(x);
        printf("Depois: %d\n", x);
        return 0;
    }

    Saída: Antes: 5, Depois: 5.

  2. Escreva uma função que recebe um ponteiro para inteiro e dobra o valor da variável apontada. Teste com um exemplo.

    ✓ Resposta:
    #include <stdio.h>
    
    void dobrar(int *p) {
        *p *= 2;
    }
    
    int main() {
        int v = 7;
        printf("Antes: %d\n", v);
        dobrar(&v);
        printf("Depois: %d\n", v);
        return 0;
    }
  3. Implemente uma função que recebe dois ponteiros para inteiros e troca seus valores.

    ✓ Resposta:
    #include <stdio.h>
    
    void trocar(int *a, int *b) {
        int temp = *a;
        *a = *b;
        *b = temp;
    }
    
    int main() {
        int x = 3, y = 8;
        printf("Antes: x=%d, y=%d\n", x, y);
        trocar(&x, &y);
        printf("Depois: x=%d, y=%d\n", x, y);
        return 0;
    }
  4. Crie uma função que recebe um array de inteiros e seu tamanho, e preenche o array com números pares (0, 2, 4, ...). Use passagem por referência.

    ✓ Resposta:
    #include <stdio.h>
    
    void preencherPares(int arr[], int n) {
        for (int i = 0; i < n; i++) {
            arr[i] = i * 2;
        }
    }
    
    int main() {
        int vetor[5];
        preencherPares(vetor, 5);
        for (int i = 0; i < 5; i++) {
            printf("%d ", vetor[i]);
        }
        return 0;
    }
  5. Escreva uma função que recebe um ponteiro para um float e o eleva ao quadrado. Teste com um valor.

    ✓ Resposta:
    #include <stdio.h>
    
    void quadrado(float *p) {
        *p = (*p) * (*p);
    }
    
    int main() {
        float x = 2.5;
        printf("Antes: %.2f\n", x);
        quadrado(&x);
        printf("Depois: %.2f\n", x);
        return 0;
    }