C++ Parâmetros e Argumentos

Na aula Funções, a função saudar() sempre dizia a mesma frase. Mas a Ana quer cumprimentar cada cliente pelo nome: "Olá, Bruno!", "Olá, Carla!". Para isso, a função precisa receber uma informação de quem a chama. Essa informação entra pelos parênteses: do lado da função, ela se chama parâmetro; do lado da chamada, argumento. Nesta aula você vai criar funções que recebem um, dois, três valores, ver o que o compilador diz quando você manda valores de menos ou de mais, e descobrir o que acontece quando o valor enviado é de outro tipo.

Você vai aprender
  • A diferença entre parâmetro (na definição) e argumento (na chamada).
  • Como criar funções com vários parâmetros, cada um com seu tipo.
  • Por que a ordem dos argumentos importa.
  • Os erros too few arguments to function e too many arguments to function.
  • O que acontece quando o argumento é de outro tipo: a conversão implícita (e por que 2.9 pode virar 2 sem aviso).

Todos os programas desta aula foram compilados com o g++ 15 na máquina fictícia da Ana, na pasta ~/cpp, com o comando padrão do guia, g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa.


A ideia: o formulário e o que você preenche

Pense num formulário de cadastro. Ele tem campos com rótulo: "Nome: ____", "Idade: ____". O formulário em branco é sempre o mesmo; o que muda é o que cada pessoa escreve nos campos.

No formulárioEm C++Exemplo
O campo em branco, com rótuloParâmetro: escrito na definição da funçãovoid saudar(std::string nome)
O que alguém escreveu no campoArgumento: o valor enviado na chamadasaudar("Bruno");

Um jeito de lembrar: o parâmetro fica no projeto da função; o argumento chega na hora da ação, a chamada. Muita gente usa as duas palavras como sinônimos no dia a dia, e tudo bem; mas as mensagens do compilador usam argument para o valor da chamada, então vale saber a diferença.


1. Uma função com um parâmetro

#include <iostream>
#include <string>

void saudar(std::string nome) {
    std::cout << "Olá, " << nome << "! Bem-vinda à lojinha.\n";
}

int main() {
    saudar("Ana");
    saudar("Bruno");
    std::string cliente = "Carla";
    saudar(cliente);
}
#include <string>
Traz o tipo std::string (texto), que o parâmetro usa (aula Strings).
(std::string nome)
O parâmetro: um tipo (std::string) e um nome (nome), como na criação de uma variável. É exatamente isso: nome é uma variável que existe só dentro da função e nasce com o valor que veio na chamada.
<< nome <<
Dentro do corpo, o parâmetro é usado como qualquer variável.
saudar("Ana");
Chamada com o argumento "Ana". Nesta execução da função, nome vale "Ana".
saudar(cliente);
O argumento pode ser uma variável. A função recebe o valor dela ("Carla"). O nome da variável de fora (cliente) não precisa ser igual ao do parâmetro (nome).
$ g++ -std=c++23 -Wall -Wextra saudar.cpp -o saudar
$ ./saudar
Olá, Ana! Bem-vinda à lojinha.
Olá, Bruno! Bem-vinda à lojinha.
Olá, Carla! Bem-vinda à lojinha.

Uma função, três frases diferentes. A cada chamada, o parâmetro nome é criado de novo com o argumento daquela chamada, e some quando a função termina.

O argumento pode ser qualquer expressão

Um valor fixo, uma variável ou uma conta: o C++ calcula primeiro o argumento e só depois entra na função com o resultado.

#include <iostream>

void mostrarDobro(int x) {
    std::cout << "O dobro de " << x << " é " << x * 2 << "\n";
}

