1. Algoritmos são sequências de instruções para resolver problemas de forma precisa. Programas são algoritmos codificados em linguagens formais.
2. Funções modularizam algoritmos complexos em subprogramas menores com objetivos específicos. Isso traz benefícios como modularidade, reuso de código e legibilidade.
3. Estruturas de dados como vetores, matrizes e registros organizam dados de forma homogênea ou heterogênea. Ponteiros armazenam endereços de memória.
1. 1.1 – Algoritmos: Funções e passagem de parâmetros
Revisando: o que são algoritmos?
Sequênciaprecisade instruções,que quandolidae executadaporoutra pessoa,produzo resultadoesperado,
ou seja, a solução de um problema. Est sequência de instruções é um registro escrito da sequência de passos
necessários que devem ser executadas para manipular dados para se chegar na resposta do problema.
Consideraremososeguinte emrelaçãoaosalgoritmos:1instruçãopor linha;instruçõesserãoexecutadas1por
1, em ordemsequencial;emcadalinha,1 instruçãofazsomente 1 coisa;tudo que estáimplícitodeve serexplicitado.
O que é um programa? Codificação de uma linguagem formal que garante que os passos do algoritmo sejam
executados da maneira esperada por quem executa as instruções. Ou seja, um programa é um algoritmo escrito de
forma tão detalhada quando for necessário para quem executa as instruções. Algoritmos podem ser mais genéricos.
Os programas, não!
Exercício – Escrever 1 algoritmo para trocar 1 pneu de carro.
FUNÇÕES
E se o algoritmo for muito grande e complexo?
Devemosmodularizaroalgoritmocom o intuitode facilitaro trabalhode programadores,diminuindoassim,a
complexidade dele.
Cada algoritmo tem uma complexidade proporcional a complexidade do problema a ser solucionado. Quanto
mais situações a serem tratadas, maior será a complexidade. A complexidade pode ser diminuída, reduzindo-se a
variedade. E a variedade pode ser diminuída, dividindo problemas maiores em problemas menores.
Aí quem entram as funções. Elas são subprogramas (pedaços de programas dentro de programas). Elas tem o
objetivo de resolver um problema específico. Se forem bem exploradas, permitem a construção de códigos bem
elaborados.
Por que devemos utilizar funções?
Modularidade
A noçãode modularidade é relacionadacomacapacidade de se escreverprogramasempedaçosde códigoque
executam operaçõesbem definidas.Cada módulo possui variáveis e estruturas próprias, independentes do restante
do programa. A ideia é que modificaçõesem trechos de código (necessárias para manutenção e continuidade de
desenvolvimento) não causem reflexos no comportamento do resto do programa. É fácil conceber um programa
divididoemtrêspartes:entradade dados,cálculose impressãodosresultados.Umaentradade dadostextual poderia
sermodificadaparaoutra emmodográficosem que opedaçoque faz os cálculospercebaamodificação.Idemparaa
saída de dados.Mesmonoscálculos,pode-semudartodaumaestruturadoprogramae garantirque a entradae saída
vão continuar a se comportar como antes. Isto é tarefa árdua sem o uso de funções e procedimentos.
Reaproveitamento de código
Vezporoutra nos deparamoscomsituaçõesonde temosque escrevercódigosmuito,masmuito,parecidosem
trechosdiferentesdo programa.Asvezesadiferençade umparaoutroé questãode umaououtra variável que muda.
Ocorre frequentemente que o trecho é exatamente o mesmo. Então faz sentido que possamos estruturar o código
repetidode maneiraaconstituirumsubprogramae,no programa propriamente dito,fazerocódigodosubprograma
ser executado para diferentes valores de variáveis. Isto provoca uma grande economia de código escrito, ao mesmo
tempo em que facilita a manutenção do programa.
Se você desejar alterar ou corrigir algo depois, é mais fácil alterar em um único local.
Legibilidade de código
Os dois aspectos acima, somados com o bom uso de nomes apropriados para os identificadores, identação e
usoracional de comentáriosnocódigo,deveriamidealmenteimplicaremumcódigolegível,istoé,compreensívelpara
2. quem o lê e até mesmo para quem o escreveu. De fato, é comum alguém fazer um programa, as vezes simples,e
depois de alguns meses ler o código e não entender o que lá está escrito. Um código legível permite uma rápida
compreensãoe viabilizasuamanutenção,correçãoe expansão,sejapelopróprioprogramadorouporoutraspessoas.
Facilita testar trechos separadamente!
É importante saber o que a função faz (qual o resultado da execução de uma função) e também como se utiliza a
função.
Funções podem ser escritas independentemente uma da outra. Isto significa que, em geral, variáveis usadas
dentro de funções não são compartilhadas pelas outras funções. Assim sendo, o comportamento da função é
previsível.Se nãoforassim, duasfunçõescompletamentenãorelacionadaspodemalterarosdadosuma da outra.Se
as variáveis são locais a uma função, programas grandes passama ser mais fáceis de serem escritos. A comunicação
entre funções passa a ser controlada – elas se comunicam somente através pelos valores passados as funções e os
valores retornados.
A primeiralinhada definiçãoé ocabeçalhoda função.Ela têm trêspartes principais:onome da função,o tipo
do resultado(que é umvalor) que a funçãocomputa e retorna,e entre parêntesesumalistade parâmetros(também
chamado de argumentosformais).Se afunção não retornanenhumvalor,o tipoé chamado de void,e esta palavraé
escritano cabeçalhonafrente donome da função.Se a funçãonão tiverargumentosformais,apalavravoidpode ser
escrita no lugar da lista de argumentos formais entre os parênteses.Para simplificar a exposição, falaremos sobre o
tipodo retorno e os argumentosformaismaistarde.Elesservempara permitirque as funçõestroqueminformações
entre si.
void nome-da-função(void)
{
declarações e senteças (corpo da função)
}
O primeirovoid significaque estafunçãonãotemtipode retorno(nãoretornaum valor),e o segundosignifica
que a função não tem argumentos (ela não precisa de nenhuma informação externa para ser executada). Isso não
significaque a função não faz nada. Ela pode realizaralgumaação, como imprimirumamensagem.A ordemem que
as funções são definidas dentro do código-fonte é importante, pois uma função deve sempre ser definida antes da
função em que ela é chamada.
O exemplo abaixo mostra um programa que usa uma função como essa:
#include <iostream>
using namespace std;
// DEFINIÇÃO da função alo()
void alo(void)
{
cout << "Alo." << endl;
}
// Programa Principal
int main()
{
alo();
}
Argumentos Reais e Formais
Note que há uma correspondênciaentre a quantidade,aordeme tipodos valoresque main() passa(estessão
chamadosde parâmetrosreaisouargumentosreais) e osargumentoslistadosnocabeçalhodafunçãocumprimenta()
(denominados argumentos formais).
3. PASSAGEMPOR VALOR E POR REFERÊNCIA
Lembrando que para cada variável está associado: um nome, um tipo, um valor e um endereço!
PassagemPorValor1 – passagem por valor
PassagemPorValor2 – mostra que os valores não são trocados
PassagemPorValor3 – Passagem por referência
Se a funçãoprecisaalteraro valorde umavariável nopontode chamadada função,entãopassamosonome da
variável como parâmetro real, e escrevemos a função indicando os parâmetros como sendo de SAÍDA o PASSADOS
POR REFERÊNCIA.
4. 1.2 – ESTRUTURA DE DADOS: HOMOGÊNEA, HETEROGÊNEA E PONTEIRO
VETORES(Arrays):é umacoleçãode 1oumaisobjetos,domesmotipo,armazenadosemendereçosde memória
adjacente.
OBS: QUANDO DEFINIMOSUM ARRAYCOMO ARGUMENTO FORMAL, ALTERAÇÕESNO ARRAY FEITASDENTRO
DA FUNÇÃO ALTERAM O CONTEÚDO DO ARRAY PASSADO COMO PARÂMETRO REAL NA CHAMDA DA FUNÇÃO. EM
OUTRAS PALAVRAS, QUANDO SÃO PARÂMETROS DE FUNÇÕES, ARRAYS SÃO PASSADOS POR REFERÊNCIA (sem a
necessidade do & na definição do parâmetro formal, como foi visto anteriormente).
MATRIZES: Coleçãode variáveisdomesmotipo,acessíveiscomumúniconome e armazenadoscontiguamentena
memória.A individualizaçãode cadavariável de umvetoré feitaatravésdo usode índices.
Vetoressãoposiçõesde memóriaidentificadasporummesmonome,individualizadasporíndicese cujoconteúdoé
do mesmotipo.
REGISTROS: Uma estrutura é uma coleção de uma ou mais variáveis, possivelmente de tipos diferentes, colocadas
juntas sob um único nome para manipulação conveniente.Por exemplo, para representar um aluno são necessárias
as informaçõesnome,matrícula,conceito.Aoinvésde criartrêsvariáveis,é possível criarumaúnicavariável contendo
três campos. Em C/C++, usa-se a construção struct para representar esse tipo de dado.
A definição de uma estrutura não cria uma variável.
Define umtipode dados (estrutura):informaaocompiladoronome,otamanhoembytese a maneiracomo eladeve
ser armazenada e recuperada da memória. Não reserva memória.
A estrutura é um tipo de dado cujo formato é definido pelo programador.
PONTEIROS:
Um ponteiroé umendereçode umobjetode dadode umtipoparticular.Emoutraspalavras, ponteirosnadamaissão
do que variáveis cujos valores são endereços de memória. A sintaxe de declaração de ponteiros é a seguinte:
<tipo_variavel> * <nome_ponteiro>;
Em algumassituações(veremosexemplosemseguida)queremossaberoendereçode umelementonamemória.Para
lidarcomendereçosemcódigosC++, precisamosde variáveisde umtipodenominadoponteiro.Umavariávelponteiro
é capaz de armazenar um endereço de memória. Ao especificar um ponteiro, precisamos especificar qual o tipo de
elemento que deve estar no endereço que será armazenado nessa variável. Isto é necessário para podermos saber
como operador com um endereço, visto que a posição de memória nesse endereço poderia conter qualquer tipo de
dados. A especificação de variáveis ponteiros é como segue: int *p;
3.1 Operador de endereço &
Uma forma de conseguir um ponteiro quando já temos acesso a um elemento na
memória é usar o operador &, ou operador de endereçamento. Podemos conseguir
o endereço de uma posição de memória colocando esse operador à esquerda do
elemento para o qual queremos o endereço. Vejamos alguns exemplos.
int a{2};
int *p; // Esta variável é um ponteiro para int
p = &a; // p tem o endereço da variável a
Após a execução do código acima, dizemos que p aponta para a.
3.2 Operador de acesso por ponteiro *
Uma vez que temos um ponteiroapontando para um elemento na memória, certamente iremosquerer fazer algum
acessoa esse elemento.Issopode ser feitocomousodo operadorunário* (não confundircomo operadorbinário *
usado para multiplicação):
std::vector<int> alguns_primos{2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
int *p = &alguns_primos[2];
int dez = *p * 2;
Também podemos alterar o valor apontado:
double x{0.0};
double *px{&x};
*px = 1.3; // Agora x vale 1.3