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.
- 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 functionetoo many arguments to function. - O que acontece quando o argumento é de outro tipo: a conversão implícita (e por que
2.9pode virar2sem 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ário | Em C++ | Exemplo |
|---|---|---|
| O campo em branco, com rótulo | Parâmetro: escrito na definição da função | void saudar(std::string nome) |
| O que alguém escreveu no campo | Argumento: o valor enviado na chamada | saudar("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,nomevale"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.
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º paraquantidade, o 3º parapreco.
$ 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.)
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
| Chamada | Parâmetro | O que chegou | Perigo? |
|---|---|---|---|
mostrarDobro(2.9) | int | 2 | Sim: a parte decimal é cortada (não arredonda), sem erro e, com -Wall -Wextra, sem aviso no g++. |
mostrarMetade(7) | double | 7.0 | Não: todo int pequeno cabe num double. |
mostrarDobro('A') | int | 65 | Confuso: um char é um número por dentro (Caracteres). |
mostrarDobro(true) | int | 1 | Confuso: 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.)
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âmetro | Exemplo |
|---|---|---|
| Uma contagem, uma quantidade, uma idade | int | void repetir(int vezes) |
| Dinheiro, medida, nota com casas decimais | double | void cobrar(double valor) |
| Um texto | std::string | void saudar(std::string nome) |
| Uma letra só | char | void desenhar(char simbolo) |
| Sim ou não | bool | void 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 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
| Escrita | O 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) |
-Wconversion | Opçã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:
2. A chamada
A função é void item(std::string produto, int quantidade, double preco). Complete a chamada para 4 borrachas de 1.75:
3. O que chega
A função é void mostrarDobro(int x). Qual valor de x chega na chamada mostrarDobro(7.8)?
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.9numintvira2, sem aviso no g++ com-Wall -Wextra. Use o tipo certo, e-Wconversionpara ser avisado. - Próxima aula: Retorno, para a função devolver um resultado.