int main() {
    mostrarDobro(5);
    int idade = 29;
    mostrarDobro(idade);
    mostrarDobro(3 + 4);
    mostrarDobro(idade - 20);
}
$ g++ -std=c++23 -Wall -Wextra dobro.cpp -o dobro
$ ./dobro
O dobro de 5 é 10
O dobro de 29 é 58
O dobro de 7 é 14            « 3 + 4 foi calculado antes: a função recebeu 7
O dobro de 9 é 18            « idade - 20 = 9

Repare na linha 11: a função não sabe que você escreveu 3 + 4. Ela só recebe o valor 7.

O parâmetro é uma cópia

Como o parâmetro recebe só o valor, mudar o parâmetro dentro da função não muda a variável usada na chamada. Na chamada mostrarDobro(idade), se a função fizesse x = 0;, a idade do main continuaria 29. Isso se chama passagem por valor, e a aula Por Valor × Por Referência mostra como fazer a função mudar a variável de fora quando você quer.

Experimente: chame saudar com o seu nome. Depois crie uma variável std::string amiga = "..."; e passe-a como argumento. Por fim, tente saudar("Ana" + std::string(" Paula"));: o argumento é uma conta de textos.

#include <iostream>
#include <string>

void saudar(std::string nome) {
    std::cout << "Olá, " << nome << "! Bem-vinda à lojinha.\n";
}

int main() {
    saudar("Ana");
}

2. Vários parâmetros

Para o recibo, a Ana quer uma função que mostre um item: o produto, a quantidade e o preço. São três informações, então a função tem três parâmetros, separados por vírgula:

#include <iostream>
#include <string>

void item(std::string produto, int quantidade, double preco) {
    std::cout << quantidade << " x " << produto
              << " = R$ " << quantidade * preco << "\n";
}

int main() {
    item("caderno", 3, 10.0);
    item("caneta", 2, 2.5);
    item("borracha", 1, 1.75);
}
std::string produto
1º parâmetro: o nome do produto, um texto.
int quantidade
2º parâmetro: quantas unidades, um número inteiro (Inteiros).
double preco
3º parâmetro: o preço de cada um, um número com casas decimais (float e double).
item("caderno", 3, 10.0);
Três argumentos, na mesma ordem: o 1º vai para produto, o 2º para quantidade, o 3º para preco.
$ g++ -std=c++23 -Wall -Wextra item.cpp -o item
$ ./item
3 x caderno = R$ 30
2 x caneta = R$ 5
1 x borracha = R$ 1.75

(O std::cout mostra 30 e não 30.00; para fixar duas casas, veja Formatar com iomanip ou std::format.)

Cada parâmetro tem o seu tipo

Na criação de variáveis, int a, b; cria duas variáveis int de uma vez. Nos parâmetros, não: cada um precisa do próprio tipo, mesmo que seja igual ao anterior.

#include <iostream>

void soma(int a, b) {
    std::cout << a + b << "\n";
}

int main() {
    soma(2, 3);
}
$ g++ -std=c++23 -Wall -Wextra semtipo.cpp -o semtipo
semtipo.cpp:3:18: error: ‘b’ has not been declared
    3 | void soma(int a, b) {
      |                  ^
semtipo.cpp: In function ‘void soma(int, int)’:
semtipo.cpp:4:22: error: ‘b’ was not declared in this scope
    4 |     std::cout << a + b << "\n";
      |                      ^

O compilador leu b como se fosse um tipo desconhecido (‘b’ has not been declared = "‘b’ não foi declarado") e, por isso, o parâmetro ficou sem nome; aí o b do corpo também não existe. A correção é void soma(int a, int b).

A ordem importa

O C++ liga argumentos a parâmetros pela posição, nunca pelo nome. Se os tipos forem diferentes, trocar a ordem dá erro de compilação, o que é bom. Aqui a Ana trocou a quantidade e o produto de lugar na linha 10:

#include <iostream>
#include <string>

void item(std::string produto, int quantidade, double preco) {
    std::cout << quantidade << " x " << produto
              << " = R$ " << quantidade * preco << "\n";
}

int main() {
    item(3, "caderno", 10.0);
}
$ g++ -std=c++23 -Wall -Wextra ordem.cpp -o ordem
ordem.cpp: In function ‘int main()’:
ordem.cpp:10:10: error: could not convert ‘3’ from ‘int’ to ‘std::string’ {aka ‘std::__cxx11::basic_string<char>’}
   10 |     item(3, "caderno", 10.0);
      |          ^
      |          |
      |          int

Could not convert ‘3’ from ‘int’ to ‘std::string’ = "não foi possível converter ‘3’ de int para std::string": o 1º parâmetro espera um texto e recebeu um número. O pedaço entre chaves, {aka …} ("também conhecido como"), é o nome completo e assustador de std::string por dentro; pode ignorar.

Mas, se os parâmetros forem do mesmo tipo, o compilador não tem como perceber a troca:

#include <iostream>

void retangulo(int largura, int altura) {
    std::cout << "largura " << largura << ", altura " << altura << "\n";
}

int main() {
    retangulo(5, 2);
    retangulo(2, 5);
}
$ g++ -std=c++23 -Wall -Wextra ordem2.cpp -o ordem2
$ ./ordem2
largura 5, altura 2
largura 2, altura 5          « compilou sem aviso: o C++ não sabe o que você quis dizer

Por isso, dê aos parâmetros nomes que expliquem o papel de cada um (largura, altura, e não a, b) e confira a ordem quando chamar.


3. Número errado de argumentos

Uma chamada precisa mandar exatamente um argumento para cada parâmetro. A Ana esqueceu o preço:

#include <iostream>
#include <string>

void item(std::string produto, int quantidade, double preco) {
    std::cout << quantidade << " x " << produto
              << " = R$ " << quantidade * preco << "\n";
}

int main() {
    item("caderno", 3);
}
$ g++ -std=c++23 -Wall -Wextra poucos.cpp -o poucos
poucos.cpp: In function ‘int main()’:
poucos.cpp:10:9: error: too few arguments to function ‘void item(std::string, int, double)’
   10 |     item("caderno", 3);
      |     ~~~~^~~~~~~~~~~~~~
poucos.cpp:4:6: note: declared here
    4 | void item(std::string produto, int quantidade, double preco) {
      |      ^~~~
poucos.cpp:10:9
O erro está na chamada, linha 10.
too few arguments to function
"Argumentos de menos para a função".
‘void item(std::string, int, double)’
O g++ mostra o que a função espera: três valores, um texto, um inteiro e um double. Compare com a sua chamada e veja qual falta.
note: declared here
"Nota: declarada aqui": aponta a linha 4, onde a função foi definida, para você conferir os parâmetros.

Mandar argumentos a mais dá o erro irmão:

$ g++ -std=c++23 -Wall -Wextra muitos.cpp -o muitos       « item("caderno", 3, 10.0, 5);
muitos.cpp: In function ‘int main()’:
muitos.cpp:10:9: error: too many arguments to function ‘void item(std::string, int, double)’
   10 |     item("caderno", 3, 10.0, 5);
      |     ~~~~^~~~~~~~~~~~~~~~~~~~~~~
muitos.cpp:4:6: note: declared here
    4 | void item(std::string produto, int quantidade, double preco) {
      |      ^~~~

Too many arguments = "argumentos demais". O C++ não "ignora o que sobra" nem "deixa em branco o que falta": a chamada tem de bater com a função. (Dá para tornar um parâmetro opcional, dando a ele um valor padrão: é a aula Valores Padrão.)

No editor do guia, a mesma coisa com outras palavras

O compilador do editor é o Clang. Para a chamada com argumentos de menos, ele escreve error: no matching function for call to 'item' ("nenhuma função compatível para a chamada a ‘item’") e explica numa nota: candidate function not viable: requires 3 arguments, but 2 were provided ("função candidata inviável: exige 3 argumentos, mas 2 foram fornecidos").

Missão: o programa não compila. Rode, leia o erro e descubra quais chamadas estão erradas. O lápis custa 1.5 cada; a mochila, 89.9 (e a Ana vendeu só uma). Corrija as chamadas, sem mexer na função.

#include <iostream>
#include <string>

void item(std::string produto, int quantidade, double preco) {
    std::cout << quantidade << " x " << produto
              << " = R$ " << quantidade * preco << "\n";
}

int main() {
    item("lápis", 2);
    item("mochila", 89.9);
}
#include <iostream>
#include <string>

void item(std::string produto, int quantidade, double preco) {
    std::cout << quantidade << " x " << produto
              << " = R$ " << quantidade * preco << "\n";
}

int main() {
    item("lápis", 2, 1.5);
    item("mochila", 1, 89.9);
}

4. Argumento de outro tipo: a conversão implícita

E se o argumento for de um tipo diferente do parâmetro, mas "parecido"? Por exemplo, um double para um parâmetro int. O C++ tenta converter sozinho o valor para o tipo do parâmetro. Isso se chama conversão implícita (implícita = sem você pedir; a aula Conversão de Tipos explica todas as regras).

#include <iostream>

void mostrarDobro(int x) {
    std::cout << "O dobro de " << x << " é " << x * 2 << "\n";
}

void mostrarMetade(double x) {
    std::cout << "A metade de " << x << " é " << x / 2 << "\n";
}

int main() {
    mostrarDobro(2.9);
    mostrarMetade(7);
    mostrarDobro('A');
    mostrarDobro(true);
}
$ g++ -std=c++23 -Wall -Wextra conv.cpp -o conv       « nenhum aviso!
$ ./conv
O dobro de 2 é 4             « 2.9 virou 2: a parte decimal foi jogada fora
A metade de 7 é 3.5          « 7 virou 7.0: sem perda
O dobro de 65 é 130          « 'A' virou 65, o código da letra A
O dobro de 1 é 2             « true virou 1
ChamadaParâmetroO que chegouPerigo?
mostrarDobro(2.9)int2Sim: a parte decimal é cortada (não arredonda), sem erro e, com -Wall -Wextra, sem aviso no g++.
mostrarMetade(7)double7.0Não: todo int pequeno cabe num double.
mostrarDobro('A')int65Confuso: um char é um número por dentro (Caracteres).
mostrarDobro(true)int1Confuso: true vira 1 e false vira 0 (Booleanos).

A primeira linha é a perigosa: se 2.9 fosse um preço, a Ana perderia 90 centavos em silêncio. Para o g++ avisar, acrescente a opção -Wconversion (avisos de conversão):

$ g++ -std=c++23 -Wall -Wextra -Wconversion conv.cpp -o conv
conv.cpp: In function ‘int main()’:
conv.cpp:12:18: warning: conversion from ‘double’ to ‘int’ changes value from ‘2.8999999999999999e+0’ to ‘2’ [-Wfloat-conversion]
   12 |     mostrarDobro(2.9);
      |                  ^~~

"A conversão de double para int muda o valor de 2.8999… para 2". (O 2.8999999999999999e+0 é o jeito como o computador guarda 2.9 por dentro, quase igual; veja float e double.)

No editor do guia, o aviso aparece sem pedir

O Clang avisa sobre um número escrito com vírgula indo para um int mesmo sem -Wconversion: warning: implicit conversion from 'double' to 'int' changes value from 2.9 to 2 [-Wliteral-conversion]. Mas, se o 2.9 estiver numa variável double, nenhum dos dois compiladores avisa sem -Wconversion. A lição é a mesma: escolha o tipo certo para o parâmetro (preço é double) e passe valores do tipo certo.

Quando não existe conversão: erro

Entre tipos que não têm nada a ver, o C++ não converte. Um texto entre aspas não vira número:

#include <iostream>

void mostrarDobro(int x) {
    std::cout << "O dobro de " << x << " é " << x * 2 << "\n";
}

int main() {
    mostrarDobro("3");
}
$ g++ -std=c++23 -Wall -Wextra texto.cpp -o texto
texto.cpp: In function ‘int main()’:
texto.cpp:8:18: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
    8 |     mostrarDobro("3");
      |                  ^~~
      |                  |
      |                  const char*
texto.cpp:3:23: note: initializing argument 1 of ‘void mostrarDobro(int)’
    3 | void mostrarDobro(int x) {
      |                   ~~~~^

Invalid conversion from ‘const char*’ to ‘int’ = "conversão inválida de texto para int" (const char* é o tipo de um texto entre aspas, como "3"). A nota initializing argument 1 diz que o problema é o 1º argumento. Ignore o [-fpermissive]: é uma opção antiga que transformaria o erro em aviso, e você não quer isso. Para transformar um texto como "3" em número, use std::stoi (Texto ↔ Número).


5. Detalhes que aparecem no dia a dia

Parâmetro que ninguém usa: aviso do -Wextra

#include <iostream>
#include <string>

void saudar(std::string nome) {
    std::cout << "Olá!\n";
}

int main() {
    saudar("Ana");
}
$ g++ -std=c++23 -Wall -Wextra naousado.cpp -o naousado
naousado.cpp: In function ‘void saudar(std::string)’:
naousado.cpp:4:25: warning: unused parameter ‘nome’ [-Wunused-parameter]
    4 | void saudar(std::string nome) {
      |             ~~~~~~~~~~~~^~~~
$ ./naousado
Olá!

"Parâmetro não usado: ‘nome’". O programa funciona, mas o aviso aponta um provável esquecimento: você pediu o nome e não o mostrou. Use o parâmetro ou tire-o da função.

No protótipo, os nomes dos parâmetros são opcionais

Na declaração antecipada (o protótipo, da aula Funções), o compilador só precisa dos tipos. As duas formas valem:

void item(std::string produto, int quantidade, double preco);   // com nomes: mais fácil de ler
void item(std::string, int, double);                            // só os tipos: também vale

Prefira com nomes: quem lê o protótipo entende o que cada valor significa.


Qual tipo usar no parâmetro

A informação é…Tipo do parâmetroExemplo
Uma contagem, uma quantidade, uma idadeintvoid repetir(int vezes)
Dinheiro, medida, nota com casas decimaisdoublevoid cobrar(double valor)
Um textostd::stringvoid saudar(std::string nome)
Uma letra sócharvoid desenhar(char simbolo)
Sim ou nãoboolvoid mostrar(bool detalhado)

Para textos longos e listas (std::vector), existe um jeito de passar sem copiar, o const&, explicado em Por Valor × Por Referência. Nesta aula, a cópia simples basta.


Quando dá errado

error: too few arguments to function

O que significa: a chamada mandou menos valores do que a função tem parâmetros. A mensagem mostra a função completa (‘void item(std::string, int, double)’) e a nota declared here aponta onde ela foi definida.

Como consertar: conte os parâmetros na definição e os argumentos na chamada. Mande o que falta, na ordem certa. Se o parâmetro deveria ser opcional, dê a ele um valor padrão.

error: too many arguments to function

O que significa: a chamada mandou valores a mais. É comum quando você confunde duas funções parecidas ou quando a função mudou e a chamada ficou antiga.

Como consertar: tire o que sobra, ou acrescente o parâmetro na função, se ele for mesmo necessário.

error: could not convert … from ‘int’ to ‘std::string’ ou invalid conversion from ‘const char*’ to ‘int’

O que significa: um argumento é de um tipo que não serve para o parâmetro daquela posição. Muitas vezes os argumentos estão na ordem errada.

Como consertar: compare, posição por posição, a chamada com os tipos que o g++ mostra na mensagem. Se um número está em texto ("3"), converta-o com std::stoi/std::stod (Texto ↔ Número).

O resultado sai sem as casas decimais (2.9 virou 2), sem erro nenhum

O que significa: o parâmetro é int e o argumento é um número com casas decimais. A conversão implícita cortou a parte decimal.

Como consertar: se a informação tem casas decimais, o parâmetro deve ser double. Para pegar esse tipo de engano, compile às vezes com -Wconversion.

error: ‘b’ has not been declared na linha da função

O que significa: um parâmetro está sem tipo, como em void soma(int a, b).

Como consertar: escreva o tipo de cada parâmetro: void soma(int a, int b).


Referência rápida

EscritaO que é
void f(int x)Função com um parâmetro x do tipo int
void f(std::string a, int b, double c)Três parâmetros, separados por vírgula, cada um com seu tipo
f(5);, f(idade);, f(3 + 4);Chamadas com argumentos: valor fixo, variável, expressão
f("Ana", 3, 9.5);Argumentos ligados aos parâmetros pela posição
void f(std::string, int);Protótipo só com os tipos (nomes opcionais)
-WconversionOpção do g++ que avisa conversões que podem perder valor

Exercícios

1. O parâmetro

Complete a definição de uma função que recebe um número inteiro chamado vezes:

void repetir() { … }

2. A chamada

A função é void item(std::string produto, int quantidade, double preco). Complete a chamada para 4 borrachas de 1.75:

item();

3. O que chega

A função é void mostrarDobro(int x). Qual valor de x chega na chamada mostrarDobro(7.8)?

x =

4. Em void saudar(std::string nome) e na chamada saudar("Bruno");, o que é o parâmetro e o que é o argumento?

5. A função é void retangulo(int largura, int altura) e a Ana chamou retangulo(2, 5) querendo largura 5. O que acontece?

6. A função tem 3 parâmetros e a chamada manda 2 argumentos. O que o g++ diz?

Missão final: o boletim da Ana

Missão: a Ana dá aulas de reforço. Crie a função void mostrarNota(std::string aluno, double nota), que mostra o nome, a nota e, entre parênteses, aprovado se a nota for 6 ou mais, ou reprovado se for menor (use um if/else dentro da função). Depois chame-a para os três alunos:

Bruno: nota 7.5 (aprovado)
Carla: nota 4 (reprovado)
Davi: nota 9 (aprovado)
#include <iostream>
#include <string>

// crie a função mostrarNota aqui

int main() {
    // chame a função para Bruno (7.5), Carla (4) e Davi (9)
}
#include <iostream>
#include <string>

void mostrarNota(std::string aluno, double nota) {
    std::cout << aluno << ": nota " << nota;
    if (nota >= 6) {
        std::cout << " (aprovado)\n";
    } else {
        std::cout << " (reprovado)\n";
    }
}

int main() {
    mostrarNota("Bruno", 7.5);
    mostrarNota("Carla", 4);
    mostrarNota("Davi", 9);
}

Resumo

  • Parâmetro = a variável declarada nos parênteses da função (std::string nome). Argumento = o valor enviado na chamada ("Bruno").
  • O parâmetro nasce a cada chamada com o valor do argumento e some quando a função termina. Ele é uma cópia.
  • Vários parâmetros são separados por vírgula, cada um com seu tipo: (std::string produto, int quantidade, double preco).
  • Os argumentos são ligados aos parâmetros pela posição. Trocar a ordem dá erro se os tipos forem diferentes e passa em silêncio se forem iguais.
  • Argumentos de menos ou de mais: too few arguments to function / too many arguments to function.
  • Tipos parecidos são convertidos sozinhos: 2.9 num int vira 2, sem aviso no g++ com -Wall -Wextra. Use o tipo certo, e -Wconversion para ser avisado.
  • Próxima aula: Retorno, para a função devolver um resultado.