E aí, pessoal? Aqui é o Caio do canal Froncaio. Sejam todos bem-vindos ao nosso curso completo de programação C. Primeiramente, quero informar a vocês que nós vamos começar o nosso curso do básico e gradualmente vamos migrando para assuntos mais complexos. O principal objetivo desse curso é te ensinar a programar com proeficiência na linguagem C. E para solidificar o nosso conhecimento, nós vamos desenvolver diversos programas ao decorrer do seu aprendizado. Se assim como eu, você também prefere aprender colocando a mão na massa e não apenas assistindo em inúmeros slides, você vai gostar bastante. Quanto aos pré-requisitos
necessários, minha expectativa é somente que você tenha concluído o ensino fundamental e médio. Apesar de você conseguir realizar o curso sem esse conhecimento escolar, ele é essencial para facilitar sua compreensão. Se você já esqueceu de quase tudo, te aconselho a acompanhar, mas utilizar a internet para revisitar tópicos que não entender. Fique tranquilo, pois todos os tópicos relacionados ao ensino básico serão sempre mencionados com seus nomes, para que caso você encontra a necessidade, possa procurar por eles de maneira independente. Quanto à estrutura do nosso curso, ela é baseada no livro de André Bax, linguagem C, completa
e descomplicada, mas não há nenhuma necessidade que você tenha acesso ao livro. Somente com o conteúdo aqui ministrado, os arquivos aqui disponibilizados, já vai ser suficiente para você sair aí do zero ao domínio. Aqui eu não vou entrar em detalh sobre todos os tópicos que serão abordados, porque para aqueles que estão aprendendo a sua primeira linguagem de programação, Muitos deles podem não fazer sentido, mas fiquem tranquilos, pois todos eles serão abordados com extremo detalhe. Vamos utilizar o sistema operacional Windows no decorrer do nosso curso. E se você não sabia, existem diferentes sistemas operacionais. Caso essa
seja a primeira vez que você está se deparando com essa informação, não se preocupe, pois a chance de você estar utilizando alguma das versões do sistema operacional Windows é grande. No caso de diferenças relevantes, eu vou estar sempre mencionando o que deve ser feito no Mac e no Linux. E se você está utilizando algum desses sistemas, você pode acompanhar o nosso curso. Eu vou listar sempre tudo que você vai precisar para realizar o mesmo na sua máquina. Dito tudo isso, chega de enrolação e vamos começar. A primeira coisa que eu gostaria que vocês fizessem é
clicar na barra de pesquisar do sistema operacional Windows e digitar Power Shell. Você pode tanto clicar em cima quanto apertar enter para executar esse terminal. E a gente vai se deparar com essa tela aqui. Para facilitar a compreensão de vocês, eu vou omitir algumas informações, mas eu gostaria que você imaginasse que todos os programas do seu computador eles rodam dentro do terminal. Sim, isso mesmo. Acontece que todos os programas eles podem ser acessados, né, pelo terminal, apesar de não ser uma prática muito comum, de não ser uma interface muito amigável, da mesma forma que nós
podemos clicar aqui no explorador de arquivos para que a gente possa navegar entre as nossas pastas, nós também podemos executar o explorador de arquivo através Do terminal, digitando o comando explorer. Repare, agora nós temos aqui o nosso explorador de arquivos. O mesmo pode ser feito para calculadora, a gente pode sim clicar no menu iniciar, procurar a nossa calculadora ou podemos simplesmente executar o comando calc e aí a calculadora vai abrir ou o nosso bloco de notas. Uma outra coisa que a gente pode tirar de informação, observar essa janela é que ela sempre mostra em qual
pasta nós estamos. A gente poderia estar abrindo essa pasta aqui, vindo em C2 pontos. Repare dois pontos aqui. Usuários front tutorial. E a gente vai estar localizado na mesma pasta que nós estamos no nosso terminal. Se utilizarmos o comando clear, limpamos o nosso terminal todos os comandos e as respostas que foram obtidas ali dentro. Uma outra coisa que eu gostaria que vocês tomassem ciência é que nem todos os programas eles executam dentro de uma interface gráfica. Por exemplo, eu posso aqui, aí lembre-se que a gente estava utilizando comandos que executavam programas e eles estavam abrindo
janelas. Mas eu posso utilizar aqui um comando também, né, que ele vai executar um programa que vai listar para mim todas as pastas e arquivos contidos Dentro do diretório que eu estou atualmente, que é o LS. E ao apertar enter, observe, ele me deu uma resposta dentro do próprio terminal. Alguns programas eles não possuem interface gráfica porque não é necessário que eles possuam. Eles são feitos para serem executados em um ambiente que não possui essa interface gráfica. E na nossa jornada, como começaremos fazendo programas simples, faremos eles para que executem somente dentro do terminal, sem
uma interface gráfica. Bom, para tirar isso da frente e matar essa dúvida que pode ter aparecido, você pode, ao clicar na sua barra de pesquisa do Windows, digitar o comando cmd ou prompt de comando e executar um outro terminal. Pessoal, esse daqui não é o mesmo terminal Windows Power Shell. O cmd ou prompt de comando, ele é um terminal antigo, tá? O que que acontece? O sistema operacional Windows, diferente do Linux, ele é um sistema retrocompatível. Ele consegue executar programas de versões antigas do Windows, ou seja, Windows XP, Windows 7. E a Microsoft, por manter
essa retrocompatibilidade, se vê obrigada a manter também o promp de comando, já que alguns dos seus programas só executam dentro deste terminal. Mas o novo terminal do Windows, que já vem sendo utilizado algumas versões, é o Windows Power Shell. Qual que é a diferença? entre esses dois terminais, além de um e ser Mais atualizado. Pessoal, existe um padrão chamado POS. Todos os comandos que a gente utilizou aqui no Windows Poersell, você vai conseguir estar utilizando no Mac e no Linux, mas esses comandos não vão funcionar dentro do prompt de comando. Por exemplo, aqui nós usamos
o comando ls para listar todas as pastas e arquivos dentro do diretório que nós estamos. Observe que no prompt de comando nós estamos no mesmo diretório e eu vou tentar utilizar o comando ls. Ele não é reconhecido e ele me retorna este erro. Só para esclarecer para vocês. Sim, existem dois terminais dentro do Windows, o Prompt de Comando e o Windows Power Shell. E caso você esteja utilizando o Linux ou Mac, você terá somente um terminal. Mencionei também aqui que os comandos que nós utilizamos, você consegue estar utilizando nesses três sistemas operacionais. Isso acontece por
causa do padrão POS. O padrão Proix de sistema operacional, ele só é seguido no Mac, mas o Linux é muito marcado por seguir algumas das orientações desse padrão. E o Windows Power Shell também começou a aderir parcialmente. Agora podemos fechar esses dois terminais. E a última coisa que eu gostaria de mostrar para vocês sobre esse assunto envolve emuladores de terminais. A gente vai ir na Microsoft Store e vamos procurar dentro da loja por Windows Terminal. Nas versões do Windows 11 mais recentes, ele já vem instalado. Se você estiver no Windows 10 em versões mais antigas,
ele Pode não estar instalado. Basta clicar em instalar. Por aqui mesmo nós podemos abrir o Windows Terminal ou podemos também ir na barra de pesquisar editar o Windows Terminal. Fique tranquilo, pois isso aqui é muito simples. Eu só quero não deixar brechas paraa dúvida. Acontece que você agora pode estar se perguntando, então eu tenho três terminais na minha máquina. Eu tenho o Power Shell, eu tenho o CMD. Vamos abrir aqui o Power Shell, vamos abrir aqui o CMD. E eu ainda tenho o Windows Terminal. Pessoal, não é isso que está acontecendo, tá? Dentro do sistema
operacional Windows, você só possui dois terminais, o Windows Power Shell e o Prompt de Comando. Existem emuladores de terminal, eu gostaria que você imaginasse eles como cascas, ou seja, janelas e interfaces para que a gente veja os nossos outros terminais. Observe que no Windows terminal que nós instalamos pela loja, a gente agora consegue ter janelas. A gente consegue abrir aqui dentro o prompt de comando e a gente também consegue abrir po shell. Não se preocupe que esse azul cloud shell, por enquanto nós vamos ignorar essa informação. Isso daqui é só para que você entenda que
existem emuladores de terminais e terminais. Se você estiver no Linux, vai depender da distribuição que você está utilizando. Se você estiver utilizando o Ubunto, o emulador de terminal padrão é o Gnom Terminal. Se você estiver no Mac, você Também pode instalar outros emuladores de terminais, como Alarcrit, por exemplo. No decorrer do nosso curso, nós vamos utilizar somente Windows Power Shell através do emulador de terminal Windows Terminal. OK? Já estamos quase terminando essa parte de instalação. Prometo para vocês que agora vai ser bem rapidinho. A gente vai abrir o nosso navegador aqui. Recomendo que vocês utilizem
o mesmo buscador que eu, no caso vou estar utilizando aqui o Google. E agora nós vamos procurar aqui por chocolate. Pessoal, do que se trata isso daqui? Tá? Isso aqui é um gerenciador de pacotes. Eu quero que vocês imaginem como se nós agora estivéssemos instalando uma loja, OK? A gente vai entrar aqui no nosso site oficial chocolate.org e a gente vai clicar aqui em instalar. Daqui a pouquinho vocês vão entender melhor porque a gente precisa fazer isso. Aqui nós podemos ver os requerimentos. a gente precisa eh estar num num sistema operacional Windows em alguma das
suas versões mais recentes. A gente precisa que o nosso terminal PO Shell esteja no mínimo na versão V2 ou V3. E para descobrir isso, nós podemos aqui no nosso terminal utilizar o seguinte com PS version table. Nós observamos que eu estou utilizando a versão 5.1. Você provavelmente vai estar assistindo esse vídeo em uma versão mais nova, mas caso você esteja utilizando uma versão mais Antiga, você não terá nenhum problema, desde que ela seja maior do que a versão três. Uma outra coisa que nós precisamos nos certificar antes de continuar com a nossa instalação é que
a gente tem a versão do Dnet 4.8. Como é que a gente pode fazer isso? A gente pode ir na nossa barra de pesquisar e procurar por ativar. ou desativar recursos do Windows. Essa janela vai abrir e [roncando] aqui nós verificamos que a gente tem o DNET versão 4.8. Caso você não encontre isso, é necessário que você instale o DNET 4.8. Feito isso, a gente pode então instalar o nosso gerenciador de pacotes. É muito simples, pessoal. Primeira coisa que nós precisamos fazer é executar o nosso PShell com a permissão de administrador. Vou digitar na barra
de pesquisar poell executar como administrador. Sim. E a partir de agora o nosso terminal já está executado como administrador. Bom, aqui a gente precisa, só para verificar se a gente já tem a permissão correta, executar o comando get execution policy. E aqui a gente vê que eu estou restrito para modificar isso daí. e o seu esteja restrito como o meu. Basta você executar o comando set execution policy all sign. E aí a gente vai digitar aqui y caso que está em português, S. Vamos verificar agora Novamente utilizando o comando get execution policy. E agora a
gente já recebe o retorno que a gente esperava, ó. Sign. Feito isso, a gente vai copiar esse comando e dar enter. Agora basta aguardar até o final da instalação. Finalizada a nossa instalação, a gente vai perceber que a gente já retornou uma mensagem chocolate CLI. Lembrando que CLI é a sigla para command line interface, ou seja, interface de comando de linha. está pronto. Ele está me informando que eu posso chamar o comando choco dentro do meu terminal, mas que é preciso que eu reinicie o meu terminal. Então, vou fechar o meu terminal, vou abrir o
meu terminal agora usando o Windows Terminal, o nosso emulador de terminal, e vou digitar o comando choco. Ele já mostra para mim que a gente está com a versão do chocolate e versão 2.3.0. Até então o que nós fizemos foi instalar o que eu disse para vocês, que é um gerenciador de pacotes e pedir para que interpretassem isso como uma loja. A partir desse comando choco que a gente acabou de instalar, a gente vai conseguir baixar programas dessa loja e essa loja vai fazer tudo o que, por exemplo, a Microsoft Store faz pra gente atualizar
esses programas. Porém, esta loja que esse gerenciador de pacote, o Choco, ele não possui uma interface gráfica, pessoal. É claro que a gente poderia ter Instalado o choco através de um processo normal que você estaria utilizando aí em uma máquina convencional. Porém, existem inúmeras vantagens de estar utilizando a instalação via CLI. Uma delas é que imagine que você tivesse que instalar isso em diversas máquinas. Bom, foi só um conjunto de comandos que a gente teve que utilizar. Futuramente a gente poderia automatizar isso e muitas das vezes instalar coisas pelo terminal vai ser mais rápido do
que instalar através da utilização de uma interface gráfica. OK? Nossa loja agora está instalada. O que mais a gente precisa fazer é executar novamente o Power Shell com a permissão de administrador e instalar o Min GW utilizando a loja Choco. Para fazer isso, a gente vai utilizar o comando choco, install min e GW. Lembrando que você precisa da permissão de administrador para instalar programas no Windows. Digite S. E agora basta aguardar. Aqui a gente já recebeu uma mensagem que a instalação do MinW aconteceu de maneira correta, mas para verificar se tá tudo funcionando na sua
máquina, é necessário que a gente esteja reiniciando o nosso shell. Vamos fechar e abrir novamente. Ao abrir um novo shell, a gente pode digitar o comando GCC traço traço Version. Se você receber alguma mensagem de erro, é indicativo que o seu processo de instalação houve algum problema. Se você receber alguma mensagem semelhante a que está aparecendo aqui no vídeo, Isso quer dizer que a partir de agora a gente já tem tudo que é necessário para criar códigos na linguagem C. Se você estiver utilizando o Mac ou Linux, o processo de instalação vai ser diferente, tá?
E provavelmente você nem vai precisar realizar esse processo de instalação. Utilize o comando GTC traço traço Version no seu terminal e verifique se já está instalado ou não. Caso não esteja, a instalação no Linux é muito mais fácil. Dentro da loja padrão do Mac e do Linux você já consegue instalar o GCC. E basicamente, pessoal, a gente poderia seguir o nosso curso inteiro utilizando somente um editor de texto como bloco de notas. e o terminal com os programas que nós já instalamos no nosso terminal. Isso já seria suficiente para que a gente pudesse acompanhar todo
o nosso curso. Mas acontece que a gente não vai fazer isso. A gente vai utilizar uma IDE. E o que que é uma IDE, pessoal? Uma IDE é uma interface de desenvolvimento. É um programa que oferece um editor de texto e também oferece um emulador de terminal, além de outras coisitas a mais. E o que nós vamos estar utilizando vai ser o Visual Studio Code. Você pode utilizar aqui o mesmo buscador que eu, no caso, vou estar utilizando o Google, e procurar por Visual Studio Code. É importante que você se lembre do termo code, não
confunda com Visual Studio. Ao Acessar a página, a gente clica em download e faz o download pro nosso sistema operacional, que no caso é o Windows. Ao clicar aqui, o download vai iniciar. Eu já baixei esse arquivo. Vamos fazer a instalação aqui em conjunto. Basta executar o instalador, concordar com os termos de uso. Próximo. Próximo. Eu vou adicionar a opção de ao clicar com o botão direito e ir em mais opções, ele me dá a opção de abrir o arquivo com o Visual Studio Code. E também vou clicar na opção de criar um atalho na
área de trabalho. Instalar. Agora basta aguardar. Vamos clicar em concluir e vamos ver essa nossa IDE. Ao abrir pela primeira vez, pede que a gente escolha um tema. Eu vou escolher o tema dark padrão mesmo. Muito bem, muito bem. Pessoal, fechei aquelas janelas que estavam abertas aqui. Pessoal, queria, primeiramente, tranquilizar a todos vocês. Caso você não saiba como utilizar o Visual Studio Code, fique tranquilo que no decorrer do curso tenho certeza que você vai entender. Bom, a primeira coisa que eu recomendo que você faça nesse início, né, e crie uma pasta para você, para que
a gente coloque os arquivos que nós vamos desenvolver em conjunto. Aqui eu criei uma pasta chamado Froncilio Corse. Cliquei no na área de trabalho com botão direito, novo pasta e dei esse nome aqui. Agora a gente vai vir aqui em file, open folder e vamos abrir esta pasta. Feito isso, a gente vai clicar aqui no Explorer e aqui a gente consegue criar arquivos que vão estar dentro da nossa pasta. Também comentei com vocês que o Visual Studio Code ele não era só um editor de texto, mas ele também possuía um emulador de terminal. Para que
a gente possa estar abrindo esse nosso emulador de terminal, vou estar colocando aqui em tela cheia. A gente vai ver a opção terminal e a gente vê aqui o atalho para abrir um novo terminal. Esse emulador de terminal está executando o terminal Power Shell e a gente consegue verificar isso aqui. Caso a gente queira trocar para o cmd, a gente consegue clicar em CMD. E agora a gente tem esses nossos dois terminais aqui abertos. Tudo bem? Por enquanto isso vai ser suficiente para que a gente já possa dar o nosso primeiro passo. Muito bem. Muito
bem. Agora podemos fazer o nosso primeiro programa. Só um parêntesezinho extremamente rápido. Caso você já utilize o Visual Studio Code, já programe uma outra linguagem, fique tranquilo porque no decorrer do curso vou mostrar as extensões que eu utilizo, tá? E vou mostrar como instalá-las. Mas vamos lá criar o nosso primeiro código. A gente vai vir aqui, clicar com o botão direito, criar um novo arquivo, main.c. O nome do arquivo fica a critério de vocês, desde que o tipo do arquivo seja ponto C, tá bom? E aqui nós vamos dar #clude stdio. Aumentar o tamanho para
facilitar a visualização de vocês. Nós vamos agora saltar uma linha. Eu posso saltar duas, três ou mais. Vamos soltar uma linha. Vou utilizar o termo void. Não se preocupem, a gente vai explicar em detalhe o que que é isso. Main parênteses. Aqui a gente pode dar espaço ou não, tá? Chaves também eu posso dar espaço entre as chaves e os parênteses ou não. Print f parênteses aspas. E aqui a gente pode colocar a nossa mensagem para ser impressa no nosso terminal. minha primeira mensagem no terminal. OK? E para finalizar, ao terminar de chamar essa função
print f, a gente vai colocar o ponto e vírgula. A gente pode estar apertando contrl S para salvar, vindo em file, save, ou apertando a tecla de atalho contrl S. E agora a gente consegue compilar este programa que nós escrevemos para que a gente depois possa executá-lo e mostrar para vocês aqui o Processo. GCC, o nome do nosso arquivo que contém o nosso código, main.c, traço e o nome do nosso arquivo executável. Eu vou chamar ele de executável. Bom, não retornou nenhum erro, não de não deu nenhuma mensagem, mas a gente pode ver que a
partir de agora a gente criou um arquivo executável. Tá? Vamos tentar executar esse arquivo. Para fazer isso, a gente vai utilizar o ponto barra executável. No Windows, a barra é a invertida, tá, pessoal? Ponto barra executável. E vamos dar enter. Olha só, o nosso programa que imprime a mensagem no terminal acabou de funcionar. É claro, pessoal, que surgem várias dúvidas, né? Do que se trata esse include, o que que é essa esse void main? Por o que que é esse ponto e vírgula, tá? O que que tá acontecendo? O comando GCC, né? O que é
esse arquivo executável? E evidentemente nós vamos explicar isso em detalhes. E a primeira coisa que a gente vai entender um pouco melhor aí sobre o seu funcionamento é o compilador. Pessoal, observem, né, que a gente tinha um arquivo main.c e a gente precisou transformar ele em um arquivo executável para que então a nossa máquina pudesse fazer aquilo que a gente comandou ela fazer, que era imprimir uma mensagem no terminal. Eu Trouxe uma imagem aqui para ilustrar um pouco desse processo. Imagine que aqui à esquerda nós temos um arquivo main.c, tá? E a gente precisa transformar
ele em um arquivo final é ponto exe. Por que que é necessário fazer isso, pessoal? O seu processador, a sua máquina, ela não consegue entender arquivos que contenham textos. Ela só consegue entender arquivos desse tipo aqui, que são uma sequência de instruções em números binários. E o compilador ele serve exatamente para fazer isso. É importante a gente entender esse processo porque isso faz com que a gente compreenda todas as nossas limitações. Por que que nós não podemos escrever o nosso código de qualquer maneira? Porque o nosso compilador vai identificar um erro e não vai conseguir
gerar um arquivo executável. Portanto, o que determina quais regras nós devemos ou não seguir é exatamente o nosso compilador. Eu vou mostrar isso daqui para vocês agora na prática. Bom, por que que tem esse ponto e vírgula? Vamos tentar retirar ele e gerar um arquivo executável para ver o que acontece. O compilador agora nos dá um retorno informando que não foi possível gerar o arquivo executável e informa qual o erro está ocorrendo e traz pra gente uma sugestão da localização deste erro. Lembre-se, pessoal, que os programas executados no terminal não necessariamente possuem uma interface gráfica.
Eu poderia vir aqui no nosso terminal, digitar explorer e ele abriria o nosso explorador de arquivos. Só que eu posso também utilizar o comando ls, que lista os arquivos da nossa pasta e ele não vai abrir nenhuma interface gráfica. O nosso compilador é um programa CLI, ou seja, command line interface. um programa que funciona na interface de comando de linha. E agora eu quero mostrar para vocês um pouquinho melhor sobre esse programa, né, o compilador e como a gente pode usar ele. Observem que nós utilizamos aqui o nome do nosso compilador GCC, informamos o nome
do nosso arquivo main.c C e através da flag ou do parâmetro traço O indicamos que nós queremos gerar um arquivo pz com o nome executável. Vamos então fazer a correção aqui e realizar esse processo novamente. Apertei contrl S para salvar o nosso arquivo gcc main.c C traço O executável 2. Ele gera o arquivo pon de nome executável 2. Eu poderia utilizar o nome executável e aí ele vai sobreescrever este arquivo aqui caso ele já exista. Tudo bem? Uma última informação que eu gostaria de trazer para vocês acerca do compilador é que, claro, pessoal, nessa imagem
aqui a gente tá omitindo várias informações. O nosso programa compilador, apesar dele não possuir uma interface gráfica, ele está realizando várias etapas, como análise sintática, ou seja, se as coisas Do nosso código estão posicionadas de maneira que elas façam sentido e que permitam criar esse arquivo executável. análise semântica, se o que foi escrito está escrito de maneira correta, dentre inúmeras outras coisas. E eu não vou entrar em detalhe nesse momento acerca desse processo, de todas as etapas que um compilador executa, mas existe uma etapa que eu considero muito relevante de ser mencionada, porque caso contrário
pode levar vocês a terem dúvidas. E é a seguinte, nesse processo de compilação, o nosso programa GCC, ele vai adicionar ao nosso código instruções complementares do sistema operacional. Caso você não saiba, todas as peças que estão conectadas a seu computador, como o seu monitor, a sua placa de vídeo, o seu HD, elas são controladas pelo seu sistema operacional, neste caso aqui, o Windows. Isso quer dizer que eu não posso fazer um programa que simplesmente acessa o monitor e imprime coisas. Eu preciso pedir para o sistema operacional, ele vai funcionar como intermediador para que ele imprima
para mim. Então, é como se fosse o pedido sistema operacional imprime para mim. E cada um desses sistemas operacionais, ele vai ter um conjunto de regras sobre como esse programa deve executar esse pedido. Na prática, o que isso quer dizer? Que se você escrever um arquivo main.c, C compilar gerar um arquivo pze. Você só vai conseguir executar no sistema operacional que você está utilizando. Você não consegue pegar esse arquivo ponxe e utilizar no Linux. Talvez alguns de vocês estejam se perguntando: "Poxa, mas eu já sei que se é uma linguagem que pode produzir programas que
funcionem em vários sistemas operacionais. Afinal de contas, se foi inclusive utilizado para criar esses sistemas operacionais. E você está correto? Porém, observem que não existe nenhum arquivo que você consegue utilizar para instalar tanto no Windows, quanto no Linux, quanto no Mac. Por esse motivo, quando a gente acessa um site para fazer um download, eles dão sempre as opções, a instalador do Windows, instalador do Mac, instalador do Linux. Isso quer dizer que a gente pode fazer o nosso programa Main.c e a gente compila ele para cada um desses sistemas operacionais. Então aqui utilizando o comando que
eu mostrei para vocês, a gente tá compilando ele para o Windows, OK? E agora que a gente já entendeu o nosso processo de compilação, eu quero explicar um pouco melhor o nosso código. Caso você tenha dificuldade de entender essa parte da explicação, fique tranquilo, pois tudo que eu disser aqui nós discutiremos diversas vezes no decorrer do curso. E o que realmente é necessário que você compreenda é que por algum tempo os programas que iremos escrever terão sempre essa mesma estrutura, só mudando o que está dentro dos colchetes. Dito isso, aqui vai uma explicação simplificada sobre
o que está acontecendo aqui dentro e porque que a gente precisa escrever essas linhas de Código. A primeira coisa que vocês precisam entender é que o nosso compilador, ou seja, o comando GCC, que transforma um arquivo pc em um arquivo executável, ele consegue identificar determinadas instruções. A gente não pode sair escrevendo qualquer coisa porque ele não vai conseguir saber do que se trata, mas existem um conjunto de palavras que ele entende e sabe do que se tratam. Por isso que a gente fala que ao aprender a programar você aprende uma linguagem. Você não aprende a
se comunicar em zero e um, mas você aprende uma linguagem que um compilador é capaz de entender e gerar um código de máquina. Acontece que o print f, pessoal, ele não faz parte dos comandos que o nosso compilador conhece. Por esse motivo, a gente precisa utilizar o hashagincluddio. STIO vem de standard input and output, que traduzindo significa padrão de entrada e saída. Essa é uma biblioteca que traz alguns comandos para que a gente possa enviar mensagens do nosso terminal e receber mensagens do nosso terminal. Todos os comandos que forem de alguma biblioteca, eles serão mencionados
aqui quando forem introduzidos para vocês. E todos os demais você já pode entender que são comandos padrão que o nosso compilador já é capaz de interpretar. Quando você ouvir as palavras módulo ou biblioteca, saiba que a gente tá se referindo a esse Comando include, que inclui um módulo ou biblioteca. Eu utilizei também o termo função. E aqui é importante, pois o nosso compilador ele executa todo o código que está dentro da função main. Por isso, a nossa função não pode ter outro nome, ela precisa ser chamada como main. Entendam que na hora de transformar isso
aqui em um arquivo de máquina, o nosso compilador vai sempre procurar pela função main para a partir das instruções que estão dentro desta função gerar o nosso arquivo executável. E agora uma última coisinha. Ao final de toda a sentença que nós vamos escrever inicialmente somente dentro da função main, a gente deve finalizar ela com ponto e vírgula. Eu sei que é bastante informação, mas vamos tentar dar uma olhada nisso no código. Se eu tirar o ponto e vírgula, salvar o meu arquivo e tentar compilar, aqui eu apertei para cima no terminal, tá pessoal? Se você
entrar no terminal e apertar para cima, ele vai listando os comandos anteriores a partir do histórico de comandos que você usou. O que que vai acontecer? A gente não vai gerar o nosso arquivo executável. Toda vez que a gente tem um erro, a gente não gera esse arquivo. Vou deletar aqui para você ver que ele não tá sendo sobrescrito e sim não está gerando o arquivo executável. Executei novamente o comando e não gerou o nosso arquivo. Se a gente corrigir adicionando o ponto e vírgula, vai compilar corretamente como da última vez. Uma outra coisa que
eu quero mostrar para vocês é que se a Gente remover a inclusão da biblioteca STDIO, o que você acha que vai acontecer? Spoiler. O que você achou? provavelmente tá errado. Vamos fazer esse teste aqui, pessoal. Olha só, a gente já percebe que um arquivo executável foi criado, mas nós obtivemos um aviso. Existe uma diferença entre o warning e erro, pessoal. Quando o compilador encontra um erro, ele não gera o arquivo executável. Porém, no caso da não inclusão da biblioteca STDIO, o compilador automaticamente incluiu esta biblioteca para a gente e emitiu um aviso informando que nós
não adicionamos a biblioteca STDIO. Acontece que assim como os programas que você utiliza na sua máquina recebem atualizações, os compiladores também ganham novas versões. E nas versões mais recentes, ele automaticamente inclui a biblioteca STDIO, gera o arquivo executável e emite um aviso. Porém, pessoal, isso só vai acontecer para bibliotecas padrão e ainda vai gerar esse aviso. Portanto, vamos tratar como de uso obrigatório a inclusão da biblioteca. STIO todas as vezes que vamos utilizar o print F. Muito bem. Muito bem. Vamos agora salvar esse arquivo que a gente criou. Vou copiar esse código aqui. Vou colar
nesse nosso arquivo. Agora vamos fazer um novo programa. Se você quiser, você pode reaproveitar essa estrutura. Eu, particularmente, quando estou aprendendo uma nova linguagem, gosto de reescrever do zero para poder fixar melhor a sintaxe dessa linguagem. Então, include stiu. Voidman parênteses chaves. Pessoal, ao programar a gente sempre vai precisar realizar algumas operações básicas da matemática. Seja lá qual for a finalidade do seu programa, você vai eventualmente utilizar conhecimentos matemáticos. Esteja você fazendo um programa para controle de caixa de uma loja de imóveis, um programa para analisar dados de um perfil, quantas pessoas visitaram um perfil,
um jogo, provavelmente ele vai possuir pontos de vidas, habilidades que você possui ou não possui. E por esse motivo, o nosso próximo passo nesse momento é tentar fazer um programa que realize uma operação básica de adição. Então, vamos lá. A gente vai utilizar aqui o comando int. Vamos criar a variável a = 1. Fique tranquilo que a gente vai discutir isso daqui em detalhes daqui a pouquinho. Int = 2, int = a + b. E depois vamos imprimir no nosso terminal utilizando o comando print f. E aqui nós vamos utilizar o símbolo Porcentagem de uma
vírgula depois das aspas C. Já já a gente vai ver isso aqui com bastante atenção. Bom, pessoal, se tudo ocorrer como a gente gostaria, estamos somando 1 + 2, portanto, deveríamos imprimir na nossa tela 3. Vamos salvar, compilar o nosso código. GCC main.c traço o executável. O nosso programa foi compilado. Agora vamos executá-lo para ver se a gente recebe o resultado que a gente esperava, que seria a impressão do número três no nosso terminal. Foi exatamente isso que aconteceu. Acontece que agora acabei introduzindo alguns comandos novos, como a declaração de uma variável feita pelo comando
int e o nome dessa variável. A gente vai ter que entender melhor do que se trata uma variável. a operação de atribuição, que nós podemos observar ela acontecendo através do símbolo igual, assim como um novo jeito de imprimir na tela essas variáveis que criamos. Bom, cada linha finalizada com ponto e vírgula, nós vamos chamar de uma sentença. Observe que aqui nós temos uma, duas, 3, 4 sentenças. Fiquem atentos para não confundirem. Não é porque nós temos duas linhas que nós teremos duas sentenças. Aqui vai um exemplo. Observe que na linha seis e s nós temos
somente uma sentença, pois toda sentença é finalizada com ponto e vírgula. Separar o nosso código dessa Forma depois de um operador não causaria erro nem aviso no processo de compilação. Vou demonstrar isso para você aqui agora. Mas o que é importante é que você entenda o conceito de sentença. Também precisamos entender o que é uma expressão. Cada operador define uma expressão. Não se preocupe, pois veremos todos os operadores da linguagem C. Mas até o momento nós temos dois operadores que utilizamos, o de adição e o de atribuição. Nesse caso aqui, por termos dois operadores, temos
duas expressões. Voltando ao nosso código, só para garantir que você entendeu. Na linha quatro temos um operador, portanto temos uma expressão. Na linha cinco, temos um operador, portanto temos uma expressão. Na linha seis, temos dois operadores, portanto temos duas expressões. Também quero explicar um pouquinho melhor sobre sintaxe e semântica. O que eu quero que vocês se lembrem ao pensar na sintaxe é que ela é aquilo que define se uma sentença é válida ou não para a linguagem, avaliando a posição de lexemas. Por exemplo, a sentença C in + 1 igual, ela é uma sentença inválida.
Se a gente for observar, nós temos os mesmos elementos da linha 4, só a letra que eu acabei trocando. Mas aqui o que vai determinar se conseguiremos ou não compilar vai ser a sintaxe correta. Observe que ao tentar compilar encontramos um erro. Portanto, quando você ouvir sintaxe, lembre-se que é o Que determina a sequência correta dos lexemas. Lexemas que são um conjunto de palavra, operações, dentre outras coisas. Quando a gente falar de semântica, estaremos nos referindo ao significado das sentenças e expressões, ou seja, o que realmente está acontecendo quando executamos o nosso programa e ele
tenta realizar a operação de uma expressão ou de uma sentença. Em outras palavras, o que o nosso código está fazendo? Então, bora entender melhor a semântica do nosso código. Pessoal, aqui nós temos três componentes que a gente encontra em todos os computadores, que é o disco rígido, processador representado aqui em verde e a memória RAM. Para quem não sabe, o disco rígido, ele é uma peça física que está dentro do seu computador. Ele pode ser um SSD ou um HD. E ele recebe esse nome porque mesmo ao desligar, ou seja, sem nenhum tipo de energia,
ele salva as informações dentro dele. Todas as fotos e arquivos do seu sistema operacional estão salvos dentro do seu disco rígido, inclusive o nosso arquivo ponto C e o nosso arquivo pxe que aprendemos a criar através do processo de compilação. Agora a gente vai entender o que acontece quando esse arquivo está sendo executado. O nosso processador, ele é o responsável por realizar tarefas como retirar informações do nosso disco rígido e mover para a nossa memória RAM. O que eu quero que você entenda é que a memória RAM ela é muito mais rápida do que o
nosso disco rígido, porém ela apresenta uma desvantagem que sem a Energia, sem o seu computador estar ligado. Ou seja, caso a gente carregue um programa na nossa memória RAM, se nós reiniciarmos o nosso computador ou desligarmos, a nossa memória RAM, vai ser totalmente limpa. Vamos dar uma olhada então no que está acontecendo no nosso código. A primeira coisa que acontece quando nós executamos o nosso programa é a tarefa de transferir todas as instruções do nosso programa pra nossa memória RAM. Isso é realizado pelo processador, OK? Após carregar todas as nossas instruções na nossa memória RAM,
o nosso processador vai ler cada instrução que está presente na memória RAM, uma por uma. A primeira instrução é a de criar a variável A com o conteúdo um, ou seja, encontrar um espaço vazio na memória RAM, que nós iremos acessar através da letra A e colocar dentro desse espaço o conteúdo um. Observe que foi exatamente isso que aconteceu aqui. O nosso segundo passo é encontrar um espaço vazio na memória RAM. Nós vamos endereçá-lo com a letra B e atribuir a ele o valor dois. Foi exatamente o que aconteceu aqui. E o terceiro passo, pessoal,
ou a terceira sentença a ser executada, ela já envolve dois operadores. Portanto, nós temos duas expressões a serem resolvidas. A primeira expressão A + B. O nosso processador vai acessar o conteúdo de A no endereço de A, que no caso é um, e ele vai substituir, vai ser um e vai somar esse conteúdo com o conteúdo de B, que está no endereço B, que no caso é 2. 1 + 2 3. Agora ele vai pegar esse valor 3 e vai atribuir a um endereço vazio que nós vamos acessar através da letra C. Observe que foi
exatamente o que aconteceu aqui. E por último, o nosso programa vai executar a instrução de imprimir a variável C. Portanto, ele vai acessar o espaço de memória que está associado à letra C, pegar o conteúdo que é três e imprimir esse conteúdo. E assim o nosso programa finaliza. Bom, pessoal, essa explicação que eu dei para vocês, ela não está errada, ela está correta, mas é claro que eu omiti algumas coisas que são desnecessárias nesse momento. Por exemplo, você poderia se perguntar, quando o nosso processador está somando esses valores, 1 + 2, aonde ele armazena isso,
tá? Que é nos registradores, que são uma memória mais rápida que fica dentro do nosso processador. Enfim, poderia deixar essa explicação muito mais complexa, mas o que eu gostaria que vocês entendessem nesse momento é uma estrutura geral de execução de um programa. Talvez alguns de vocês aí que já conheçam um pouquinho mais de rádio esteja pensando: "Poxa, agora eu entendi porque com mais memória RAM eu consigo executar mais programas ou porque talvez eu ter memória RAM demais, mas não ter um processador bom suficiente, não vai me permitir estar executando tantos programas. Talvez alguns já estejam
tendo insite. Ah, entendi porque que o SSD, que é um pouquinho mais rápido, ajuda na execução dos programas, fica mais rápido para carregar do disco rígido para memória RAM, mas se você não tiver esse conhecimento, não tem problema. Só Gostaria de trazer a estrutura geral do funcionamento de um programa, pois ela vai nos ajudar no nosso processo de aprendizagem e de construção de soluções. Inclusive, vai nos ajudar também a entender melhor do que se trata uma variável. Antes da gente continuar aqui, pessoal, eu vou salvar que a gente já escreveu em um novo arquivo. E
vamos agora aprender um pouco melhor sobre as nossas variáveis, esses espaços na memória RAM que nós utilizamos para salvar informações. Vou recomeçar o nosso código. Vou incluir da biblioteca stio. Void main parênteses corochetes. E posso dizer para vocês que as variáveis de tipo primitivo em ser podem ser divididas basicamente em três grupos: caracteres, números inteiros e números reais. Pessoal, o que eu acabei de fazer aqui é adicionar um comentário. Existem duas formas da gente adicionar um comentário, que é utilizando barra barra. Ou também podemos fazer isso com barra asterisco, asterisco barra para finalizar. Aí a
gente pode saltar linha. Este é um comentário de mais de uma linha. Veja que o Visual Studio Code ainda está dando esta cor verde para meu comentário. Acontece que o comentário ele nada mais é do que uma Informação que é ignorada pelo nosso compilador. Quando a gente vai transformar o nosso arquivo ponto C em um arquivo executável, o nosso compilador simplesmente ignora toda a informação que estiver entre barra asterisco e também toda a informação que estiver depois de duas barras. Bom, vamos lá. Então, pra gente poder utilizar uma variável que armazene um caracter, nós vamos
utilizar a palavra char, o nome da nossa variável. E eu vou adicionar aqui nome da variável. Pra gente poder criar uma variável do tipo inteiro, nós vamos utilizar int, nome da variável dois. O nome pode ser qualquer nome. E para utilizar números reais, nós vamos utilizar float, nome da variável 3. Pessoal, na prática, o que que tá acontecendo? Quando nós declaramos uma variável, nós estamos reservando um espaço na memória. E por que que é necessário que a gente declare uma variável já informando qual que é o tipo dessa variável? Isso acontece porque o tamanho do
espaço de memória que é reservado depende do conteúdo que nós vamos armazenar. Inclusive, por esse motivo, nós temos também o short int. nada mais são do que tipos para armazenar inteiros, porém que reservam um espaço maior na memória RAM para que a gente possa Armazenar um número maior, ou seja, um número que contém mais dígitos. O mesmo acontece para números reais, porém aqui nós não utilizamos o termo short, e sim double e long. Gostaria de mencionar aqui que se você está com dúvida do que são números reais ou números inteiros, esse é um conteúdo do
ensino básico e você pode procurar por ele, procurando por conjuntos numéricos. Aí você vai encontrar explicações sobre o conjunto dos números naturais, inteiros, racionais e reais e reais. Porém, pra gente prosseguir aqui no nosso conteúdo, basta lembrar que números inteiros são aqueles que não possuem vírgula, números reais são aqueles que possuem vírgula. Bom, pessoal, eu trouxe aqui a declaração de diferentes tipos e eu quero mostrar aqui para vocês uma imagem que vai trazer em detalhe a diferença desses tamanhos. Por exemplo, a variável do tipo char, ela tem somente 1 byte. A variável short int tem
2 bytes, a intit. O long intro bytes. E bom, nesse momento aí você deve estar se perguntando por que que existem dois tipos diferentes, mas que possuem a mesma capacidade de armazenamento? Pessoal, esse valor que tá aqui, ele está aqui. Por quê? Porque vai variar de plataforma para plataforma. Não é só o sistema operacional que você vai estar utilizando, mas assim, no geral, né, eh, a gente vai deixar esse valor aí para que vocês saibam que isso pode acontecer. Talvez você tá tentando utilizar o long int, mas a capacidade dele vai ser a mesma do
int, tá? Em sistemas Linux, processador Intel, 64 bits, isso aqui não acontece, tá? Ele tem um valor maior, ele tem uma capacidade maior de armazenamento, mas fica aí essa explicação para vocês de porque que a gente tá deixando esse valor exatamente igual ao do in. OK? O nosso float ocupa 4 bytes, o nosso double ocupa 8 bytes, o nosso long double ocupa 16 bytes. Pessoal, observe que utilizando o long double, nós conseguimos ir de um valor extremamente baixo até um valor extremamente alto. Se você está observando aqui 3,4 x 10 elevado -4932 e pensando, poxa,
por que que começa em 3,4? Isso daqui também é um conteúdo do ensino básico chamado notação científica. Acontece que esse número aqui pode ser escrito como 0,004930 x 34. Já esse número aqui pode ser escrito como 34004932. Então, se você ficou na dúvida, dá uma olhada no conteúdo de matemática básica de notação científica, mas a gente não precisa se preocupar muito com isso, não, tá pessoal? É só para que realmente você não se perca aqui na nossa explicação. Outra coisa que eu gostaria de mostrar para vocês é que nós podemos criar esse espaço na memória,
reservar esse espaço na memória e depois atribuir um valor a ele. Nesse caso do caracter, nós vamos atribuir o valor aqui utilizando aspas simples. Aspas simples a gente utiliza para um caracter e aspas duplas a gente utiliza para um conjunto de caracteres. Qual que é a diferença? Quando nós fazemos isso, a gente tá armazenando somente uma letra em ASC do 2, que é como se fosse uma tabela que fala, ó, cada letra corresponde a um número. E quando nós utilizamos aspas duplas, a gente está armazenando como se fosse um conjunto de caracteres. Vou chamar aqui
de string, mas a gente vai ver isso aí com profundidade. E toda string, ela é marcada pelo final dela com um símbolo especial. Então esse símbolo especial, ele tá sendo adicionado aqui e não é o que a gente quer. A gente só quer armazenar um caracter por enquanto. Depois nós vamos aprender a montar aí um arreio, ou seja, vários caracteres em sequência para que a gente possa armazenar palavras, frases, nomes completos. Mas voltando aqui sobre a atribuição de valor, nós vamos atribuir um valor a uma variável utilizando o símbolo de igualdade. Então, lembre-se, toda vez
que você vê esse símbolo, nós estamos falando de uma atribuição. E a gente pode primeiro criar esse espaço, Depois armazenar esse valor. Poderia estar em qualquer linha do nosso código, inclusive poderia estar aqui, por exemplo. Ou nós podemos também já criar esse espaço e atribuir um valor à variável, ou seja, a esse espaço da memória. Uma outra coisinha que eu gostaria de falar com vocês sobre variáveis é que a gente pode criar variáveis que não armazenem números negativos. E para isso a gente utiliza a palavra unsign, que quer dizer sem sinal. A gente pode fazer
o short inch da mesma forma. Essa aqui eu vou nomear como variável. Para float a gente não consegue tá utilizando o termo unsign, mas a gente consegue fazer isso para caracteres. Hum, aqui vocês podem aí ficar um pouco confusos, mas vou tentar trazer um pouco do por que isso acontece. Bom, pessoal, quando nós utilizamos aqui o char, né, a gente reserva um byte da memória que corresponde a 8 bits. E se a gente lembrar aí um pouquinho aí da nossa boa e velha matemática, a gente vai perceber que 8 bits podem armazenar 2 elevado a
8. 2 elevado 8, pessoal. Vou multiplicar 2 aqui por 2 8 vezes mais uma é 256. Então nós podemos armazenar 256 valores. Esses 256 valores, Eles vão ir de -17 até 127. Então, 255 valores de -17 a 127. Quando a gente utiliza, vou deixar isso aqui comentado, quando a gente utiliza o unsigned shar, esses valores aí vão ir de zero a 255. A quantidade é a mesma, porém agora a gente não tem esses valores negativos. Aí você pode estar se perguntando: "Poxa, mas a gente tá armazenando somente letras? Por que que a gente está discutindo
valores?" Bom, vamos dar uma olhada aqui, pelo Google. tabela Ask 2. Se você observar a tabela Ask 2, ela contém 127 símbolos. É, são, é a tabela de conversão, né, pessoal, onde cada número desse aqui corresponde a um símbolo, porém existem tabelas um pouco maiores. E aí, caso você esteja utilizando uma dessas tabelas, inclusive o site aqui acho que não tá muito bom não, você pode estar utilizando oigned int. Bom, toda essa explicação aqui é só para te mostrar qual que é a finalidade, porque que isso existe, tá? E para que você não fique aí
com essa pulga atrás Da orelha. Bom, agora já vimos aí os principais tipos de variáveis, mas nós não conversamos sobre como nomear essas variáveis. Pessoal, o nosso compilador ele vai permitir que a gente utilize aí qualquer letra do alfabeto, minúscula ou maiúscula, ou underscore, para começar o nome da nossa variável. Depois de começar com um desses caracteres, a gente pode utilizar aí números também, caso a gente queira. Vocês viram que eu utilizei ele também e também utilizei o ele não pode conter outros caracteres, ou seja, que não sejam essas letras, o e os números. Teoricamente
aí o nome da nossa variável pode ter qualquer tamanho, porém, né, a gente só vai utilizar internamente o o nosso compilador, só vai utilizar os primeiros 65 caracteres. Então, caso você crie duas variáveis de 100 letras, mais as 65 primeiras de cada uma seja iguais, você vai vai você vai ter um problema em tempo de compilação. É isso aqui. As nossas variáveis em CS que são case sensitive, ou seja, existe uma diferença entre maiúsculas e minúsculas. Por exemplo, se a gente criar e a variável n ou n maiúscula, elas serão variáveis diferentes. Isso aqui só
sobre a parte de nomear variáveis as nossas limitações e o que pode ou não pode ser feito e também essa diferença aí entre variáveis minúsculas e maiúsculas. Bom, pessoal, toda variável ela tem algumas propriedades. Aí só com essa explicação que eu dei, a gente já consegue entender quatro dessas propriedades. A gente sabe que ela tem um nome que nós podemos escolher. Acabei de explicar quais são as regras para escolher o nome. A gente sabe que ela tem um endereço. A gente viu naquela Explicação sobre o funcionamento de um programa, que é um espaço na memória.
A gente sabe que ela tem um valor, tá? né? Um conteúdo armazenado nesse espaço de memória. E ela também tem um tipo que vai determinar aí a quantidade de memória ocupada pela variável, formato de ser armazenado. O que pode gerar de confusão é é a afirmação que eu vou fazer agora. Toda variável possui essas seis propriedades. O que isso quer dizer? que mesmo uma variável que eu não tenha atribuído um valor, ela vai possuir todas as propriedades, assim como valor. Não se preocupe demais com isso, mas na prática acontece que ao reservar um espaço na
memória RAM, ali dentro já possui algum valor aleatório, OK? Mesmo que você não tenha colocado nenhum conteúdo ali. Então, quando a gente cria uma variável, ela vai ter um valor aleatório. Isso acontece por quê, pessoal? O computador ele não apaga nada da memória. Ele nunca apaga nada da memória. Ele simplesmente reserva aquele espaço e fala: "Pô, esse espaço aqui nesse endereço nós vamos utilizar para tal coisa". Agora, se já tinha sido usado anteriormente ou não, não importa. Por que que a gente não sai apagando e coloca zero em tudo? Porque isso aí não teria muita
utilidade, só deixaria o seu computador mais lento. Quanto ao escopo, a gente vai aprender sobre ele depois, tá? Mas se você quiser dar uma lida aí, é só a parte do nosso programa, onde essa variável é acessível. Futuramente a gente vai ter variáveis que vão ser acessíveis somente de determinado lugar do nosso programa. E o tempo de vida, ele também é bem simples, tá, pessoal? É o tempo onde a nossa variável vai estar locada na nossa memória. Até então, o tempo de vida aí das nossas variáveis que a gente cria é o tempo de execução
do nosso programa. Ou seja, quando o nosso programa começa a executar, a gente reserva um espaço na nossa memória RAM. E quando o nosso programa para de executar, a partir daquele momento, aquele espaço lá tá liberado para outro programa poder utilizar. Muito bem. Muito bem. A última coisa que eu gostaria de mostrar para vocês sobre variáveis é uma variável constante ou utilizando um nome mais correto, uma constante nomeada. Então, constante nomeada. Pessoal, pode parecer meio contraditório aí a ideia de uma variável constante, tá? Mas também não é muito complexo, não. Vocês vão entender aqui. Ao
utilizar a palavra reservada const, eu posso criar aqui uma variável constante, uma constante nomeada do tipo inteiro, que eu vou chamar ela aí de max, por exemplo. A gente viu que a gente pode utilizar letras maiúsculas. O que que acontece? vão existir situações onde eu vou querer escrever nessa variável só uma vez e nunca mais poder escrever nela. Então aqui a gente poderia vir aqui e fazer aqui max 100. E se a gente tentar fazer aqui max a 120 depois, ou seja, tinha um valor e agora a gente quer trocar o valor dessa variável, como
a gente utilizou o termo const, isso aqui vai gerar um erro, ok, pessoal? Vou mostrar isso aqui para vocês. Observe, agora nós temos um erro, inclusive, né, pessoal, a gente também tem um erro aqui na nossa linha 24. Por quê? Só corrigir aqui o ponto e vírgula também. Então, assim, assim como eu expliquei para vocês, quando a gente cria uma variável, ela já possui algum valor lá dentro. Então, a gente precisa iniciar variáveis constantes com o seu valor. E agora a gente não tem nenhum erro, OK? Espero que tenha ficado clara essa diferença. São variáveis
que a gente não consegue trocar o valor. Tudo bem? Nesse momento aí a sua mente curiosa tá doido para poxa, vou criar aqui uma uma variável constante, vou imprimir ela no meu programa, quero ver qual valor que vai sair. Será que se eu executar esse programa diferentes vezes, ele vai pegar diferentes espaços da memória RAM e imprimir valores aleatórios ali diferentes? O que que vai acontecer? Mas antes da gente falar aí sobre como imprimir esses valores contidos tante uma constante, só quero mostrar mais uma coisinha para vocês, pessoal, que é sobre identificadores e palavras reservadas.
Bom, a gente viu lá as Regrinhas do que que a gente pode fazer ou não, mas existem palavras assim, por enquanto a gente já aprendeu int, a gente aprendeu a palavra const, a gente já aprendeu a palavra long, double, float. Essas palavras reservadas, elas não podem ser usadas como nomes de variáveis, OK? A gente vai aprender todas essas palavras reservadas de C ao decorrer do nosso curso, mas só queria deixar mencionado isso daí, OK? Então, bora lá aprender aí como que a gente vai imprimir essas palavras. Primeira coisa que a gente vai fazer vai ser
salvar o nosso programa. tipos de variáveis ponto C. Vamos aqui, vou limpar esse terminal para ficar um pouquinho mais claro pra gente. Liberar um espaço aqui na nossa tela e vamos aprender a imprimir essas nossas variáveis. A gente já viu que para imprimir a gente utiliza o comando print f da biblioteca stdi. Inclusive ela estava incluída aqui, mas a gente não estava utilizando nenhuma função dessa biblioteca, mas agora a gente vai utilizar aí o print f. O print f ele é sempre composto por essa estrutura aqui, comando, parênteses e as aspas, ok? Mas nós podemos
imprimir variáveis, ou seja, às vezes eu estou querendo dizer ali imprimindo a variável do tipo char, dois pontos. E a gente quer imprimir o conteúdo dessa variável. Para isso, para imprimir conteúdo de uma variável, a gente vai utilizar o símbolo de porcentagem. Sempre, pessoal, porcentagem. E aí a gente vai ter que utilizar aqui uma letra para indicar qual que é o tipo da variável que tá sendo impressa. No caso aqui vai ser o C para o char. E a gente vai sair. A gente sai então das aspas, coloca uma vírgula, depois colocamos a variável que
a gente quer que seja impressa, nome da variável. Bom, é isso que a gente vai aprender agora, como utilizar o print f para imprimir variáveis. Antes da gente só sair falando aí sobre tudo, né, eu quero dizer para vocês o seguinte, quando a gente utiliza o print f, a gente pode ir texto variável, texto variável. Aí a gente sai das aspas, coloca uma vírgula e aí a gente coloca aqui nome da variável, depois nome da variável dois. Essa estrutura aqui vai funcionar, tá? Só trouxe ela para te mostrar aqui. A primeira variável corresponde ao primeiro
símbolo de porcentagem dentro das aspas. A segunda variável corresponde ao segundo símbolo de porcentagem dentro das aspas. Aqui já daria um erro porque eu utilizei o D e eu disse para vocês que para imprimir caracteres nós vamos utilizar o C. Que a gente precisa aprender agora Então é que nós vamos utilizar paraa impressão de diferentes tipos de variáveis. Pessoal, isso aqui é bem tranquilo, tá? Vai ser bem fácil de vocês lembrarem. Então vem comigo aqui. Pra gente imprimir números inteiros, nós vamos utilizar o porcentagem I. Você pode também utilizar o porcentagem D, mas eu vou
utilizar aqui o porcentagem I, porque eu acho que fica mais fácil para vocês memorizarem e dia inteiro. Para imprimir short int, a gente pode utilizar o porcentagem I para imprimir o int. A gente também pode utilizar o porcentagem I, mas para imprimir o long int, nós vamos ter que utilizar porcentagem li de long int. Bem tranquilo, né, pessoal? Eu poderia juntar tudo isso daqui em um só print, porcentagem I, porcentagem li e colocar aqui nome da variável e nome da variável 4. Mas agora eu não vou fazer isso não, tá pessoal? Vou deixar assim. Para
caso você tenha alguma dúvida, você é só vir aqui e bater o olho mais fácil aí e relembrar. Pra gente imprimir unsigned, nós vamos utilizar o porcentagem U e o porcentagem Lu. Lembrando que ao final de uma sentença a gente precisa utilizar o ponto e vírgula, né? Eu não coloquei os o ponto e vírgula aqui nas nossas Impressões. Para números reais, adivinhem só? Nós vamos utilizar o porcentagem F de float, nome da variável cinco. Pro double, nós também vamos utilizar o porcentagem F. Ele consegue estar imprimindo uma variável do tipo double. E agora, agora para
o long double, pessoal, a gente não vai conseguir utilizar o porcentagem F. Nesse caso aqui, o que eu indico para vocês estarem utilizando é o porcentagem E. E o porcentagem E, na verdade, na verdade ele é utilizado para notação científica, OK, pessoal? E a gente vai ver aí como é que isso vai funcionar. Bom, se eu tentar imprimir isso daqui, que que vocês acham que vai aparecer na tela? Eu acho que vai aparecer uma bagunça. A gente não vai conseguir praticamente identificar nada. Então, vamos dar uma olhada aí para ver se eu estou correto. Corrigindo
ali alguns erros, ó. De novo, esqueci o ponto e vírgula. Bom, aqui é um erro. Nome da variável dois não existe, né? Deixar só o nome da variável aí. E pronto, tudo certo pra gente rodar o nosso código. E olha só, ficou bem ruim da gente visualizar. Por qu, pessoal? a gente precisa adicionar uma quebra de linha. Quando a gente tá utilizando print F sem utilizar uma quebra de linha, a gente vai imprimir isso daqui. Depois a gente vai pro nosso próximo print F. Não há uma quebra de linha. Para utilizar a quebra de linha
dentro das aspas, nós vamos utilizar o símbolo barra N. E para deixar o vídeo um pouquinho melhor, pessoal, vou dar uma acelerada nessa parte. E basicamente o que eu estou fazendo é colocando barra N ao final de cada print F, colocando um textinho ali mostrando qual que é o tipo que tá sendo impresso e também tô inicializando as variáveis aí com valor um para que a gente não pegue valores aleatórios. Então tô atribuindo aí um valor a cada uma dessas variáveis. Bom, pessoal, agora para inicializar variáveis, ou seja, atribuir valor a variáveis float double e
long double, vai ter uma pequena diferencinha. Então, se eu quiser atribuir aqui, por exemplo, 3.9, eu aconselho você a colocar o F no final, tá? Depois a gente vai explicar direitinho porque que isso acontece, mas é porque caso você não coloque, se você colocar somente 3.4 ou 3.9, não colocar o f no final, o nosso compilador vai interpretar como se isso fosse um double, ok? E aí ele vai tentar colocar um double dentro de uma variável float. Vai acontecer ali uma conversão e a gente vai ver depois aí como é que a gente faz isso.
Então uma boa prática aí para float a gente colocar o F. E para long double eu vou utilizar a anotação científica. Eu poderia estar utilizando essa anotação para float e para double também, mas vou deixar daquela forma ali para que vocês se lembrem. Então o que que nós vamos fazer aqui? Vamos colocar, por exemplo, aí o 3.9 X 10 elevado a 23 ou vamos deixar menos - 23. E aqui a gente coloca o símbolo L de long double, OK? a gente imprimir a nossa constante, a gente vai utilizar aí o tipo dessa nossa constante. E
tá aqui o nosso código. Agora bora tentar executar ele aí que eu quero mostrar para vocês que a gente vai encontrar aí uma peculiaridadezinha sobre o Lung Double. Bora. Bom, vamos observar aqui que aconteceu tudo como a gente esperava, tudo foi impresso, mas a gente já consegue identificar que a gente teve um problema com o nosso long double. Observem que eu coloquei aqui 3.9 el -23 e ele tá me mostrando aqui 5.3 elevado -312. Tá totalmente errado aqui esse valor. Antes da gente entrar de cabeça na nossa solução, vou executar de novo, de novo e
de novo. E observem que o valor do nosso long double está mudando, pessoal. Ele tá vindo ali, às vezes vem 4.03, agora veio do tá vindo o valor aleatório. Vou explicar para vocês o que que tá acontecendo e a gente já resolve esse problema aí aprendendo sobre conversões de tipo cast e truncamento. Então vamos lá, pessoal. Ó, a primeira coisa que eu vou fazer aqui é salvar esse nosso código. Bom, imagine a seguinte situação. Nós temos uma variável do tipo float chamada B, que possui o valor 3.9. E nós Queremos converter o valor que está
contido nessa variável para salvar em uma variável do tipo inteiro chamada I. Pessoal, a gente pode fazer isso fazendo um cast, que nada mais é do que a conversão de tipo. A gente coloca entre parênteses aqui o tipo que a gente gostaria de converter. No caso, nós queremos converter para um inteiro. E a gente coloca aqui a nossa variável. Nós vamos armazenar essa esse valor convertido na nossa variável I. A gente pode então imprimir aqui para ver o que que acontece quando a gente faz este castem. Como a gente converteu para inteiro, nós vamos usar
o porcentagem I. E vamos ver o que acontece. Observe que aqui não teve o comportamento que talvez você esperasse. Nós simplesmente perdemos todos os valores depois do ponto e ficamos com o valor três. Quando nós fazemos uma conversão de dados ou quando nós damos um cast, acontece o truncamento. Truncar é diferente de arredondar. Se a gente estivesse arredondando para um inteiro, a gente provavelmente estaria arredondando o valor para quatro. Mas como estamos truncando o valor, a gente está simplesmente ignorando todos os valores depois da vírgula. Tranquilo, né, pessoal? É um jeito da gente estar convertendo
esses Tipos. Uma outra forma de fazer isso sem que a gente tenha que criar uma variável é a gente convertendo na hora de realizar a impressão. A gente ainda vai utilizar o porcentagem I, pois nós vamos estar imprimindo o valor inteiro. Vamos executar aqui para ver o que que acontece. Observem que o comportamento é o mesmo. Nós poderíamos também fazer o contrário, convertendo um inteiro para float. Observem que agora casas decimais foram adicionadas. E basicamente, pessoal, nesse momento, o que eu gostaria que vocês entendessem é isso. Existe essa operação chamada cast, que nós podemos realizar
utilizando, entre parênteses, o tipo para qual queremos converter e em seguida a variável da qual iremos acessar o conteúdo para conversão. E queria que vocês se lembrassem que essa conversão não é um arredondamento e simuncamento. O valor está sendo truncado, ou seja, estamos desconsiderando os valores decimais, no caso da conversão de um float para um int. Vou voltar aqui e vou deixar dessa forma. Vamos salvar esse arquivo. E agora a gente vai entender aí qual que é o problema que tá acontecendo nesse código pra gente poder corrigir ele, pois nós já sabemos como dar um
cast e isso é o que vai ser necessário para que a gente Resolva o nosso problema. Vou compilar aqui, vou executar e vamos verificar aqui o problema que nós estamos tendo. O correto seria que a gente estivesse utilizando o Lu, porque o LI ele é para long int e não para unsign long int. Então a gente teria que estar utilizando aqui o Lu. Agora, em relação ao nosso Long Double, pessoal, esse problema só existe no Windows. Se você tiver no Linux ou no Mac, a impressão do Long Double vai acontecer normalmente. No Windows, nós instalamos
o Mi GW, que trazia ali o compilador GCC e as bibliotecas básicas que nós estamos importando. Mas quando tentamos imprimir utilizando a função print f da biblioteca stdio que o GW instalou, ela não consegue lidar com o tipo longe. Então o que nós podemos fazer é utilizar o cast que aprendemos anteriormente, mas acontece que o cast ele realiza aquele truncamento, ele desconsidera algumas casas decimais. Por isso, o ideal seria que a gente convertesse o número que tá na nossa variável para um texto e então a gente imprimisse esse nosso texto. Mas como a gente não
aprendeu sobre variáveis que armazenam textos, que nós vamos chamar aqui de strings, então a gente não vai utilizar essa solução aqui que seria mais correta, mas a gente consegue resolver o nosso problema utilizando um castble. demonstração de uso de uma string. Eu vou só mostrar aqui para vocês, para que vocês entendam aí como é a impressão de uma string. Shar nome igual aspas duplas Caio. E agora a gente poderia imprimir aqui impressão de string utilizando porcentagem S barra N, que é uma quebra de linha. Tudo bem? Futuramente a gente vai aprender aí como que a
gente converte o conteúdo de uma variável para uma string. Na prática, seria transformar aí um número que está na sua memória RAM em um texto. OK? Vamos soldar aqui para você. E a gente tem aqui a impressão da nossa string e também resolvemos o problema do long double. Tudo certo? Vou copiar [roncando] isso daqui. Vou atualizar ali o nosso código print f. Bom, vamos lá. Criando uma variável aqui inti, a gente sabe que ela possui um nome, um tipo, um endereço, um valor, um tempo de vida. Quando a gente terminar de executar o programa, a
nossa variável vai deixar de existir na nossa memória RAM. E a gente sabe que ela também tem ali um escopo que nós vamos entender um pouco melhor mais paraa frente. Até então, a gente não sabe como descobrir o endereço, mas existe uma maneira, tá? A gente pode até printar esse endereço. Para isso, a gente vai usar o símbolo porcentagem X. Acontece que quando a gente quer imprimir o endereço, nós vamos ter que utilizar este símbolo aqui, o ecomercial, e o nome da nossa variável. É claro, pessoal, que eu posso criar aqui uma variável que salva
endereços, né? Porém, Eu não vou mostrar isso aqui para vocês ainda, mas eu quero mostrar para vocês aqui esse operador e comercial e como imprimir endereços. Toda vez que a gente for imprimir algum endereço, a gente vai utilizar o porcentagem X. Vamos dar uma olhada nesse programa em execução. Observe que agora nós obtemos um endereço. Se a gente executar o nosso programa novamente, provavelmente a nossa variável vai ser criada em um outro lugar da nossa memória RAM. E para que que nós precisamos saber dessa informação? Inclusive, eu vou adicionar ela aqui no nosso printf. Impressão
de endereços de memória. O motivo da gente precisar do endereço a gente vai descobrir agora. Bom, vamos tentar fazer um programa que pergunte para o usuário no terminal qual a sua idade, qual a sua altura? E qual a sua letra favorita? Vamos adicionar uma quebra de linha e vamos criar algumas variáveis aqui. int, idade, [roncando] float, altura. E char letra favorita. Até então nada de novo. A gente reservou esse espaço da nossa memória RAM e a gente está imprimindo perguntas. Mas como é que a gente faz para poder receber uma informação do nosso usuário no
terminal? Para fazer isso, nós vamos utilizar um método chamado Scan F. O Scan F, ele tem uma sintaxe parecida com a do print F. A gente vai utilizar o parênteses, a gente também vai utilizar as aspas duplas. E a gente também vai utilizar o símbolo de porcentagem para poder receber essas informações. Aqui eu quero receber o inteiro, portanto eu vou utilizar o porcentagem I. E eu quero receber este inteiro na minha variável idade. Só que para que esse valor que eu vá receber seja guardado na variável idade, eu preciso enviar o endereço dessa nossa variável.
E a gente sabe que a gente consegue enviar o endereço utilizando o operador e comercial. A gente pode fazer o mesmo, então, para altura, chamando a função scan indicando que vamos receber um float e enviando aqui a nossa altura. Também podemos fazer o mesmo para um caracter e o endereço aqui da nossa variável letra favorita. Vamos então executar o nosso programa. Eu já adianto para vocês aí que vai ter um pequeno probleminha pra gente poder estar resolvendo. Compilar o nosso programa. Vamos executá-lo. Qual a sua idade? Vamos adicionar o inteiro. Observe que a gente utilizando
o nosso símbolo de porcentagem para o scan f, ele entende que ele está esperando inteiro. Pode ser um número maior, um número um pouco menor. Posso ter 8 anos. Colocar aqui 26. Qual a sua altura? Vou colocar 1,80. E aí a gente teve um probleminha que ele já perguntou qual é a letra favorita e no último scan F a gente não conseguiu digitar aí qual que era a nossa letra favorita. Bom, vou dizer para vocês que a gente vai resolver esse problema adicionando um espaço antes da porcentagem no Scan F. Se você tiver extremamente curioso
sobre o por que isso acontece, vai est aparecendo um tempo aí na tela. Você pode estar pulando o vídeo para essa parte. Eu vou explicar para você em detalhe porque que nós precisamos fazer isso, mas fique tranquilo que quando a gente for falar sobre strings, você vai entender direitinho porque que a gente precisa desse espaço. Ora, então a gente corrigiu esse problema. Vamos tentar fazer uma impressão aqui. Quero mostrar para vocês um jeito um pouquinho mais organizado de imprimir utilizando somente um print f. A gente vai colocar aqui a sua idade é porcentagem. E a
gente pode utilizar a barra aqui, pessoal, para que a gente possa quebrar a linha. A gente fecha aspas, usa a barra e assim nós podemos continuar na outra linha. A sua altura é barra. Lembrando que é essa barra aqui invertida, não essa daqui. Utilizando o Visual Studio Code, você pode identificar aí através das cores. A sua letra favorita é porcentagem C. Vamos adicionar uma quebra de linha aqui. E aqui a gente tem que colocar o porcentagem F de float. Adicionei depois aqui a quebra de linha para que você não confunda. Esse barra N está dentro
das aspas duplas. Ele é a quebra de linha. E esse barro aqui é para que a gente possa fazer o nosso print F acontecer em mais de uma linha, tá bom? Só por uma questão de organização. Então aqui eu vou colocar o barra de novo, vou colocar a vírgula e vou colocar as variáveis. Idade, altura letra favorita. Fique tranquilo, tá? Isso aqui é só um outro jeito. Eu poderia ainda escrever o nosso print F em uma só linha. Vamos compilar e executar o nosso programa. Qual a sua idade? Qual a sua altura, qual a sua
letra favorita? E aqui ele traz todas essas informações pra gente. Observe como nós estamos utilizando o padrão aí ASK 2 para realizar a impressão, eh, a gente não consegue estar imprimindo aí o e acentuado. Então a gente vai trocar aqui pelo e com h. Vamos compilar e executar. Qual a sua idade? 26. Qual a sua altura? 880. Qual a sua letra favorita? C. O que é importante aqui é que você tenha consciência do que que tá acontecendo. Nós estamos reservando um espaço na memória, nós estamos imprimindo uma mensagem, nós estamos armazenando um valor nesse espaço
que a gente já havia reservado previamente. E aqui nós estamos imprimindo o valor que está contido dentro das nossas variáveis. Preste bastante atenção, pois quando a gente quer acessar o conteúdo de uma variável, a gente simplesmente utiliza o nome dela. E Quando nós estamos utilizando o print f, é exatamente isso que a gente gostaria, acessar o conteúdo. Se a gente quiser acessar o endereço, a gente pode fazer isso. Eu já mostrei para vocês. A gente pode est utilizando o e-comercial no nosso print f e aí a gente vai estar imprimindo o endereço da nossa variável.
Normalmente a gente não vai querer fazer isso utilizando o print f. O que vai nos interessar imprimir vai ser realmente o conteúdo. No scan f a gente precisa passar o endereço e o conteúdo. O conteúdo aqui o usuário digita no terminal e o endereço é para que a função scan saiba aonde armazenar esse conteúdo. Bora salvar esse conteúdo aqui. Número se scanf pc. A gente já conversou sobre o que é uma expressão. A expressão está sempre dentro de uma sentença e nós sabemos quantas expressões nós temos a partir de uma análise sobre quantos operadores nós
temos. Então [roncando] agora, pessoal, a gente vai conversar um pouquinho mais sobre os nossos operadores. Os nossos operadores, eles podem ser classificados como unários, binários ou ternários. O nário é um operador que atua somente sobre um operando. Binário é aquele mais comum, né? A gente precisa ter informação dos dois lados. Então, quando a gente fala de uma adição, a gente sempre adiciona um elemento a um outro elemento. Um operador unário aí que a gente já viu é o e comercial. Ele atua somente sobre um operando e nos dá um endereço daquele operando. E o ternário
atua sobre três operandos. Ele tem um operando inicial, um no meio e um à direita. OK? Bom, pessoal, dentre os operadores, nós temos os operadores Aritméticos. São muito baseados na matemática, né? A gente tem a adição, a subtração, a multiplicação e a divisão, que funcionam da maneira que a gente espera mesmo, adicionando ali duas variáveis, subtraindo duas variáveis, multiplicando duas variáveis e dividindo duas variáveis. Mas esse operador também pode ser utilizado de maneira unária, ou seja, sem a existência de dois elementos. Caso a gente faça mais a somente, a gente vai obter aí o mesmo
que a. Caso a gente utilize o operador de subtração de maneira unária, a gente vai chamar ele de negativo. Ele simplesmente vai negativar um número ou uma variável. Aqui nós temos também o operador de módulo que nos dá o resto da divisão. Ou seja, se a gente utilizar aqui 5 módulo 2, vai resultar em um, porque 5/ 2 dá 2 com o resto 1. Caso você esteja um pouco confuso, te recomendo revisar a divisão de números inteiros. É um conteúdo do ensino fundamental. A gente vai implementar isso daqui. Tenho certeza que você vai conseguir relembrar
aí. Quanto aos demais operadores, eu prefiro explicar para vocês enquanto a gente escreve um pouquinho de código. Então, vamos lá. Vou criar aqui três variáveis agora de um novo jeito. Tô declarando todas na mesma linha e vou atribuir um valor a cada uma delas. Vou adicionar um comentário. Operadores aritméticos. E o primeiro deles é o positivo. Vamos tentar aqui imprimir utilizando o operador positivo em um valor negativo pra gente ver o que acontece. eem um valor positivo também. O negativo. Vamos fazer a mesma coisa. operador de adição. Esse aqui a gente já sabe como funciona.
Acabamos já utilizando aí mesmo antes dessa explicação. Subtração divisão. módulo, faremos 3 dividido por 2. Aqui, pessoal, a gente pode tanto utilizar uma variável quanto utilizar um valor, OK? O valor que está armazenado na nossa variável C é o valor 3 e nós queremos o módulo dele por dois. Esse daqui é o que dá um pouquinho mais de dor de cabeça, porque ele não é tão intuitivo para nós, não é algo que a gente utiliza no nosso dia a dia com tanta frequência, principalmente se você tiver começando a programar, mas fique tranquilo, basta você se
lembrar que é o resto da divisão. Portanto, a divisão deve acontecer primeiro. Então, a gente está sempre fazendo a divisão e a gente está obtendo o resto desta divisão. Pra você poder verificar isso dentro da sua cabeça, você só vai Encontrar esse valor se você primeiro realizar essa divisão. Então aqui nós temos 3 di por 2 é 1. 1 x 2 é 2. Portanto, a gente tem o resto 1. Esqueci de adicionar aqui o de multiplicação. Lembrando que eu não precisava estar utilizando variáveis para mostrar esses operadores para você. poderia simplesmente estar colocando o número
aqui diretamente. Então, hora da mágica da edição. E aqui está o nosso código com quebras de linhas adicionadas e algumas informações aí no print para que a gente possa visualizar mais facilmente o que que tá acontecendo. Bora compilar e rodar esse nosso código. E a gente vai observar aí que adicionando positivo a um número positivo, esse número se mantém igual. Adicionando positivo a um número negativo, ele também se mantém igual, como é esperado, é o comportamento da matemática. Adicionando negativo a um número positivo, ele é então negativado. Adicionando negativo a negativo, a gente torna esse
número positivo. A operação de adição não tem problema que o resultado seja negativo. A operação de subtração também funciona como esperado. Ou seja, quando a gente tá subtraindo por um número negativo, negativo com negativo se torna positivo. Portanto, a gente obtém o resultado três. A divisão também se comporta como a gente espera. A multiplicação 2 x 5 = 10. Tudo certo? E o módulo pessoal 3/ 2. A gente obtém aí o resto. 1. 1 x 2 dá 2. E a gente verifica aí a diferença de 3 para 2, que é 1 para obter o resto.
Ok? Vamos então salvar esse nosso arquivo antes da gente dar continuidade sobre o nosso estudo de operadores aritméticos. Vamos criar aqui quatro variáveis. A = 5, B = 5, C = 5 e D igual 5. E agora eu quero mostrar para vocês os operadores de pré-incremento e pós-cremento, pré incremento e pós incremento. Para que vocês entendam o funcionamento desses dois operadores, eu vou realizar aqui uma impressão do operador de précremento e também a imprensão utilizando o operador de pós incremento. Lembrando que a diferença entre esses dois operadores é se eles estão depois da nossa variável
ou antes. Esse daqui é um operador binário, pois ele atua somente sobre uma única variável. O operador de pré-incremento, ele realiza a seguinte operação: a igual a A + 1. Ou seja, nós pegamos o conteúdo de A, que é 5, somamos a ele 1, então obtemos valor 6 e colocamos esse valor dentro da nossa variável, no nosso espaço de memória. Acontece que ao utilizar o pré-incremento, nós fazemos essa operação antes de imprimir. Caso a gente utilize o operador de pós incremento, a gente realiza essa operação depois de imprimir. Para verificar isso, eu vou colocar aqui
valores finais mais um print, onde nós vamos imprimir os dois valores. Vou utilizar o B aqui. Lembrando que eu posso tanto utilizar porcentagem I quanto porcentagem D. Para imprimir inteiros, vamos imprimir o valor de A e B. Aqui a gente espera que a nossa impressão utilizando o pré-encramento seja do valor 6, utilizando o pós incremento seja o valor 5, mas que logo em seguida o conteúdo da nossa variável passe a ser seis. Por isso a gente tá colocando esse print para que a gente possa analisar se esse é o comportamento que vamos obter. Vamos lá
compilar. E é exatamente isso o que aconteceu. Portanto, tenham sempre esse cuidado. Quando a gente utiliza o operador de pré-incremento, a mudança acontece antes na nossa sentença. E quando a gente utiliza o operador de pós incremento, a mudança ocorre após a nossa sentença. Lembrem-se, pessoal, que uma sentença sempre é finalizada com ponto e vírgula. Portanto, se a gente tivesse ali uma nova variável chamada e e a gente utilizasse aqui a atribuição de um valor e vai ser igual a c, primeiro iríamos atribuir o valor de c à Variável e e somente ao final da nossa
sentença iríamos realizar a operação de incremento somando o conteúdo de C a mais 1 e então armazenando dentro dessa variável. Prestem bastante atenção nisso e utilizem do conceito de sentença, caso vocês fiquem em dúvida. Só para garantir, quando utilizamos o pré-incremento, a nossa operação é realizada antes da sentença atual. Quando utilizamos operador de pós incremento, a nossa operação é realizada depois da sentença atual. Bom, pessoal, nós também temos os operadores de prédecremento e pós decremento. E a diferença entre esses operadores, que basicamente realizam a mesma operação, porém agora reduzindo o valor da nossa variável em
um. O pré-decremento vai então realizar a operação. Vamos trocar a variável aqui para C. C = a C - 1 antes de realizar a impressão e o pós decremento vai realizar essa operação após o final da sentença onde utilizamos esse operador. Portanto, vou colocar aqui embaixo e podemos então realizar a impressão desses dois valores no nosso print de valores finais. Aqui não precisamos da variável e utilizei ela somente para explicar para vocês a questão da sentença. Então vamos lá compilar e executar o nosso código. Observe que o comportamento foi exatamente aquele que nós esperávamos, onde
a operação de pré-encramento foi realizada antes da execução dos outros elementos da nossa sentença e a operação de pósdecremento foi executada depois da execução dos elementos da nossa sentença. Nesse caso aqui, a chamada da função print f e a passagem desse valor da variável D. Vamos agora salvar esse nosso arquivo. Vamos aprender mais cinco operadores aritméticos da linguagem C. Bom, pessoal, esses operadores aqui eles servem como abreviações. Então, basicamente o comportamento que a gente vai obter em cada um deles poderia ser obtido utilizando aí uma sentença bem simples. Por exemplo, nós temos aqui o operador
mais igual que realiza a soma de um determinado valor. Lembrem-se que quando nós utilizamos o operador de incremento, ele adicionava somente um valor. Caso você queira adicionar um valor diferente de um, você pode estar utilizando o operador mais igual. Ao invés de escrever essa expressão, nós podemos fazer o mesmo para subtração, para multiplicação, para divisão e para o módulo. F - 2 vai ter o mesmo comportamento de remover duas unidades do valor da nossa variável. g x igual a 3 vai ter o mesmo comportamento de multiplicar por três o conteúdo da nossa variável. H dividido
igual 4 vai ter o mesmo valor que dividir a nossa variável por 4. E módulo igual a 6 vai alterar o valor da nossa variável para o resto da divisão do conteúdo da nossa variável pelo valor 6. Bom, pessoal, não se preocupem muito com isso. É, com o tempo vocês vão pegando. O que eu gosto de utilizar é um pouco dessa interpretação que eu dei para vocês. Se eu tenho uma variável operador mais igual a 5, eu tento me lembrar que então eu vou estar somando 5 ao conteúdo da minha variável. O operador menos igual
a 2, eu estarei reduzindo o valor dois da minha variável. o operador vezes igual, eu vou estar multiplicando o conteúdo da minha variável e armazenando nela mesma, né? Claro, mas serve para divisão. Vou estar dividindo o conteúdo da minha variável por um valor e armazenando nela mesma e pro módulo. Qualquer dúvida que vocês tiverem, vocês podem estar vindo aqui no nosso arquivo para relembrar. Vou adicionar os prints para que vocês possam testar isso e verem que eu não estou enganando vocês. E é claro que se a gente tentar executar esse código agora, a gente vai
obter um erro, pois eu não declarei essas variáveis. Eu estou simplesmente Utilizando a variável aqui, mas eu deixei para fazer isso depois de criar essas sentenças, para que a gente possa utilizar valores onde nós vamos obter aí números semelhantes. Por exemplo, aqui eu quero obter o valor 10, portanto vou criar a minha variável, inicializá-la com valor 5. Também quero obter o valor 10, portanto vou iniciar ela com valor 12. Como vamos multiplicar por 3? Vou inicializar com valor 10 para que a gente possa obter um valor 30. Como vamos dividir por 4? Vou inicializar a
nossa variável com valor 40 para que a gente possa obter o valor 10. E aqui, como vamos pegar o resto da divisão, vou inicializar a nossa variável com o valor 35 para que a gente possa obter o valor 5. Para imprimir a porcentagem, nós vamos utilizar o símbolo de porcentagem duas vezes. E agora sim, podemos executar o nosso programa. Aqui faltou o operador de atribuição. Bom, faltou aqui adicionar o nosso ponto e vírgula, mas os valores estão corretos. 10 30 10 e 5. Com a mágica da edição, vou estar adicionando essas quebras de linhas. E
aqui está o nosso código. Vou salvar esse nosso arquivo para que vocês possam estar consultando depois. Nós finalizamos o nosso estudo sobre os operadores aritméticos em C. Vamos agora iniciar o nosso estudos sobre os Operadores relacionais que recebem esse nome, pois são operadores binários utilizados para comparar duas variáveis ou dois números. E aqui já está aparecendo uma lista de todos eles. Nós temos o operador menor que, maior que, menor ou igual que, maior ou igual que, igual a e diferente d. Toda comparação, ela retorna verdadeiro ou falso. Caso A seja menor do que B, vamos
obter verdadeiro. Caso A seja maior do que B, vamos obter verdadeiro. Caso A seja menor ou igual a B, vamos obter verdadeiro. Caso A seja maior ou igual a B, vamos obter verdadeiro. Caso A seja equivalente, nós vamos obter verdadeiro. E caso A seja diferente, nós vamos obter o valor verdadeiro. Evidentemente que em todas essas operações, se a afirmação for falsa, nós vamos obter o retorno de falso. Já já vou explicar para vocês o que é verdadeiro e o que é falso na linguagem C. Mas antes disso, eu já queria falar um pouquinho sobre o
operador de igualdade. Todas as vezes que nós utilizamos o símbolo igual, nós estávamos atribuindo valores a uma variável. Observem esse comportamento em todos os nossos códigos anteriores. Nós não utilizamos o símbolo de igualdade com o mesmo sentido da matemática, ou seja, uma afirmação de que dois elementos ou um valor e o conteúdo de uma variável são iguais. Agora, ao utilizar operadores relacionais, para fazer essa comparação, nós precisamos utilizar dois símbolos iguais. Bora ver como é que isso funciona no nosso código e entender qual o valor que nós obtemos quando o retorno é verdadeiro e qual
valor obtemos quando o retorno é falso. Para verificar isso, podemos utilizar um print f e aqui mesmo realizar a comparação. Bom, precisamos também criar essas variáveis. E como eu quero verificar qual o valor de verdadeiro, vou inicializar a variável A como um e a variável B como do. Aqui nós estamos imprimindo o retorno dessa nossa expressão. Lembrando que nós temos aqui uma expressão, pois estamos utilizando um operador e todo operador forma uma expressão. Vamos rodar esse nosso código e observe que obtivemos o valor um. Vamos fazer a afirmação inversa e nós vamos obter zero. Em
algumas linguagens existe o tipo buleano, porém em C, todos os nossos operadores retornam um para verdadeiro e zero para falso. Vou realizar aqui todas as outras comparações. Menor e igual, maior e igual. Vou deixar comentado qual o nome de cada um desses operadores. Menor que, maior que, menor ou igual, maior ou igual, equivalente e diferente. Vamos executar o nosso código, onde nós estamos comparando o valor da variável A, que é 1, com o valor da variável B, que é 2. E o nosso retorno é exatamente como esperado. 1 é menor do que 2. Um não
é menor do que 2, por isso falso. 1 é menor ou igual a 2, portanto Verdadeiro. 1 não é maior ou igual a 2, portanto falso. 1 é diferente de 2, portanto o operador de equivalência retorna falso. 1 diferente de 2, nosso operador retorna o valor verdadeiro. Nós vamos entender melhor aonde utilizar esses operadores, mas já deixo mencionado aqui que eles são de extrema relevância na programação. Vamos salvar esse arquivo. Antes da gente dar uma olhada nos nossos últimos operadores, que são os operadores lógicos, eu quero fazer uma breve revisão sobre os operadores aritméticos e
os operadores relacionais. Observem que os operadores aritméticos são operadores da matemática e que resultam em um valor inteiro ou real. Caso a gente esteja realizando a adição de números inteiros, nós vamos obter o valor de um número inteiro. Caso a gente esteja realizando a adição de números reais, nós vamos obter o valor de um número real. mesmo serve para subtação, multiplicação, divisão e para todos os outros operadores, inclusive os de incremento e de decremento, que simplesmente adicionam um e armazenam esse valor na nossa variável. A ideia aqui é que vocês entendam que os operadores aritméticos
nos retornam valores. Já os operadores relacionais, eles comparam dois valores. Todos os operadores relacionais são binários, portanto todos exigem que você utilize duas variáveis ou dois valores, mas apesar deles compararem dois valores, ou seja, números inteiros ou números reais, o retorno, o que nós obtemos ao utilizar os operadores relacionais é um valor de verdadeiro ou falso. E é importante que vocês tenham isso em mente, pois os operadores lógicos servem exatamente para comparar verdadeiro e falso. Observem a diferença. Os operadores relacionais comparam valores inteiros ou reais e nos dão como retorno um valor de verdadeiro ou
falso. Já o operador lógico compara valores de verdadeiro ou falso e nos dão como retorno verdadeiro ou falso também. Esses operadores são três. E se você nunca viu esse conteúdo e não conseguir compreender somente com a minha explicação, fique sabendo que o comportamento desses operadores de negação, o I e o O, fazem parte da matemática, mais especificamente da matemática discreta. E vocês podem encontrar mais informação sobre esse conteúdo, procurando por lógica matemática ou operadores lógicos. O operador de negação, ele transforma verdadeiro em falso e falso em verdadeiro e é o único operador lógico unário. Já
os operadores I eO são operadores binários. No operador I. Se nós tivermos dois valores verdadeiros, nós vamos obter um retorno verdadeiro. Se nós tivermos um valor verdadeiro e um valor falso, nós vamos obter um retorno falso. Já no operador de ou basta que um dos dois elementos, sendo comparados, sejam verdadeiros. E aí nós vamos obter o retorno verdadeiro. Vamos dar uma olhada nisso, escrevendo um pouquinho de código. Aqui eu estou utilizando o operador relacional, portanto ele recebe dois valores e retorna verdadeiro ou falso. O resultado dessa expressão aqui vai ser verdadeiro ou falso. E agora
eu vou utilizar o nosso operador de negação, que vai inverter o valor da Variável relacional. Caso na variável relacional a gente obtenha verdadeiro, que é o que vamos obter, já que um é equivalente a um, nós vamos estar invertendo esse valor para falso e armazenando esse conteúdo na variável A. Vamos agora rodar esse nosso código e observar que obtemos o valor falso zero. Bom, pessoal, eu também poderia escrever todas essas operações direto aqui no nosso print f, ou seja, primeiro iríamos ali realizar a comparação entre dois números e depois iríamos negar o resultado que retornaria
dessa comparação. Aqui eu vou fazer diferente para que a gente retorne falso, já que um é diferente de dois. e vamos negar o valor falso, obtendo então o verdadeiro. Vou deixar das duas formas e vamos agora falar sobre o nosso operador E. Da mesma maneira, eu realizei duas comparações, verifiquei se o valor 5 é maior do que 3. Isso vai nos retornar verdadeiro. E verifiquei se o valor 5 é maior do que 4, que também vai nos retornar verdadeiro. Vou deixar comentado e vou fazer a impressão desses retornos. Agora vamos realizar a comparação do conteúdo
da variável relacional 2 e da variável relacional 3. Serão verdadeiro e verdadeiro. E o nosso operador I, quando recebe verdadeiro e verdadeiro também retorna verdadeiro. Portanto, a gente vai armazenar o valor verdadeiro na variável Comparação I. Vamos realizar a impressão. E observem que o comportamento foi exatamente o que a gente esperava. Como eu disse para vocês, nós poderíamos realizar todas essas operações criando uma expressão aqui dentro do nosso print f. Aqui eu vou fazer uma comparação entre um valor verdadeiro e um valor falso. E vamos verificar qual vai ser o retorno. 5 é maior do
que 3, portanto vai retornar verdadeiro. Vamos utilizar o operador endar se 5 é maior do que 7 e obtemos o valor falso. Agora só falta o operador ou. Nele vou fazer um pouquinho diferente. Vou realizar a comparação sem criar variáveis para utilizar os nossos operadores relacionais, mas vou armazenar o resultado do nosso operador ou em uma variável e vou realizar a impressão dessa variável. Observem que 5 não é maior do que oito, portanto vamos obter o valor falso, e que 5 não é maior do que 7, portanto também vamos obter o valor falso. Comportamento esperado
é que o nosso operador ou retorne então falso. É exatamente isso que acontece. Vamos agora verificar o nosso comportamento. Caso a gente tenha um valor falso, 5 maior do que 8 é falso. E um valor verdadeiro, 5 maior do que 4, que é verdadeiro, 5 maior do que 8. Para escrever o nosso operador ou a gente utiliza esse símbolo chamado pipe ou Barra vertical, que você pode estar digitando apertando shift e a tecla ao lado do Z em teclados ABNT2 5 maior do que 4. Vamos salvar e executar. Como uma das nossas comparações se torna
verdadeiro, basta que um dos valores seja verdadeiro para que o nosso operador lógico retorne verdadeiro. Bom, tem algumas informações que foram omitidas de vocês. A primeira vez que nós conversamos sobre verdadeiro ou falso foi quando nós vimos os nossos operadores relacionais comparam números e nos retornam valor de verdadeiro ou falso. E eu disse pra vocês que o valor inteiro um significa verdadeiro e que o valor zero significa falso. Essa informação não está incorreta, mas ela está incompleta. Na linguagem C, zero significa falso e verdadeiro é todo valor que seja diferente de zero. Em outras palavras,
um é tido como verdadeiro. 200 também é tido como verdadeiro. E inclusive números do tipo float diferentes de zero, também são tidos como verdadeiro. Apesar de não ser muito comum, você pode talvez utilizar de maneira errada esses operadores e obter uma resposta inesperada. Então, por isso tô trazendo essa explicação aqui. Nós vamos negar o valor 280. E como eu disse para você, todo valor diferente de zero é tido como verdadeiro. Portanto, nós vamos negar verdadeiro e obter falso. Nós podemos também realizar a comparação de dois valores inteiros utilizando o operador I. Como nenhum deles é
zero, nós vamos obter aí o retorno verdadeiro. E agora nós vamos aprender o único operador ternário da linguagem C. Ternário, pois ele trabalha com três variáveis ou com três valores que podem vir de uma expressão. A princípio, ele pode parecer um pouco complexo, mas se você tiver entendido todos os nossos operadores anteriores, você não vai ter problema em estar utilizando o operador de condição ternário. Observe que ele ganha esse nome condição, pois esse é o primeiro elemento deste operador. E na condição nós só podemos utilizar operadores relacionais. e operadores lógicos, porque somente operadores relacionais ou
lógicos vão nos retornar um valor de verdadeiro ou falso. Normalmente vamos utilizar aí operadores relacionais que comparam números e nos retornam um valor verdadeiro ou falso. Em seguida, nós temos dois elementos e e em seguida nós temos dois elementos, que serão duas variáveis. Nós temos aqui um código para mostrar o funcionamento deste operador, onde pedimos para que o usuário digite dois números inteiros. Salvamos esses números na variável X e na variável Y. Então, utilizamos um operador relacional X maior que Y. Caso isso retorne verdadeiro, ou seja, o valor um, então este nosso operador ternário irá
retornar o valor da variável X. Caso X maior que Y retorne falso, então esse nosso operador ternário, que está em azul irá retornar o valor da variável Y. Vamos dar uma olhada nesse código de funcionamento e testar alguns valores. Vou compilar esse código e vou executá-lo. Digite dois Números inteiros. Nós vamos digitar o valor 2 e o valor 5. O valor 2 vai ser atribuído à variável X. O valor 5 vai ser atribuído à variável Y. Em seguida, nós vamos executar o nosso operador ternário, onde a primeira operação a ser realizada vai ser a de
condição. Vamos verificar se x é maior do que y. 2 não é maior do que 5, portanto vai retornar falso. Como a resposta é falsa, o valor que essa expressão irá retornar será o valor de y, o que vem depois dos dois pontos, ou seja, o valor 5. Então, o valor 5 será armazenado na variável maior. Depois iremos realizar essa impressão. Observe que foi exatamente o que aconteceu. Caso eu digite 5 e 2, o valor 5 será armazenado na variável x. O valor dois será armazenado na variável Y. Terminada essa sentença, nós vamos executar a
próxima sentença, iniciando por essa expressão que contém operador ternário. Dentro do operador ternário, vamos verificar se x é maior que y, 5 é maior do que 2. Como o valor vai ser verdadeiro, iremos retornar desta expressão o conteúdo da variável x 5. Então, vamos armazenar esse valor na variável maior. Depois, vamos realizar a impressão desta variável. E foi exatamente isso que aconteceu. Para que você consiga utilizar todos os operadores que nós vimos até agora, é necessário prática. Mas antes de praticar, nós temos ainda um tópico a ser abordado. Vamos salvar esse nosso arquivo e eu
quero conversar com vocês sobre Precedência de operadores. Se vocês observarem bem, quando eu estava utilizando os operadores lógicos, eu fiz uso dos parênteses, exatamente porque nós ainda não tínhamos aprendido sobre a precedência de operadores, mas a partir de agora a gente vai conseguir prever o comportamento de algumas expressões, como por exemplo, o que acontece se eu criar a variável A, B, C e D, fizer com que o valor da variável D seja igual a 5 e então escrever a seguinte sentença: A = B, que é igual a C, que é igual a D. Qual será
a ordem de execução desse comando? O que acontece se eu fizer a sentença a igual a 2 + 3 - 4? Qual dessas operações será realizada primeiro? E é isso que nós vamos aprender agora, pessoal. Em casos como esse, é aplicada a regra de precedência dos operadores, onde a expressão com um operador de maior precedência é avaliada primeiro. Lembrando que em uma sentença nós identificamos o número de expressões pelo número de operadores, ou seja, no nosso código, o operador de maior precedência será realizado primeiro. Isso quando a gente estiver falando de operadores de precedência diferente.
No caso de operadores de mesma precedência, Geralmente as operações serão realizadas da esquerda pra direita. Observe aqui na linha 9, primeiro iríamos realizar a operação 2 + 3, que é igual a 5, e depois a operação 5 - 4, que é igual a 1. Já que os operadores de adição e subtração t a mesma precedência. Esse vai ser o comportamento na maioria das expressões, porém existem exceções. Se estivermos utilizando o operador de atribuição, primeiro iremos efetuar a expressão da direita, onde a variável C vai receber o valor 5, que é conteúdo da variável D. Então,
a variável B vai receber o valor 5, que é conteúdo da variável C. E então a variável A vai receber o valor 5, que é conteúdo da variável B. Aqui temos o que eu acabei de explicar para vocês. Quando todos os operadores possuem a mesma precedência, é aplicada a regra de associatividade dos operadores. A maioria dos operadores possui associatividade à esquerda, onde as operações mais à esquerda são realizadas primeiro, mas os operadores unários e a atribuição possuem associatividade à direita. Lembrando que operadores unários são aqueles que só precisam de um elemento. Portanto, caso eu utilizasse
aqui um operador unário, lembrando que operador unário é aquele que realiza uma operação somente sobre um valor. E aqui eu vou trazer um exemplo para vocês, utilizando a negação lógica e a negação aritmética. Como no caso de operadores unários, a primeira operação a ser realizada será da direita. Aqui nós vamos negar o valor 4, que é tido como verdadeiro. Então vamos Obter o valor zero. Então vamos negar a zero e zero negativo é a mesma coisa que zero. Se a gente fizesse o contrário, nós negaríamos o valor falso. Vamos obter um. Então transformaríamos um no
valor -1. O exemplo com operador de atribuição, eu já trouxe para vocês. E os operadores de incremento e decremento, eles não são associativos. Vale lembrar, pessoal, que os operadores de incremento e decremento podem ser divididos em pré-incremento e pós incremento. O operador de pós incremento, ele sempre é realizado ao final dessa sentença. Ou seja, depois de fazer com que A receba o valor de A, aí sim nós vamos estar realizando a operação de incremento. Já o operador de pré-incremento vai realizar essa operação antes mesmo de executar a nossa sentença. E aí surge essa pergunta. Teoricamente
deveria ser válido escrever a mais, mas também não é. Por quê? Escrito dessa maneira, a gente pode entender que o que tá dentro do parênteses é o operador de pós incremento, vai ser realizado ao final da nossa sentença. E então nós deveríamos pegar o conteúdo que vai retornar dos parênteses, enviar ele para um novo operador de incremento para que ele então pudesse realizar novamente essa operação. Porém, quando a gente já está trabalhando esta nossa sentença, nós não temos como voltar no tempo e acessar o retorno do conteúdo em parênteses. Por esse motivo, a gente não
pode utilizar o operador de incremento Dessa maneira. Dito isso, o que é mais importante que você se lembre é que todos os operadores quando possuem a mesma precedência vão ter associatividade à esquerda, ou seja, vamos resolver as operações à esquerda primeiro, exceto no caso do operador de atribuição e dos operadores unários. Vou escrever isso daqui no nosso código. Incremento e decremento. Não são associativos, pois são realizados ou antes ou depois da sentença em que são declarados. E na dúvida, nós temos aqui uma tabela de precedência. Vou ensinar vocês a fazerem uso dessa tabela. E basicamente
aqui a gente precisa entender que quanto mais em cima, maior a precedência do nosso operador. Se tivermos utilizando operadores do mesmo tipo, a gente pode verificar o comportamento em associatividade. Se estivermos utilizando operadores diferentes, a gente pode verificar o comportamento analisando qual está mais acima. Por exemplo, se eu utilizar o operador de adição ou de subtração, que possuem a mesma precedência, a associatividade será à esquerda. Mas caso eu esteja utilizando o operador de multiplicação e o de adição numa mesma sentença, nós vamos resolver primeiro a multiplicação. Observe, analisando a nossa sentença, nós temos três expressões,
pois temos três Operadores, subtração, multiplicação e adição. E vindo aqui na nossa tabela, a gente sabe que o operador de multiplicação será realizado primeiro e somente depois vamos realizar a operação de adição e subtração. Portanto, nesse caso aqui, se a gente fizer o uso desses parênteses ou não, o resultado será o mesmo. Os operadores lógicos e e também possuem nível de precedência diferente. Portanto, caso a gente utilize eles em uma mesma sentença, como por exemplo, um e um, verdadeiro e verdadeiro, vai retornar o valor um. Então o valor que essa expressão vai retornar vai ser
utilizado no operador ou que também vai retornar um, já que basta que um dos lados seja verdadeiro. Eu sei que essa execução vai acontecer dessa forma e eu posso verificar isso na nossa tabela de precedência. Pessoal, é claro que não há problema nenhum em você estar utilizando os parênteses para garantir o comportamento do seu código. Até então, eu tava tendo esse cuidado de sempre utilizar os parênteses, mas com a prática no decorrer do tempo, você vai adquirindo essa experiência de saber onde é necessário ou não fazer uso dos parênteses. E nesse momento nós já aprendemos
sobre processo de compilação, como utilizar bibliotecas através do comando include variáveis cast de variáveis, que seria a mudança de tipo de uma variável. Aprendemos sobre input/ output, ou seja, entrada e saída de dados no terminal. Aprendemos também todos os nossos operadores da linguagem C, sendo que podemos classificá-los em operadores aritméticos, operadores relacionais, operadores lógicos. Também Podemos classificá-los entre operadores unários, binários e ternários. E aprendemos também o nosso único operador ternário da linguagem C. Nesse momento, pessoal, recomendo que vocês acessem o link aqui na descrição e vocês vão encontrar uma lista de exercício envolvendo todos os
tópicos que nós já estudamos até aqui. E depois da resolução desses exercícios, nós vamos instalar algumas extensões que vão facilitar o nosso processo de programação dentro do Visual Studio Code. É claro que você pode pular o vídeo, já fazer uso dessas extensões, mas no seu processo de aprendizagem é importante que a gente adicione esses recursos facilitadores em momentos que não vamos prejudicar o seu aprendizado. Portanto, recomendo que vocês resolvam essa lista de exercício utilizando somente o Visual Studio Code da maneira que configuramos até agora. Muito bem, agora vamos instalar algumas extensões no Visual Studio Code.
Eu vou mostrar para vocês porque que nós vamos fazer isso. Aqui temos um print F, onde eu esqueci de utilizar o ponto e vírgula no final da nossa sentença. E observem que a gente não tem nenhuma forma de identificar esse erro, a não ser quando nós vamos compilar o nosso código. Por esse motivo, a gente vai tá vindo aqui à esquerda em extensões e vamos procurar pela extensão C. barc+. Observem que o Visual Studio, na primeira vez que é executado, já sugere a instalação dessa extensão, mas até então nós não havíamos utilizado ela. Vou clicar
na primeira opção. Aqui nós vemos o número de downloads realizados nessa extensão. E clicar em instalar. Vamos agora verificar o nosso código. E observem que recebemos um indicativo de erro de três maneiras, ou melhor, de quatro maneiras. A primeira delas, o nosso explorador de arquivos. O nome do nosso arquivo fica vermelho. E aqui a gente obtém o número de erros que foram encontrados, né, por essa extensão que está analisando o nosso código. Nós também podemos identificar o erro nessa parte de cima no nome do nosso arquivo, seguido da quantidade de erros encontrados. E dentro do
nosso editor de texto, se vocês observarem bem, nós temos aqui um sublinhado em vermelho. À esquerda, na barra de rolagem, a gente encontra também a cor vermelha indicando a linha do erro. Vou descer aqui. Observem que nem sempre a linha do erro vai ser identificada corretamente, tá bom, pessoal? Mas isso já possui uma utilidade, pois é um alerta de que há algum problema no nosso código. Vou adicionar o ponto e vírgula. Automaticamente a extensão já identifica que não há nenhum erro encontrado no nosso código. Agora, uma outra extensão que nós vamos estar realizando a instalação.
Ao procurar por C C++, nós encontramos essa extensão da Microsoft que se chama CC+ Extension Pack. Na verdade, essa extensão traz um conjunto de pacotes que a gente vai precisar táar instalando para poder utilizar a nossa próxima extensão, que vai ser Code Runner. Procure por Code Runner. O autor chama de um RAM. Clique em instalar. Vamos verificar se a gente já consegue estar utilizando essa extensão. Caso contrário, a gente vai precisar reiniciar o nosso Visual Studio Code. Observem que agora nós temos a opção, ao clicar aqui em cima, run code e ao clicar nela, o
nosso código é compilado e executado. Vou deletar os arquivos executáveis e vou utilizar a nossa extensão novamente. Observem que ela cria o nosso executável com o mesmo nome do nosso arquivo main.c C e então executa esse arquivo, além de nos informar o tempo de execução. Bom, é só uma comodidade. Não mostrei isso antes para vocês, para que vocês reforçassem o processo de compilação e depois de execução. Mas se a gente for observar aqui, basicamente estamos utilizando o comando CD Change Directory para acessar a pasta onde o nosso arquivo está. Esse comando aqui, assim como no
C, significa que além de executar essa entrada, nós vamos executar um outro comando, que é o comando de compilação gc main.c traço o main e então a execução do arquivo. Eu vou imprimir porcentagem de epar. Vamos executar o nosso código. Clicando para executar o nosso código, utilizando a nossa extensão Code Runner, a gente vai se deparar com um pequeno probleminha. Caso a gente remova aqui o nosso Scan F, observem que a execução vai acontecer de maneira correta. Executar novamente para vocês verem. Mas com o scan f, a nossa extensão code runner não está funcionando. O
jeito de Resolver isso, a gente vai vir aqui em configurações. Configurações. Vamos digitar code, traço runner. Run code configuration. Vamos descer e marcar a opção rodar no nosso terminal. Run in terminal. Feito isso, a gente pode fechar as configurações. Eventualmente vocês devem se deparar com a presença dessa pasta aqui, pvscode. É uma pasta que traz algumas informações para que as nossas extensões utilizem ela. O que eu queria informar para vocês é que caso vocês deletem essa pasta, não vai acontecer nenhum problema, tá? A única diferença é que nossas extensões talvez vão executar um pouquinho mais
lento, pois aqueles arquivos servem para que essas informações armazenadas façam com que nossas extensões executem de maneira mais rápida. Caso você delete essa pasta, não tem problema. Tudo vai continuar funcionando da maneira correta, mas eventualmente ela vai ser criada novamente pelo Visual Studio Code. A gente pode só ignorar quando isso acontecer. Uma outra diferença que vocês vão encontrar no vídeo a partir de agora é essa barra azul que tá aparecendo aqui embaixo. Gostaria de explicar para vocês que é porque eu estou utilizando a minha máquina para fazer as gravações e não aquela máquina que eu
havia criado do zero para que a gente pudesse configurar todo o nosso ambiente de desenvolvimento em conjunto. Eu adicionei essa barra, pois eu estou simultaneamente executando programas de gravação, edição, eu não quero que eles distraiam vocês. podem ficar tranquilos, pois as extensões que eu vou estar utilizando e o meu ambiente de desenvolvimento vai estar exatamente alinhado com o seu, caso você tenha seguido o tutorial até aqui. Então vamos pro nosso próximo tópico, estruturas de controle. Pessoal, estruturas de controle podem ser divididas em estruturas condicionais e estruturas iterativas. Nós vamos estar estudando agora as estruturas condicionais.
Essa parte do nosso curso é importante porque é exatamente ela que difere o computador de uma calculadora. Aprendendo bem esse tópico, a gente vai ter a capacidade de decidir entre executar um conjunto de ações ou outro caso determinada condição seja atendida. Dá uma olhada nesse diagrama que tá aparecendo na tela de vocês e perceba que caso uma condição seja verdadeira, lembrando que a gente viu os nossos operadores relacionais, onde a gente consegue estar relacionando dois números e obtendo o valor verdadeiro ou falso. Por exemplo, A é menor do que B ou A é maior do
que B, vai retornar um valor de verdadeiro ou falso. Também vimos os nossos operadores lógicos que nos permitem relacionar valores de verdadeiro e falso e retornam um verdadeiro ou falso também. Caso a gente esteja utilizando algum desses operadores na nossa condição e eles retornem valor verdadeiro, nós podemos estar executando sentenças subordinadas. Se eles retornarem o valor falso, nós não vamos executar nada. Vale lembrar que em si nós não temos o tipo buleano, Portanto qualquer valor que não seja zero vai ser tomado como verdadeiro. Vamos dar uma olhada na implementação desta estrutura condicional simples. Vou criar
uma variável. Vou fazer um programa que peça para o usuário digitar um inteiro. Vou receber esse valor na nossa variável. E agora vou utilizar a estrutura que a gente acabou de aprender. Dá uma olhada aqui no nosso slide. A gente tem a sintaxe dessa estrutura. If, entre parênteses, condição, entre colchetes, a sentenças subordinadas. Aqui eu vou utilizar um operador aritmético. Lembrem que um operador aritmético ele retorna um valor e todo valor é interpretado como verdadeiro. Então, basicamente, essa nossa condição aqui seria sempre verdadeiro. Mas o que eu quero verificar é se o resto da divisão
do valor digitado por dois vai ser equivalente a zero. Eu não preciso colocar os parênteses aqui porque eu já sei as regras de precedência, onde a precedência do operador de módulo é maior do que a precedência do operador de equivalência. Portanto, esta expressão aqui vai ser resolvida primeiro, vai nos retornar um valor. E se o resto da divisão for igual a zero, colchetes, eu vou imprimir porcentagem de e parar. E executar o nosso programa. Agora vamos digitar aqui o número inteiro. Lembrando que a nossa estrutura condicional simples realiza uma atividade caso verdadeira e caso falso
não faz nada. Vou digitar o número quatro. 4 módulo de 2 é igual a 0, pois tem resto zero essa divisão. Como o zero é equivalente a zero, nós vamos obter aqui o valor verdadeiro. Por isso, vai acontecer uma impressão na nossa tela. Quatro é par. Vou tentar executar esse código novamente. Vou só apertar para cima, mas poderia também clicar na nossa extensão. E vou digitar agora o número ímpar set. Digitando sete, observe que nada acontece. Bom, pessoal, aqui a gente tem que tomar alguns cuidados, principalmente com os nossos operadores, viu? Prestem bastante atenção para
não confundir o operador de equivalência e operador de atribuição. Nesse caso aqui, daria um erro, né? Porque a gente estaria tentando atribuir o valor zero ao valor dois. Mas, né, se a gente tivesse fazendo uma verificação dessa forma aqui, não daria erro. a gente atribuindo o valor zero a x isso nunca seria verdadeiro. Se eu tivesse atribuindo o valor um, observem o que que aconteceria. Independentemente do número que eu digitasse, digitando o número ímpar, o nosso print aconteceria. Então, bastante cuidado com isso. Um outro cuidado que a gente deve tomar é sempre se lembrar que
valores positivos são interpretados como verdadeiro em ser. Vou deixar isso aqui comentado para vocês, tá? Deixei aqui também algum dos operadores para que a gente possa se lembrar deles. Seria interessante, Pessoal, se a gente pudesse imprimir também caso o nosso número fosse ímpar. E com o que a gente aprendeu, isso já é possível. Basta a gente utilizar o operador diferente. Vamos executar esse nosso código. 8 par 7 é ímpar. Observem que o que eu fiz aqui foi utilizar duas estruturas condicionais. simples, porém existe um jeito mais otimizado paraa gente estar fazendo isso. E quando eu
digo otimizado, eu não quero dizer só mais fácil de escrever, mas também mais rápido do seu processador estar executando. Aqui na tela de vocês, a gente tem a solução que eu acabei de construir, onde nós estamos utilizando duas estruturas condicionais simples. A única diferença na escrita do código é que aqui nós não fazemos o uso do colchetes. É possível que você não faça o uso do colchetes caso você tenha somente uma sentença. Lembrando que a sentença ela é sempre finalizada com ponto e vírgula. Então, se você só tiver um ponto e vírgula depois do seu
if, você não precisa est utilizando o colchetes, caso contrário, ele é obrigatório. E à esquerda nós temos um diagrama que ilustra o funcionamento dessas duas estruturas condicionais simples. O nosso programa pede para que o usuário digite o inteiro. A gente salva o número digitado em uma variável. A gente verifica se x é divisível por 2. Caso verdadeiro, a gente imprime que esse número é par. Caso falso, a gente não faz nada. O próximo passo é Verificar se xível por 2. Se essa afirmação for verdadeira, a gente imprime que o nosso número é ímpar. Caso contrário,
a gente não faz nada e chegamos ao fim do nosso programa. A questão aqui, pessoal, é que o nosso código, ele vai sempre realizar essas duas verificações. Primeiro, ele vai verificar se x é divisível por 2. E mesmo que essa afirmação seja verdadeira e ele imprima que o número é par, ele vai executar o restante do nosso programa, verificando se xível por 2. E aí, quando retornar falso, o nosso programa vai chegar ao fim. Existe uma maneira de evitar que a gente tenha que fazer essas duas verificações, pois se x é divisível por 2, é
claro que essa afirmação de baixo não pode ser verdadeira, já que não é possível que o número seja par e ímpar ao mesmo tempo. Vamos ver como a gente pode estar implementando essa solução de um jeito mais otimizado, agora utilizando a estrutura condicional de dois caminhos. Observe no nosso diagrama que o nosso programa pede um inteiro, ele armazena esse inteiro em uma variável e ele verifica se x é divisível por 2. Se verdadeiro, ele executa um conjunto de instruções. E se falso, ele executa um outro conjunto de instruções. Agora, não precisando verificar duas vezes, mas
colocando sentenças para serem executadas, caso falso. Prestem atenção. Na estrutura condicional simples, quando a nossa condição não é atendida, quando a gente obtém o valor falso, nada acontece no nosso código. A gente pode fazer uso de mais de uma estrutura simples. E aí, mesmo que aqui nada seja executado, a gente vai realizar uma verificação, podendo Executar ou não. E agora a gente tá vendo a estrutura condicional de dois caminhos, onde caso a nossa afirmação seja falsa, a gente consegue enviar instruções a serem executadas também. Vamos dar uma olhada aqui na sintaxe. A gente vai utilizar
o if, condição, colchetes, sentenças subordinadas a serem executadas se a condição for atendida. Depois vamos utilizar o comando els e as sentenças subordinadas a serem executadas caso a condição seja falsa. Vamos modificar esse nosso código para utilizar a estrutura condicional de dois caminhos. Basta adicionar o termo else. E pessoal, como nós só temos uma sentença a ser executada dentro do nosso if e dentro do nosso ELS, não é obrigatório que a gente utilize os colchetes. Vamos salvar, executar o nosso programa. Digite o número inteiro. Eu vou digitar oito. Ele vai imprimir que oito é par
e não vai precisar realizar nenhuma outra verificação. Já vai finalizar o programa. Caso eu digite um número ímpar, ele vai verificar que o módulo de 7 por 2 é equivalente a 1, ou seja, o resto da divisão de 7 por 2 é 1. 1 é diferente de zero. E então vai executar a impressão que está dentro do nosso ELS, sem ter que realizar uma outra comparação como a gente tinha antes. Nesse ponto aqui, eu gostaria que estivesse bem claro a diferença da utilização de uma estrutura de caminho simples para uma estrutura de caminho Duplo. Na
estrutura de caminho simples, quando obtemos o valor falso, você pode utilizar duas estruturas de caminho simples para conseguir o mesmo comportamento numa estrutura de caminho duplo. Porém, você vai ter que estar realizando duas verificações. Ou seja, se aqui nós estamos utilizando uma equivalência para que a gente pudesse utilizar uma estrutura de caminho simples com esse mesmo comportamento, aqui embaixo nós teríamos que utilizar o operador de diferença. Já com estrutura de caminho duplo, nós não precisamos realizar uma outra verificação, ou seja, utilizar um outro operador condicional, mas podemos colocar qual sentença vai ser realizada caso a
gente obtenha o valor falso. Bom, pessoal, o próximo passo é a gente entrar de cabeça nessas estruturas condicionais simples e estruturas condicionais de caminho duplo, vendo como a gente pode estar utilizando uma dentro da outra. Mas antes disso, eu vou salvar esse nosso arquivo e vou voltar no nosso arquivo número 13 de operador ternário. Eu quero deixar bem claro para vocês qual que é a diferença entre o operador ternário e a estrutura de caminho duplo. E a primeira coisa que vocês devem entender é que tudo que o nosso operador ternário é capaz de fazer, as
nossas estruturas condicionais também são, seja ela simples ou ela de caminho duplo. Porém, nem tudo que a nossa estrutura condicional é capaz de fazer, o nosso operador ternário pode fazer. Se você pegou o que eu disse, você deve estar pensando: "Então, eu não preciso fazer uso do operador ternário, eu posso simplesmente utilizar as estruturas condicionais que eu aprendi." Se você chegou nessa conclusão, você está correto. Então, vou mostrar isso para vocês, modificando esse nosso programa, onde nós encontramos o maior número, utilizando agora uma estrutura condicional de caminho duplo. O usuário aqui digitou dois números. A
gente armazenou esses números em duas variáveis, X e Y. A gente vai verificar se X é maior que Y. Caso X seja maior que Y, a gente vai armazenar o valor de X na variável maior. Caso contrário, a gente vai armazenar o valor de y na variável maior. Como a gente só tem uma sentença, uso das chaves não é obrigatório, mas observem que utilizando o nosso operador ternário, nós vamos escrever menos. E caso você estivesse utilizando estrutura condicional simples, fazendo a verificação oposta, se x for menor que Y, o nosso operador ternário teria um desempenho
maior, OK? Mas caso você esteja utilizando a estrutura condicional de caminho duplo, ele vai ter o mesmo desempenho. Bom, pessoal, eu não, como eu quero que vocês tenham clareza sobre todos os comandos que vocês estão utilizando, eu vou trazer uma explicaçãozinha que vai te ajudar a Decidir quando você pode ou não fazer uso do operador ternário. Pessoal, se você tiver mais de uma sentença, imagine que aqui nós teremos também a variável menor e nela nós salvaríamos ali o valor de y. Nesse caso, a gente já precisaria fazer uso dos colchetes. Nós não poderíamos substituir essa
estrutura condicional pelo operador ternário. Repetindo, se você tiver mais de uma sentença a ser executada, caso verdadeiro ou caso falso, você não vai poder substituir pelo operador ternário. Uma outra coisa que você não pode também é caso você tenha uma sentença que chame uma função. Em outras palavras, se você tiver qualquer coisa que não seja uma atribuição dentro do seu ifels, você também não vai poder substituir ele pelo operador ternário. Recapitulando, eu só posso utilizar o meu operador ternário no lugar da minha estrutura condicional, caso eu só possua uma sentença a ser executada dentro do
meu ifu else. que essa sentença seja uma atribuição de valor. Se eu tiver um print f, eu já não vou poder estar fazendo o uso do operador ternário. E por que que isso acontece, pessoal? Observem que o nosso operador ternário, ele faz uma verificação se x é maior do que y e ele simplesmente retorna o valor de x ou o valor de y. E aí a gente pode já atribuir isso na variável maior. Então nós vamos poder utilizar aí o nosso operador ternário. Quando a gente tiver uma sentença desse tipo, vou deixar aqui comentado para
vocês e vou atualizar o nosso arquivo de operador ternário. Muito bem. Então nós vamos agora ver ifs aninhados ou estruturas condicionais aninhadas, que é a utilização dos comandos que a gente já aprendeu, uma estrutura condicional simples ou uma estrutura condicional de caminho duplo, porém uma dentro da outra. Para que a gente não se perca, eu vou estar utilizando uma ferramenta chamado Excalidraw. Você pode estar vindo no Google, procurando por Excalidraw e acessando esse site. E a gente vai desenhar aí o nosso problema. Só para deixar claro aqui o que que nós estamos fazendo, vamos fazer
um programa. Deixa eu aumentar, tentar aumentar a fonte aqui. Vamos fazer um programa que verifica se um número é negativo ou não. Caso positivo, verifica se é zero ou não. Primeiro passo vai ser imprimir na tela digite um número. O segundo passo vai ser armazenar o número digitado. O terceiro passo vai ser verificar se esse número é negativo. Caso esse número seja negativo, nós vamos realizar a impressão se o número é negativo e vamos finalizar o programa. Caso o número seja positivo, na verdade aqui nós vamos colocar verdadeiro. Só para não ficar diferente do que
a gente já vinha fazendo. Eu vou mudar de lado aqui, pessoal, porque antes a gente já tava utilizando O caminho à direita para verdadeiro, né? Caso a gente obtenha o valor falso, nós vamos colocar aqui uma nova comparação, verificando se x é igual a 0. Caso x seja igual a 0, nós vamos imprimir é zero. Caso X seja diferente de zero, nós vamos imprimir é positivo. Realizada essa impressão, a gente vai finalizar o programa. Vou colorir aqui para ficar um pouquinho melhor da gente visualizar. E bom, pessoal, o mais importante é você conseguir tá identificando
se a gente vai estar utilizando uma estrutura condicional simples ou uma estrutura condicional de caminho duplo. Para fazer isso, analisando o nosso diagrama, observem que se verdadeiro, nós vamos realizar uma impressão, tá tranquilo? Então, dentro do nosso if, a gente vai ter só um print f, como na condição falsa. Nós vamos ter alguma coisa a ser realizada, alguma sentença vai estar ali dentro. A gente vai ter que estar utilizando o nosso operador de caminho duplo para resolver essa etapa aqui. Essa etapa resolvida. Observem que o que nós estamos vendo é o que vai estar dentro
do nosso ELS, que será um outro if. E analisando, a gente sabe que esse if também vai ser uma estrutura de caminho duplo, pois quando falso ele realiza uma sentença. Se não realizasse nada, seria somente um if, não teria um else. Mas como tem algo a ser feito quando a gente obtém o valor falso, a gente vai precisar utilizar o else. Vamos implementar esse nosso programa. Print X, print F, digite um número. Scan F, porcentagem D. Vamos armazenar esse número na variável que a gente criou. Vamos fazer um if que verifica se x é negativo.
Nós vamos ter somente uma sentença dentro desse nosso if. Então, não precisamos utilizar o colchetes se o número é negativo. Caso contrário, aí nós vamos ter uma outra estrutura condicional que vai verificar se esse número é igual a zero. Como só temos uma impressão para ser realizada, se essa estrutura for verdadeira, a gente vai imprimir é zero. Lembrando que eu posso utilizar tanto porcentagem D quanto porcentagem I. Caso contrário, nós só temos uma operação, então também não precisamos utilizar as chaves. Print F é positivo. Vamos executar esse nosso código. Digite um número menos 5. Observe
que a gente tá obtendo um erro porque nós não utilizamos o operador de endereçamento, o nosso scan f. Agora sim. - 7 é negativo, 5 é positivo, 0 é zero. Bom, pessoal, antes da gente salvar esse arquivo, tem mais uma coisa envolvendo aí a sintaxe do nosso ifels, em específico a sintaxe do nosso ifels aninhado. Eu quero que vocês entendam que todo if else ele é interpretado pelo computador com uma só sentença. Ou seja, quando nós fazemos uso do if else, if condição sentença else sentença, eu quero que vocês imaginem que aqui nós temos como
se fosse uma só linha de código pro nosso compilador. E aí você pode estar se perguntando, mas que diferença essa informação vai fazer? Essa informação vai fazer diferença na hora de você estar lendo o código de outras pessoas ou até mesmo escrevendo seus. Como o nosso if else ele é interpretado como uma só coisa, nós podemos escrever ele da seguinte maneira. Percebam que o nosso código tem o mesmo comportamento que nós vimos no nosso diagrama, porém eu posso fazer if, a sentença caso verdadeira, else. E eu já havia dito Para vocês que quando a gente
utiliza o ELS, caso a gente tenha uma só instrução a ser realizada, nós não precisamos dos colchetes. E agora eu disse para vocês que o if else ele se comporta como uma só instrução. É assim que o nosso compilador vê a instrução e ifs. Portanto, a gente pode estar escrevendo esse código dessa maneira. Se você tiver um pouquinho de dúvida em relação a isso, você sempre pode fazer uso dos colchetes, mas é muito comum que a gente se depare com o código escrito dessa maneira. OK? Vou salvar esse arquivo. E só para garantir que ficou
claro para vocês, eu vou trazer um outro exemplo, onde caso o número seja positivo, nós vamos estar realizando o conteúdo de somente uma instrução que vai ser o if else. Vamos verificar se esse número positivo é par. Caso seja, nós vamos imprimir. Caso não seja, nós também vamos realizar uma impressão. Essa aqui é uma outra situação onde a informação de que o nosso compilador interpreta o nosso ifó instrução pode afetar na escrita de código. Um pequeno detalhe que eu não cheguei a comentar, mas que agora vocês já têm condição de entender, que vai clarificar algumas
coisas, principalmente para aqueles que já aprenderam a programarem uma outra linguagem, é em relação à identação. Pessoal, em linguagens onde nós utilizamos símbolos para determinar o final de uma sentença, ou seja, o nosso ponto e vírgula, geralmente a gente tem uma flexibilidade de não depender da identação. linguagens onde a gente utiliza o símbolo das chaves, que eu vivo confundindo aqui nos vídeos e trocando por colchetes, nós também temos essa flexibilidade de não precisar nos preocupar com a identação. O que isso quer dizer? Isso quer dizer que o meu código estivesse escrito desta maneira aqui, ele
funcionaria exatamente igual, pois eu sei que após o meu if, eu vou encontrar uma sentença e o fim dessa sentença vai ser indicado pelo ponto e vírgula. Na prática, daria pra gente escrever códigos ainda mais absurdos, podendo até deixar todo ele em uma só linha. E isso não geraria nenhum problema para nós na hora de executar esse nosso programa. É claro que nós vamos sempre entar o nosso código para facilitar a nossa compreensão dele, mas eu quero mencionar para vocês que isso não é obrigatório na linguagem C. Em outras linguagens que não fazem uso do
símbolo que declara o fim de uma sentença ou dos colchetes quando a gente tem mais de uma sentença dentro de um if, o seu código vai se comportar de uma maneira inesperada. Bom, basicamente em C, você pode estar adicionando quebra de linha em quase todos os lugares, mas também tem lugares onde isso não é possível. Por exemplo, dentro das aspas, a partir do momento que você abriu aspas, você não vai poder estar adicionando ali uma quebra de linha. Você precisa fechar suas aspas. E eu já mostrei para vocês anteriormente como é que a gente realiza
esse tipo de quebra de linha para organizar melhor a nossa impressão com print f. Dito isso, eu sei que para muitos de vocês esse conteúdo de estrutura condicional pode estar gerando um pouquinho de confusão, mas fiquem tranquilos que nós vamos ter o nosso momento de prática para poder remediar essa sensação que vocês estão tendo agora. Antes disso, a gente só precisa ver mais um comando que tem um comportamento bem semelhante à nossa estrutura condicional simples. Muito bem, já salvei o nosso arquivo e agora a gente vai aprender uma outra estrutura de controle condicional da linguagem
C. Vamos deixar de lado que a gente já viu, focar nessa nova estrutura. Depois que a gente aprender sobre seu funcionamento, eu faço uma comparação dela com tudo que a gente já viu até agora. Então vamos lá. O comportamento dessa nossa nova estrutura condicional é o que está aparecendo no diagrama na tela de vocês. Nós temos uma variável e nós verificamos o valor dessa variável. Supondo que essa variável seja do tipo inteiro, caso o valor dela seja um, a gente vai executar determinadas instruções e sair do nosso programa. Caso o valor seja dois, a gente
vai executar outras instruções e sair do nosso programa. O caso default vai ser utilizado quando o valor não for nenhum dos inseridos acima. Nós temos aqui também essas setas, pois na linguagem C é possível passar de um caso para o caso seguinte. E é isso que essas setas estão nos indicando. Vamos dar uma olhada como é que é a sintaxe desse comando. Pois bem, a sintaxe desse Comando é suitch. a gente coloca entre parênteses qual variável nós vamos estar utilizando. Abrimos as chaves e então utilizamos a palavra reservada case. Colocamos um valor e as instruções
que nós gostaríamos de executar. E aqui nós temos também o comando break. Comando break ele não vai ser utilizado só no switch, tá pessoal? E ele serve para que a gente saia, interrompa a execução de determinada instrução, no caso, a execução do sutch. Observe que se nós tivéssemos uma variável inteira e aqui nós tivéssemos o valor um e o valor dessa variável fosse um, a gente quisesse realizar uma impressão como este valor é um, logo em seguida nós gostaríamos de interromper todos os casos que estão abaixo saindo do comando switch. E é para isso que
o comando break existe. Vamos dar uma olhada em um exemplo de código, onde nós temos um programa que pede que o usuário digite um ou dois. Nós armazenamos a informação digitada pelo usuário na variável opção. Então nós executamos a instrução switch com a variável opção. Caso o valor da variável opção seja um, nós vamos imprimir no terminal, você digitou um e vamos interromper a execução do sutch. Caso o valor da variável opção seja dois, nós vamos imprimir na tela. Você digitou dois, vamos interromper a execução da instrução sutch. Caso o valor seja diferente de um
ou dois, nós vamos imprimir: você não digitou nem um nem dois. Observem que no caso default não é necessário que a gente utilize o break, pois após realizar essa impressão, nós não vamos ter nenhuma outra opção logo abaixo. Então essa instrução sutchizada. Vamos dar uma olhada aqui na execução desse nosso código. Pessoal, primeira coisa que eu quero mostrar para vocês é o que acontece caso a gente não esteja utilizando a palavra break. Vou remover a palavra break do caso um. Eu vou digitar o valor um. E observem que foi impresso na nossa tela. Você digitou
um e você digitou dois. Quando a gente não faz uso da palavra reservada break para interromper o sutch, ele vai acessar o caso um e vai acessar o caso dois sem realizar nenhum tipo de verificação. Portanto, a partir do momento que a gente tenha entrado em algum desses casos, se a gente não encontrar o break, a gente vai entrar em todos os seguintes. Mas no caso dois, ele encontra o break e então interrompe a execução do comando switch. Se eu remover o break do caso dois, observe que a gente vai entrar no caso um, vai
realizar a impressão e depois nós vamos realizar todas as instruções que estão nos casos abaixo até finalizar a instrução sutch. Se eu digitar o valor dois, observem que nós vamos entrar no caso dois. Nós não vamos acessar o caso um, pois o valor da variável opção é dois. E ao entrar no caso dois, nós vamos executar todas as instruções que estão nos casos abaixo. Por que que eu tô trazendo essa explicação para vocês? Observem que essas setas que ligam as instruções servem exatamente para isso. Por exemplo, pode ser que caso o valor da variável seja
um, eu simplesmente execute as minhas instruções e saia do meu programa. Então essa seta vermelha não vai existir. Mas pode ser que eu queira que a partir do caso dois, ou seja, quando o usuário inserir o valor na variável dois, a gente execute essas Instruções e execute todas as demais instruções, exceto a anterior da default. Então essa seta aqui estaria existindo, eu colocaria um break, nesse caso três, então nós não executaríamos o caso default e depois sairíamos do nosso programa. É um comportamento que raramente você vai precisar utilizar, mas é importante que você saiba que
ele existe. Vou mostrar aqui para vocês. Eu posso estar adicionando o break no caso um e adicionando o break no caso três. Agora, caso eu digite um, só vamos imprimir a mensagem, você digitou um. Caso eu digite dois, nós vamos imprimir, você digitou dois e você digitou três até encontrar o break. Caso digite qualquer valor que não faz parte do comando switch, nós vamos utilizar o caso default. Pessoal, a presença do caso default, ela não é obrigatória. Se eu não utilizar o caso default e inserir um valor que não faz parte das verificações dentro do
meu comando sutch, nada vai acontecer. Simplesmente com o conteúdo da variável opção sendo 800, nós não temos nenhum caso que é atendido, portanto nada será executado dentro da minha instrução sutch. Uma outra coisa que eu gostaria de mencionar para vocês é que essa variável utilizada no comando switch, opção deve ser do tipo int, char ou enum. opção deve ser do tipo. O Enum é um tipo que a gente ainda não aprendeu, mas em breve a gente vai dar uma olhada nele, tá bom? E basicamente o que eu tenho a dizer sobre o comando switch é
isso. Nesse slide nós temos algumas informações importantes que já foram explicadas para vocês. Somente variáveis de tipos ordinais, já arinten podem ser Usadas para a seleção dos casos. Cada caso cobre apenas um possível valor da variável, portanto, somente constantes literais ou nomeadas do mesmo tipo da variável podem ser usadas. Constantes pessoal, são números que a gente insere no nosso código. Portanto, o número um seria uma constante literal, o número dois seria uma constante literal, o número três e assim por diante. Já o termo constante nomeada, pessoal, eu vou até corrigir porque a gente aprendeu que
pra gente criar uma constante nomeada, a gente utiliza o termo const, coloca o nome dessa nossa constante e atribui o valor na hora que a gente tá instanciando, certo? Isso daqui é uma constante nomeada, mas acontece que essa constante nomeada, ela não fazia parte da linguagem C até aproximadamente 1999. Quem inventou esse tipo de constante nomeada foi a linguagem C++ e depois isso foi aderido na linguagem C. No caso aqui nós temos um espaço de memória que armazena um valor, uma variável como qualquer outra, porém esse valor armazenado não pode ser trocado. Mas se a
gente tentar utilizar isso daqui no switch case, nós não vamos conseguir. Observe que a gente já tá encontrando aqui um problema. Então, por que que aquilo estava naquele slide? Porque existe um outro jeito, né, que na verdade tem um funcionamento diferente de criar uma constante nomeada. Seria um jeito mais antigo, inclusive menos seguro. Vou mostrar para vocês por que é utilizando o comando define. Se a gente Utilizar o comando define, a gente pode colocar aqui constante nomeada dentro do nosso sut case, colocar o valor um, que é o que já tava, né? Agora eu consigo
estar executando, inserindo o valor um e o comportamento aí é o que a gente já esperava. Por que que é possível fazer isso, pessoal? Quando a gente tá utilizando o comando define, a gente não tá armazenando um valor na memória RAM e impedindo que esse valor seja modificado. A única coisa que o nosso compilador tá fazendo, ele tá procurando todas as ocorrências da palavra constituindo por um antes de gerar o nosso arquivo executável. Então, na prática, o que tá acontecendo aqui é a mesma coisa que se eu tivesse escrevendo o valor um, removendo essa linha
e então compilando. E é isso que o compilador vai fazer. Mas fica aí essa explicação para vocês, que vocês podem estar utilizando o comando define para poder substituir uma palavra por um número. Não é recomendado que vocês façam isso. Então, normalmente vocês vão ter que usar constantes literais, ou seja, números quando você tiver utilizando o switch. A única outra opção, que na verdade é a mesma coisa que tá utilizando o número, é utilizar o comando define, onde você vai substituir a palavra que estiver aqui por um outro número. Tudo bem? E aqui muita atenção que
eu disse que a única opção é o número, mas eu também disse para vocês que a variável pode ser do tipo char. Vale lembrar que o tipo char, por trás das cortinas, ele é tratado como um inteiro, mas ele também pode ser utilizado. Então a gente poderia ter aqui case, B e C e está utilizando uma variável do tipo char. Digite Aar o B, vou digitar A, você digitou A. Digitar o B, vou digitar B, você digitou B. Vou salvar esses nossos arquivos aqui e vamos agora dar uma olhada nesse tipo Enumu. Pessoal, a gente
vai estudar os registradores e aí esse tipo Enum vai fazer bem mais sentido, mas nesse momento eu quero que vocês pensem nesse nosso tipo Enum como um comportamento e finalidade bem semelhante ao do define. Vão existir momentos onde a gente não vai querer estar utilizando aí números no nosso código. a gente vai querer usar palavras para deixar o nosso código mais legível. E aí a gente pode estar utilizando o tipo Enum. Eu posso estar criando aqui o meu tipo Enum fora da função main ou dentro da função main. Eu vou fazer isso aqui fora. E
aí eu vou usar a palavra reservada enum. Eu vou usar uma outra palavra, vai chamar aqui dia, por exemplo. Vou usar as chaves e vou colocar aqui domingo igual a 0, segunda igual a 1, terça = 2, quarta = 3, quinta = 4, sexta igual a 5, sábado igual a 6 e ponto e vírgula. Pessoal, eu posso fazer tanto dessa forma ou não colocar o símbolo de igual. Como aqui a gente tá começando do valor zero, eu não preciso colocar esses números. Então vou deixar essa linha aqui comentada. Bom, eu já entendi mais ou menos
aí que eu posso utilizar a palavra domingo como o número zero, a palavra segunda como o número um, mas como é que faz para eu efetivamente fazer uso disso? É Diferente do definer, não basta a gente sair escrevendo. Depois que a gente define ali em cima, a gente tá definindo o nosso tipo. Tá aqui definindo o tipo enum dia. Nós vamos ter que criar uma variável do tipo enum dia. Criando uma variável do tipo enum dia. E para isso nós vamos utilizar agora o comando enum dia o nome da nossa variável. E pronto, a partir
desse momento a gente acabou de criar a nossa variável. a gente não colocou nenhum valor nela ainda. Eu poderia vir aqui e colocar o valor dois, tá? Mas eu não coloquei nenhum valor porque eu vou fazer um print f e vou pedir pro usuário digitar digite um dia da semana de zero a 6. Vou utilizar o scan f. E para fazer uso do enum nós vamos utilizar aí o porcentagem I ou porcentagem D. e o endereço da nossa variável. Observem, pessoal, que o nosso usuário está inserindo um número. Então, o enum ele é utilizado para
que a gente possa substituir números por palavras somente internamente dentro do nosso código. Toda vez que a gente for atribuir um valor a uma variável do tipo enum, a gente vai ter que est atribuindo o valor inteiro, tá bom? Vamos colocar aqui a variável D no nosso sutch. E agora utilizando o Enu, eu posso vir aqui e escrever domingo print F. Domingo, dia de descansar, case segunda e por aí vai. Vou deixar comentado aqui para vocês. Vamos adicionar um inteiro a nossa variável menu. Podemos utilizar os nomes já que estamos utilizando a variável do tipo
enum. Bom, tá aí. Vamos executar para mostrar para vocês funcionamento desse nosso código. Vou digitar zero. Domingo, dia de descansar. Ele acabou imprimindo segunda também porque eu não adicionei o meu break. Agora sim, domingo, dia de descansar. Então assim, não tem muita utilidade prática para nós, principalmente nesse momento, tá? Mas fica aí explicado para vocês que existe essa variável enum. Uma outra forma de tá escrevendo ela é assim, tá? formatando o nosso texto. E pessoal, utilizem isso daqui só se vocês realmente aí quiserem, por exemplo, colocar um menu na sua tela, onde o usuário vai
ter as opções ali de zero até seis, mas você não quer colocar aqui case zero, você quer trazer uma informação ali, por exemplo, case desistiu, case fechou o programa, né? Vocês podem estar fazendo uso disso, mas fora essa situação, eu não vejo muita utilidade prática no Enum, porém eu tô um pouco insatisfeito com a explicação Que eu dei. Acho que pode ter deixado algumas brechas. Então, vou dar uma pequena pincelada a mais no Enum aqui para vocês, trazendo um outro exemplo de uso. A gente poderia estar fazendo aí enumen e a gente coloca aqui false
true. Agora, nesse caso aqui, eu estou criando o tipo enum bole buleano, criando uma variável do tipo enum buleano. Vamos ver esse conteúdo aqui para para ficar melhor de visualizar. E aí eu poderia utilizar aqui o termo enuman, e criar a variável aqui, por exemplo, BL. E agora eu posso utilizar a atribuição dentro dessa variável com o termo true ou com o termo false, onde true vai ser substituído por um e false vai ser substituído por zero. Se eu quiser especificar isso, eu posso deixar ali como primeiro falso que vai ser zero, e o true
vai ser 20, por exemplo. Depois eu posso fazer a impressão desse valor, utilizando a porcentagem 20. E aí observem que foi exatamente o comportamento que a gente esperava. Para tentar deixar ainda mais claro esse nosso tipo Enum, vou deixar mais alguns comentários aqui e trazer uma explicaçãozinha rápida para vocês. Quando o usuário está digitando, nós devemos receber um tipo inteiro. Quando nós formos atribuir um valor A uma variável do tipo N1, nós podemos utilizar um valor inteiro ou uma palavra que faça parte do tipo que criamos, que vai ser convertida para um inteiro. Em outras
palavras, pessoal, você pode pensar no tipo Enum como um tipo inteiro. Quando a gente for receber uma informação pelo scan f, a gente precisa receber um número inteiro. Porém, quando a gente for atribuir um valor, assim como a gente fez aqui na linha 11, nós podemos atribuir utilizando a palavra true ou também utilizando o valor 20. Da mesma forma que a gente pode fazer isso, a gente pode fazer comparações. Ifl for equivalente a true, eu vou imprimir comparação bem suucedida. Da mesma forma, eu posso fazer BL é igual a 20. Vamos dar uma olhada aí
nesse nosso código. Estamos imprimindo a variável BL. Vamos verificar se é 20 mesmo. Verificamos que ela é. Fizemos uma comparação bem-sucedida utilizando a palavra true. E uma outra comparação bem-sucedida utilizando o número inteiro, 20. De toda maneira, pessoal, o importante aqui é que você tenha entendido a finalidade do uso, tá? E as Limitações quando a gente tá utilizando aí o Enum. Basicamente, ele serve para que a gente visualize o nosso código de uma maneira um pouquinho melhor, tirando alguns números e trocando por palavras, palavras que a gente já declarou anteriormente quando a gente criou esse
tipo, tá? E existe essa limitação, apesar da gente conseguir usar essas palavras dentro do nosso código, na hora de receber um valor para uma variável do tipo EN1, a gente ainda precisa estar recebendo um valor inteiro. Bom, pessoal, quero trazer para vocês aí agora uma explicaçãozinha sobre quando você deve estar utilizando uma estrutura condicional simples, quando você deve estar utilizando uma estrutura condicional de caminho duplo e quando você deve estar utilizando o nosso comando sutch. De maneira super geral, a estrutura condicional simples, quando ela pode ser substituída pela estrutura condicional de dois caminhos, ela deve
ser substituída, tá? Observem aqui quantos quadrados laranjas a gente tem, tá? Isso aí representa uma expressão utilizando um operador relacional que vai retornar aí verdadeiro ou falso ou um operador lógico também, né? E a gente vai estar reduzindo aí o número de comparações. Então, a gente tá com o código que faz a mesma coisa, mas agora ele só faz uma comparação. Então, é mais interessante que você utilize a estrutura condicional de dois caminhos. Na prática, nem sempre você vai conseguir identificar isso, mas a dica que eu dou para você é analisar quando você constrói a
sua solução utilizando um condicional simples, se essa verificação é o oposto de alguma outra. Não precisa ser necessariamente a próxima. Pode ser que você tenha inscrito ela em outro lugar do seu código, mas se você tá realizando uma verificação que é oposta à outra, por exemplo, nesse caso aqui, X é divisível por 2, X não é divisível por 2, tá utilizando operador de igualdade, utilizando operador de diferença. E caso X seja divisível por 2, é impossível que a afirmação de baixo seja verdadeira, né? Se a de cima for verdadeira, é impossível que a de baixo
seja. E o inverso também acontece se a de cima for falsa, né? Se X não for divisível por dois, é impossível que a de baixo não seja verdadeira. Isso é um bom indicativo que você pode estar construindo o seu código utilizando uma estrutura condicional de dois caminhos. Já o sutch, ele é sempre a melhor opção quando você puder tá fazendo o uso dele, porque no sutch a gente só tem uma comparação. Não vou explicar para você como agora, mas eu quero que você entenda que o sutch ele só realiza uma comparação. Ele simplesmente pula ali
pro caso, né, onde o valor é o mesmo que está contido na variável e já executa as instruções. Ele não precisa tá passando ali caso por caso, ele vai direto no caso correto. Por isso o switch ele é a melhor opção sempre que a gente pode escolher ela. Mas e aí, como é que eu vou saber então se eu posso ou não posso tá fazendo uso do comando switch? Pessoal, para identificar isso, você não vai ter muita dificuldade. Basta você observar se a entrada, a variável que você está comparando ali dentro dos seus ifs são
do tipo char inteiro. Se elas forem, você já tem um Indicativo aí bom de que você pode tá utilizando o sutch, porém não é garantido para que você realmente possa estar utilizando o sutch, você vai verificar que os seus ifs estão utilizando somente o operador lógico de equivalência. Vou mostrar isso aqui para vocês no código. Observem aqui que a gente tem um programa onde o usuário digita o dia da semana. Nós vamos imprimir aí alguma mensagem relacionada ao dia da semana digitado. Primeiro requisito que está sendo atendido, o usuário está dando como entrada um inteiro.
Se ele der como entrada um inteiro um caracter, é um indicativo que a gente pode estar refaturando esse nosso código, ou seja, reescrevendo com comando switch. Verificado isso, basta a gente verificar se o que está dentro do nosso if é somente a operação de equivalência. E observe que dentro do nosso IF a gente só tem aí as operações de equivalência. Então, já compensa a gente fazer uso do comando switch, pois nesse nosso código a gente tá realizando todas essas verificações. Mesmo que o usuário digite zero, a gente entra aqui, verifica que o valor digitado é
igual a zero, imprime e depois a gente verifica se o valor digitado é igual a um, vê que não é, não faz nada. Verifica se o valor digitado é igual a dois, vê que não é, não faz nada. Verifica se o valor digitado é igual a três, vê que não é, não faz nada. E por aí vai. Por isso, é melhor a gente fazer uso do comando switch. Vou deixar comentado para vocês como saber se posso transformar minha sequência de ifs em um switch. F aviso que essa é uma preocupação que vocês não deveriam estar
tendo nesse momento, otimização de código, mas eu também não posso deixar de mencionar, senão o nosso aprendizado perde todo sentido, né? Vocês podem ficar fazendo perguntas aí sobre qual a necessidade de utilizar o comando sutchit, porque ele existe, se é só uma questão estética na hora de escrever o nosso código, na hora de ler o nosso código. Então, acredito que seja importante aí trazer essas informações para vocês. Mas já adianta que se você tá tendo contato com a linguagem C agora, você não precisa se preocupar muito com isso. E chegou o momento da gente resolver
a nossa segunda lista, que tá disponibilizada em um link aqui na nossa descrição. Só posso mostrar o caminho. Quem deve percorrê-lo são vocês. Então, espero que você pause esse vídeo e vá praticar tudo que você aprendeu até aqui sobre estruturas condicionais. Vamos dar continuidade aos nossos estudos sobre estruturas de controle. E se vocês estiverem se lembrando, eu disse que nós tínhamos as estruturas condicionais e as estruturas iterativas. E é exatamente isso que nós vamos aprender agora, as estruturas iterativas. Mas antes disso, vamos dar uma olhada no funcionamento de uma estrutura condicional. Nós agora temos a
capacidade de fazer um programa como esse, que esse diagrama está representando na tela de vocês, onde a gente pode perguntar pro usuário se ele gosta ou não de pão de queijo. A gente pode salvar a resposta dele em uma variável. Até então, a gente só tem utilizado alguns tipos simples. A gente não aprendeu salvar sequências de caracteres. A gente poderia ali entender que se ele digitar um, a resposta dele é Que ele gosta de pão de queijo. Se ele digitar zero, a resposta é que ele não gosta de pão de queijo. E fazendo uso da
estrutura condicional, a gente pode analisar essa resposta e baseado nela, tomar diferentes ações. Nesse caso aí, caso o indivíduo responda que gosta de pão de queijo, a gente faz essa análise e convida ele pr tomar um café. Caso ele responda que não gosta de pão de queijo, a gente fala que tá atrasado e vai embora. Para aqueles mais atentos, já perceberam, essa é uma estrutura condicional de dois caminhos, mas nós vimos também estruturas condicionais de caminho simples e também [roncando] vimos o comando sutch, que a gente pode utilizar para substituir o uso de várias estruturas
condicionais de caminho simples. Quando a gente tiver somente uma comparação e essa parte de análise da nossa estrutura condicional simples for simplesmente uma comparação entre tipo inteiro ou chá, a gente pode estar realizando essa substituição pelo comando sutch, que é mais otimizado, mais legível. Também vimos também aí que a gente pode utilizar o enu, mas que no final das contas nós só estamos utilizando palavras que estão associadas ao inteiro para deixar o nosso código mais legível. E agora nós vamos ver as estruturas iterativas. E a primeira pergunta que a gente deve se fazer é: qual
a nova capacidade nós iremos adquirir aprendendo estruturas iterativas? E de cara vamos responder essa pergunta. Agora nós vamos poder criar um programa como esse que está aparecendo na tela de vocês, onde a gente pergunta se a pessoa gosta de pão de queijo. A gente armazena a resposta Dela em uma variável, a gente analisa a resposta e caso a pessoa responda que não, a gente pergunta novamente: "Não gosta? Tem certeza? Você não gosta de pão de queijo?" Nós esperamos a pessoa responder, analisamos novamente a resposta e caso ela responda que não, nós perguntamos novamente e fazemos
isso até ela mudar de ideia. Quando a pessoa responder que sim, nós convidamos ela para tomar um café. Observem, agora nós vamos poder criar uma repetição até que determinada resposta seja dada ou que determinado critério seja atendido. Isso vai abrir portas para que a gente faça inúmeras coisas, dentre elas, um programa como esse que eu estou mostrando para vocês, mas também um programa que só vai finalizar caso determinada condição seja atendida. Bom, vamos dar uma olhada então nos tipos de estruturas iterativas que nós vamos aprender. Nós vamos aprender três estruturas iterativas, o comando while, comando
do while e o comando for. Mas existe uma diferença no propósito dessas estruturas iterativas. Nós veremos que o Wio e o DUI são estruturas lógicas. E o que isso quer dizer? Quer dizer que nós só vamos sair da nossa repetição, da nossa execução, da nossa iteração, caso uma condição seja atendida. Nós só vamos parar de perguntar se a pessoa gosta de pão de queijo caso ela responda que sim. Enquanto ela responder que não, nós vamos ficar repetindo essa pergunta. Ou seja, nós só saímos desse ciclo de repetição caso determinada lógica dentro do nosso programa seja
atendida. E nós estamos nos referindo a esse comportamento quando falamos de estruturas lógicas, OK? Nós também veremos o comando for, que é uma estrutura com o contador. E a diferença dessa estrutura interativa é que nós vamos fazer essa repetição um determinado número de vezes. Ou seja, a gente perguntaria ali se a pessoa gosta de pão de queijo 10 vezes. E depois de perguntar 10 vezes, a gente iria finalizar o nosso programa. Então vamos dar uma olhada no nosso comando while, o primeiro comando de estrutura iterativa lógica que nós vamos aprender. Aqui em cima a gente
tem a sintaxe do nosso comando, comando while, condição e sentenças subordinadas. Observem que a sintaxe é idêntica ao do comando if. A diferença é só a palavra. O em inglês significa enquanto enquanto determinada condição for verdadeira, ele vai ficar executando tudo que tiver entre as chaves. À esquerda, a gente tem um diagrama. Nesse nosso diagrama, nós podemos perceber que a primeira coisa a ser executada é a verificação da condição. Caso a condição seja verdadeira, nós executamos as nossas sentenças e depois verificamos a nossa condição novamente. Caso ela seja verdadeira novamente, a gente executa as nossas
sentenças e depois verificamos a nossa condição novamente até que ela Seja falsa. Quando ela for falsa, nós saímos da nossa estrutura de iteração while. À direita, nós temos um código utilizando o while. E vamos dar uma olhada nesse código. A gente tem a declaração da variável i e uma atribuição de valor que tá acontecendo numa mesma sentença. A gente tá observando aí de azul. Então, a gente tá criando a variável i, um espaço na memória que armazena um inteiro e estão armazenando o valor zero. E dentro da nossa condição temos um operador relacional, onde nós
vamos comparar o conteúdo da variável I com o número 10. Se o conteúdo da variável I for menor do que 10, observem que ali em cima nós já fizemos uma atribuição e atualmente esse valor é zero, nós obteremos o valor verdadeiro. O valor verdadeiro em C é o um. Realizada essa operação desse nosso operador que está em laranja, a gente vai obter o valor verdadeiro. Como nós vamos obter o valor verdadeiro, nós vamos executar todas as sentenças que estão entre chaves. Observe que nós temos a sentença de impressão, print f. onde nós estamos imprimindo o
valor da variável i e logo em seguida nós temos o operador de incremento. Se você tiver na dúvida, volte na parte de operadores. Lembrando que este operador nada mais é do que i = a i + 1. Ou seja, a gente tá salvando dentro da nossa variável i o valor anterior dela mais 1. Se era zero, vai passar a ser um. Terminada a Execução das nossas sentenças subordinadas, nós vamos voltar para verificar as nossas condições. Nesse caso, a nossa condição. I é menor do que 10. Bom, agora o I não é mais zero, ele é
1. E sim, 1 é menor do que 10. Feito isso, nós vamos realizar a impressão do valor, que é 1. Vamos realizar o incremento. Então, o I vai passar a ser do finalizada a sentenças subordinadas, nós vamos verificar a nossa condição. O I agora é 2. 2 é menor do que 10. Sim, 2 é menor do que 10. E nós vamos fazer isso repetidas vezes até chegarmos no valor 10. Repare, quando a gente tiver aqui o valor nove, nós vamos fazer a impressão do valor 9, nós vamos realizar um incremento, vamos alterar o valor da
variável I de 9 para 10. E vamos pra nossa condição. Aqui nós vamos fazer uma comparação. 10 é menor do que 10? Não, 10 não é menor do que 10. 10 é igual a 10, mas menor não é. Então nós vamos retornar o valor falso. Quando retornar o valor falso, nosso programa vai seguir ou no caso, se não tiver mais nada se executado, vai finalizar. Vamos dar uma olhada nesse comportamento no código, pessoal. Temos aqui o nosso código. E antes de mais nada, só para Matar a dúvida de vocês, eu reorganizei aqui os nossos arquivos.
São os mesmos arquivos de antes, mas agora eu vou estar separando em pastas, já que nós já estamos tendo aí bastante código escrito nas nossas aulas. Primeira parte vai ser a parte de compilador, biblioteca, entrada, saída e operadores. E essa segunda parte vai ser a nossa parte de estrutura de controle. Nós já vimos as estruturas de controle condicionais, agora estamos vendo as estruturas de controle iterativas. Bora, bora executar o nosso código. Temos aqui a nossa extensão, code runner. Executei o nosso código e observe o comportamento. Imprimimos até o valor 9. Bom, dá para dar uma
melhorada nessa nossa impressão. O valor de i é barra n. Vamos executar novamente. Observe que o valor da nossa variável está mudando. Vamos criar uma variável inteira chamada entrada. Não vamos colocar nenhum valor nela. E vamos colocar aqui um print f. Você gostaria de sair do programa? Zero. Sim. Ou melhor, finalizar programa. Quebra de linha. Um. Continuar programa. Opa, opa, opa. Agora a gente precisa aí tá mudando a nossa condição. Pessoal, eu vou colocar aqui entrada diferente de zero. Se eu executar isso aqui, o que vai acontecer? Não faço ideia. Não faço ideia. Porque tem
um problema nesse nosso código que a gente já conversou sobre. Por isso que a parte de variáveis, a parte de operadores é tão importante. Eu declarei a variável entrada, mas eu não atribuí nenhum valor a ela. E se a gente der uma olhada no que a gente já viu, a primeira coisa a ser verificada é a condição antes da gente executar a nossa sentença subordinadas. Observe no diagrama à esquerda. Portanto, como eu declarei essa variável, ela reservou um espaço na minha memória RAM e lá pode ter qualquer coisa. pode ter qualquer valor. Pode ser que
esse valor seja zero. E se esse valor for zero, nós vamos verificar que a entrada zero não vai ser diferente de zero, ou seja, vai ser igual. E se for igual, como nós estamos utilizando este operador aqui, nós vamos receber o valor falso. Caso a gente obtenha o valor falso, nós não vamos executar esse programa. Observem, o operador de diferença, ele só retorna verdadeiro quando os dois valores comparados são diferentes. Então, se eu comparar 1 e 200, vai retornar verdadeiro. Se eu comparar 1 e zero, vai retornar verdadeiro. Se eu comparar zero e zero, vai
retornar Falso. E eu estou dizendo que nós não sabemos qual o valor da entrada e se ele for falso, nada vai acontecer. O nosso programa não vai executar nada, não vai imprimir nada, porque todas as nossas sentenças estão dentro do nosso comando io. Então assim, parte aí de variável, né? Mas pra gente não ter esse problema, vamos colocar aqui o valor um. E agora a gente vai verificar na nossa condição quando formos executar que um é diferente de zero. Portanto, isso daqui vai retornar verdadeiro. E se nossa condição for verdadeira, nós vamos executar isso daqui.
A partir desse momento, nós vamos imprimir o número. O valor de vai ser zero. E nós vamos fazer o incremento desse valor, onde vai passar a ser um. Depois nós vamos imprimir. Você gostaria de sair do programa? E se eu executar esse nosso código agora, ele vai rodar para sempre. Vamos dar uma olhada. E ele não para. Caso isso aconteça, você vai apertar o comando contrl C. OK? Observe, chegamos no valor 13.159. Passamos ali por aquelas sentenças 13.159 159 vezes. O comando contrl C, esse que você usa para copiar as coisas no terminal, ele serve
para interromper algum programa que esteja executando. Então, o que que a gente vai fazer? Não é esse comportamento que a gente quer. A gente quer que a nossa lógica seja atendida ali e o programa para de executar. Vamos colocar o scan f, espaço porcentagem de operador de endereçamento pra gente acessar o endereço dessa variável. Entrada e vamos salvar o valor digitado aqui. Executando, ele vai fazer a verificação, vai ser verdadeiro. Então, vai executar sentenças subordinadas, vai imprimir o valor de I e depois vai perguntar: "Você gostaria de sair do programa?" Se nós digitarmos um, ele
vai continuar tendo a nossa condição como verdadeira, né? ele, nós vamos digitar um, vamos armazenar na variável entrada. Finalizada as sentenças, nós voltamos para fazer a nossa verificação entrada diferente de zero. Um vai ser diferente de zero. Nós vamos obter o valor verdadeiro e vamos executar tudo novamente. Quantas vezes eu vou poder fazer isso? Observe o valor de está aumentando, né? Eu estou digitando um. Eu posso fazer isso quantas vezes eu quiser, tá? E se eu quiser finalizar o programa, eu posso digitar o valor zero. Zero não é diferente de zero. Nessa comparação aqui, zero
diferente de zero vai dar falso, porque zero é igual a zero. E aí quando der falso, nós vamos sair do nosso while e vamos finalizar o nosso programa. Vamos limpar aqui e fazer uma última execução. Um, um, 1 1, 1 um 1. E agora vou digitar zero para finalizar o nosso programa. Pessoal, mais uma explicação, porque eu sei que para alguns pode ser um pouco mais Difícil de entender isso. Eu quero que vocês entendam que o comando uma vez que a gente entrar nele, ele vai ficar repetindo, executando isso daqui o tempo inteiro, tá? o
tempo inteiro até que a gente possa sair desse comando. Na dúvida, observem o nosso diagrama, onde a nossa condição ela é sempre verificada. Aqui tem um outro código, tá? Porque a gente vai ver também. Mas mas antes da gente entrar aí de cabeça, eu quero já mostrar o comando do while. Olha só, a gente teve que abrir atribuir um valor pra nossa variável entrada. Eu expliquei isso para vocês sobre o motivo, mas será que existe uma maneira de que nós possamos verificar a nossa condição depois de executar as nossas sentenças subordinadas? Ou seja, eu quero
quando chegar no meu executar tudo isso e só no final fazer a verificação. Por quê? Porque aí eu não preciso colocar o valor da entrada. Eu deixo lá, pode ser qualquer coisa que estivesse na memória, não tem problema. Mas daí eu pergunto pro meu usuário, peço para que ele digite o número e quando ele digitar eu salvo na minha memória. Bom, existe um jeito de fazer isso sim. É utilizando o comando do while. Vamos dar uma olhada aqui na sintaxe desse comando. Agora nós vamos colocar a palavra reservada do, as nossas sentenças subordinadas, o nosso
comando wi e a nossa condição no final. Observem a diferença entre esses dois comandos. À esquerda nós temos o fazendo a verificação da nossa condição no Começo, né? Ou seja, antes da gente entrar no dentro do nosso while, nós vamos fazer uma verificação. Já no comando do while, a gente realiza essa verificação. Ao final nós executamos as nossas sentenças subordinadas e depois realizamos essa verificação. Vamos então fazer essa modificação no nosso programa, onde agora nós teremos que colocar o comando do, as nossas sentenças. Ao final nós colocaremos o comando while e aí a gente vai
fazer a nossa verificação. Entrada. é diferente de zero. E olhem só, aqui também tem uma outra coisa que a gente tem que se atentar, que é o ponto e vírgula. Antes vou voltar aqui para mostrar para vocês. Depois dos nossos crochetes, não tinha ponto e vírgula. Agora, se a gente utilizar o comando do while, nós vamos colocar o ponto e vírgula. Pois bem, pois bem, fiz essa modificação. Vamos executar. Passando mais uma vez no funcionamento do nosso programa, nós vamos executar todas essas sentenças. Depois nós vamos fazer uma verificação. Se nós obtivermos o valor verdadeiro,
nós vamos executar as nossas sentenças de novo. Vamos realizar a nossa verificação. Se a gente obter o valor verdadeiro, vamos executar de novo até que a gente obtenha o valor falso. Fazendo um teste, ó, estou adicionando aqui a nossa variável valor um, que é verdadeiro, e nós continuamos executando o nosso Programa. Só alguns lembretezinhos. em C, todo valor diferente de zero é interpretado como verdadeiro. Eu disse para vocês que qualquer valor diferente de zero é verdadeiro, certo? Então, imagine só que em vez de colocar uma condição, eu coloque a variável entrada. Caso o valor da
variável entrada seja qualquer coisa diferente de zero, isso vai ser a mesma coisa que verdadeiro, correto? Se for um vai ser verdadeiro. Se for 10 também vai ser verdadeiro. Mas vamos pensar aí no um por enquanto. Se o valor da minha variável entrada for um, nós vamos ter ali verdadeiro. Se ele for zero, nós vamos ter falso. Então, pessoal, eu posso escrever esse mesmo programa desta forma aqui. Vamos fazer um teste. Um, olha, um é verdadeiro. Vamos ter que executar as nossas sentenças de novo. Vamos fazer o teste aqui com 800. 800 é verdadeiro. Então
ele executa as sentenças de novo. Vamos fazer um teste com -900. Opa, - 900 também é verdadeiro. Então ele executa todas as nossas sentenças de novo. Zero. Será que o valor zero ele é verdadeiro? Olha, eu estou colocando somente a minha variável aqui. O comando wi vai fazer essa verificação da condição, mas nós não temos nenhum operador relacional ou operador lógico. Nós só temos a nossa variável. Então vou digitar aqui zero e nós interrompemos o nosso programa, certo? Vimos aí rapidola as nossas duas estruturas lógicas, while e do while. Vimos também a diferença entre essas
duas estruturas lógicas. Basicamente no IO nós verificamos a condição antes de executar as nossas sentenças subordinadas, inclusive antes da primeira, né? E no comando do IO a gente já executa as nossas sentenças e só ao final verificamos a nossa condição. Já salvei aqui os nossos arquivos. Arquivos número 10, estrutura interativa while. Arquivo número 11, estrutura interativa do while. E agora vamos praticar um pouco e eu vou comentando para vocês sobre a escolha de uso do comando wi ou do comando do wiós vamos agora fazer um programa que verifica se um número digitado é ímpar ou
par e pergunta ao usuário se gostaria de digitar. A gente já fez esse programa anteriormente, verificando se um número é para upar, mas toda vez que a gente quisesse inserir um novo número, a gente tinha que tá executando novamente o nosso programa. E agora com as estruturas iterativas, isso não vai ser mais necessário. Vou criar aqui a variável entrada e vamos fazer com o comando while. Vou criar também a nossa variável de verificação. Vou chamar ela de verifica. Se verifica for diferente de -1. Agora vou utilizar o valor -1 aqui para não utilizar sempre O
zero. Nós vamos interromper a nossa execução. Como nós estamos utilizando o comando while, nós vamos precisar ter algum valor atribuído à nossa variável. Atribuir o valor um. Vamos então realizar a impressão. Digite o número inteiro. Vamos armazenar esse valor dentro da variável entrada, utilizando um operador de endereçamento. E agora vamos utilizar a estrutura condicional de caminho duplo que a gente já havia utilizado anteriormente, onde se a entrada módulo de 2, ou seja, o resto da divisão da entrada por 2 for equivalente a zero, nós vamos imprimir o número epá. Caso contrário, nós vamos imprimir um
número é ímpar. Agora, se eu executar esse programa dessa forma, observe que o verifica tem o valor um. Nós vamos pedir para que o usuário digite um número. Inclusive, vamos até fazer essa execução aqui. Vamos armazenar esse número, vamos identificar se ele é para ou ímpar. E depois vamos verificar a nossa condição novamente. E a nossa condição vai ser sempre Verdadeira, porque o valor de verifica vai ser sempre um. E como o valor da variável verifica não está sendo alterado, nós vamos ficar nesse loop aqui eternamente. Digite um número inteiro. 8 é par, 7 é
ímpar, 2 é par, 3 é ímpar. E o nosso programa não iria finalizar, a não ser que a gente utilize o comando contrl C. Para resolver isso, nós vamos primeir mensagem. Gostaria de digitar um novo número. Um. Inserir novo número. Menos 1. Encerrar programa. Vamos utilizar o scan f e armazenar o valor digitado na nossa variável verifica. E agora sim, pessoal, ao executar esse nosso programa, nós vamos digitar o número inteiro, oito. Ele vai nos informar que o número oito é par e vai perguntar se nós gostaríamos de digitar um novo número. Vou inserir o
valor room. Nós vamos então reiniciar todas as nossas sentenças subordinadas, já que a nossa condição vai ser verdadeira. Ao digitar cinco, nós vamos entrar na nossa condicional, vamos imprimir que o número é ímpar. Depois vamos perguntar Pro usuário se ele deseja encerrar o programa novamente. Vou digitar-1 e o nosso programa vai ser encerrado. Nesse caso aqui, o que vocês acham que seria melhor? Fazer uso do wi ou do comando do while? Se você parar para analisar bem, caso a gente utilize o comando do while, nós não vamos ter essa primeira verificação que é desnecessária, já
que a gente quer executar as nossas sentenças subordinadas de qualquer maneira. Ou seja, todo o nosso programa praticamente tá dentro do nosso comando e a gente sempre vai querer entrar aqui pelo menos uma vez. Em cenários como esse, a gente já tem um forte indicativo que é melhor a gente estar utilizando a estrutura do while. Vamos fazer a modificação aqui. Utilizando o comando do while diferente de -1. Lembrando que no comando do I a gente precisa utilizar o ponto e vírgula. Agora a gente não precisa mais atribuir valor à variável verifica, já que o usuário
vai estar inserindo o valor e esse valor vai ser armazenado na nossa variável verifica. Digite o número inteiro. Dois. Gostaria de digitar um novo número? Sim, digite o número inteiro. Sete é ímpar. Gostaria de encerrar o programa, digitar o novo número. Vou encerrar o programa. Funcionamento é o mesmo, mas nosso código agora ficou mais legível. A gente tem uma verificação a menos que a primeira verificação, né? A gente estava fazendo ela, sendo que a gente já sabia que a gente estaria executando o nosso programa. Então, nesse caso, é melhor fazer uso do comando do while.
Vou deixar salvo aqui. 12. Bom, pessoal, só tem um probleminha aí no nosso código, né? Vamos est executando ele. E observe que ele começa pedindo para que a gente digite o número inteiro. E normalmente não é isso. Quando a gente entra num programa, seja ele com interface gráfica ou no terminal, ele apresenta para nós um menu, ou seja, pergunta se a gente gostaria de estar realizando alguma tarefa ou não. Então, imagine que a gente agora vai tentar mudar a ordem do nosso programa, onde nós vamos perguntar primeiro se o nosso usuário gostaria de digitar um
novo número. Vamos escanear a sua opção e se ele disser que gostaria de encerrar o programa, nós vamos interromper o uso deste programa. Com o que a gente sabe até agora, caso o usuário digite encerrar o programa, nós ainda vamos pedir para que ele digite o número inteiro. Ele vai digitar o número inteiro, nós vamos mostrar se esse número é paro ou não e depois vamos encerrar o nosso programa. Por quê? Porque nós não temos até então nenhuma maneira de interromper a nossa estrutura iterativa no meio dela. Vou colocar aqui. Caso o usuário deseje interromper,
gostaríamos de não prosseguir com as sentenças subordinadas ao comando while, sentenças subordinadas se referindo a tudo que está entre as chaves. E neste caso aqui o comando do wi, só para garantir que vocês entenderam o que nós estamos fazendo aqui. O nosso comando do wi ele vai executar toda a nossa sentença subordinada. Depois ele vai fazer uma verificação. O valor da variável verifica quando o nosso usuário Inserir -1 vai ser igual a -1. Então nós vamos obter falso e, portanto, a gente vai sair do nosso programa. Lembra que a gente só retorna as nossas sentenças
subordinadas quando a gente obtém o valor verdadeiro. E o que nós vamos tentar resolver é aprender a interromper o nosso programa utilizando um mecanismo de controle. E o mecanismo de controle que nós vamos utilizar é o comando break. Pessoal, eu vou colocar aqui o comando break e a gente vai observar o que vai acontecer. Se eu executar o nosso programa, ele vai realizar essa impressão, vai aguardar a minha entrada para armazenar esse valor na variável verifica. E agora eu vou adicionar aqui -1. Nós vamos chegar na nossa linha 17 e o nosso programa vai ser
interrompido. Se eu executar novamente, mesmo que eu digite um, nós vamos chegar na linha 17 e o nosso programa vai ser interrompido. Este é um mecanismo de controle para que a gente interrompa as nossas estruturas de interação lógica. Porém, vocês concordam comigo que esse mecanismo ele não é muito útil se utilizado dessa forma, mas se a gente puder utilizar uma estrutura condicional, a gente pode fazer uma verificação. Se verifica for igual a -1, então nós vamos executar o comando break. Caso contrário, a gente não vai fazer nada, então a gente nem precisa estar utilizando o
elss. Vamos agora executar o nosso programa. E se eu digitar a opção um, como o valor da variável verifica não é equivalente a um, essa nossa condição vai ser falsa e a gente não vai executar a nossa sentença subordinada ao nosso if. Por esse motivo, a gente pode digitar um número inteiro, oito. O número oito é par. Fazemos a nossa verificação e o valor da variável verifica é um. Então essa condição vai ser verdadeira e a gente volta a executar todas as nossas sentenças. que estão dentro do nosso comando do while. Nós vamos novamente perguntar
se o usuário gostaria de inserir um novo número. Agora vamos digitar -1 e essa verificação vai ser verdadeira. Então, o comando break interromper em inglês, vai ser executado. Observe que o nosso programa é então interrompido. Bom, não sei vocês, mas eu tô achando que esse nosso código aqui tá um pouquinho confuso de ler. Essas nossas impressões no terminais não estão tão legais. Então, bora tentar refatorar isso daí. Primeira coisa que eu quero modificar nesse nosso programa vai ser bem simples. Vou criar um programa que verifica se o número é par ou ímpar quando ele for
positivo e que interrompe A execução se o número digitado for negativo. Para resolver isso, vamos modificar a nossa impressão. Digite um número inteiro positivo para ver se é par ou ímpar ou um número negativo para sair do programa. Não precisamos mais de duas variáveis porque agora nós temos a nossa estrutura condicional. Nós vamos verificar se a entrada é negativa. E se ela for negativa, nós vamos interromper o nosso programa. Agora vamos verificar se a entrada módulo de 2, ou seja, o resto da divisão por 2 for igual a zero. Nós vamos imprimir que o número
digitado é par. Caso contrário, nós vamos imprimir que o número é ímpar. E na nossa verificação, vamos dar uma olhada aí se a nossa entrada é positiva, porque se for positivo, nós queremos retornar à informação de se é para ou ímpar e continuar a nossa execução. Vamos executar esse nosso código. O número inteiro positivo para ver se é para o imple número negativo para sair do programa. Se eu digitar um número Negativo, nosso programa é interrompido pelo nosso mecanismo de controle break na linha 15. Iite o número inteiro positivo para ver se ele é par
ou ímpar. Vamos digitar aqui 1723. E a gente tem a impressão de que o nosso número é ímpar. Realizada a impressão de que o nosso número é ímpar, a gente faz novamente uma verificação. Mas pessoal, vocês concordam comigo que essa verificação ela é desnecessária? Porque caso a gente entre com o número negativo no nosso loop, nós já vamos interromper ele utilizando o nosso mecanismo de controle. Então a gente não precisa interromper ele no final. Dá uma olhada. Vou digitar o número par. ele vai continuar executando. Só que agora ele tá fazendo essa verificação aqui e
ele também tá fazendo essa verificação aqui. Então vocês concordam que eu poderia simplesmente colocar o valor verdadeiro dentro do meu isso não vai afetar a execução do meu programa? Prestem bastante atenção no que eu estou fazendo. Eu estou dizendo que a condição da minha estrutura de iteração, ela não é tão importante, não só não é importante como ela não é mais necessária, pois o meu código, a minha iteração, ela vai ser interrompida pelo meu mecanismo de controle break. Então eu não preciso mais que essa condição retorne falso para que então eu não execute as minhas
operações. Eu posso simplesmente interromper a nossa Estrutura de repetição utilizando o comando break. Esse é o poder da nossa estrutura de mecanismo de controle. E ela é fundamental. Fundamental. Por quê? porque ela vai explicar várias coisas para vocês. Inclusive, se nós já vamos interromper o nosso programa com o comando break, não importa se a gente vai utilizar o comando while ou o comando do while. Por esse motivo, nós poderíamos colocar aqui o comando while, que não precisa do ponto e vírgula, só para que a gente tenha que escrever menos. A gente quer sempre entrar na
nossa estrutura de repetição e nós vamos sair dela utilizando o nosso mecanismo de controle break. Percebam, em situações onde nós vamos estar utilizando esse mecanismo de controle, o nosso comando break, a condição da nossa estrutura de iteração não importa. Para aqueles que estão muito preocupados com o desempenho, eles podem alegar que caso você utilize o comando do W e na primeira execução o usuário digite, quer interromper o nosso programa, ou seja, ele dê como entrada um número negativo, nós teríamos uma vantagem de escolha em relação ao comando wi e ao comando do wi nesse caso
aqui, né? Observe, se eu utilizar o comando do A, eu vou entrar nessa sentença e aí eu já vou interromper aqui. Se eu utilizar o comando IO, eu vou ter que ver, fazer uma verificação e depois eu vou interromper o meu programa. Então, teria uma verificação a mais. Mas, pessoal, isso é tão irrelevante, tão irrelevante. É claro que existe ali uma operação a mais a ser realizada. Essa verificação, né? Verifica se esse valor que tá ali, ele realmente é verdadeiro, mas uma comparação a mais, somente uma, né, não vai fazer diferença. Portanto, quando vocês forem
utilizar o comando break, façam uso da estrutura lógica da qual vocês se sentirem mais confortáveis. Quando vocês não forem utilizar o comando break, aí sim vale a pena se perguntar se é melhor utilizar o do Wi ou comando while, porque aí você não vai ter que atribuir um valor à variável caso você opte pelo uso do comando do while. Outra coisa que eu quero que vocês percebam é que o nosso comando break ele só vai servir para interromper quatro comandos, sendo eles a estrutura condicional sutch. A gente já viu como é que o brake funciona
dentro da estrutura condicional sutch nossos três comandos de estruturas iterativas. Portanto, não se preocupe. Se você ver um if e o comando break, o comando break não faz nada na nossa estrutura condicional simples. O comando break, ele só interrompe estruturas iterativas, ou seja, loops, repetições. Vamos então salvar esse nosso arquivo. Arquivo número 13, mecanismo de controle break. E agora, pessoal, a gente vai ver mais um mecanismo de controle, que diferentemente do nosso break, ele não Vai sair do loop, mas ele vai [roncando] simplesmente pular todo esse código e ir pra nossa condição. Vou mostrar para
vocês que vocês vão entender melhor. Vou modificar aqui para que caso a pessoa digite menos1, a gente saia do nosso programa. Nós vamos executar, vamos receber um valor e se ele for igual a -1, a gente vai sair do nosso programa. Mas caso a entrada seja um valor negativo, nós vamos então utilizar o nosso mecanismo de controle contínuo. E o que vai acontecer é que nós não vamos estar realizando a impressão desses prints, nem mesmo essa verificação. Vamos dar uma olhada nesse funcionamento que vai ficar mais claro. Programa rodando. Digite o número inteiro positivo para
ver se é para o imp ou um ou um número negativo para sair do programa. Vou digitar aqui -9. Observem quando a gente encontra o comando contínuo, a gente pula pra nossa condição. Se eu digitar -1 agora, nós vamos para o nosso break. E o mesmo vai acontecer pro comando? Sim, pessoal, o mesmo vai acontecer pro comando i. Dá uma olhada. Se eu digitar -9, a gente vai encontrar o comando contínuo, nosso mecanismo de controle, e a gente vai voltar pra nossa condição. Aqui a gente tem aquele programa que a gente acabou de ver no
Visual Studio Code com essa explicação. Comando break, Ele força o encerramento imediato da nossa estrutura interativa. o comando contínuo, quando ele é alcançado, ele faz com que o programa salte imediatamente pra próxima iteração, no caso pro teste da condição do IO. Se esse teste for verdadeiro, a gente vai entrar novamente no nosso laço. Pessoal, essas estruturas são importantes e é importante que você entenda a relação que elas têm com os nossos loops, porque utilizando o comando contínuo, a gente poderia não fazer uso do comando break também. Dá uma olhada. Se o usuário digitar um valor
negativo, nós vamos entrar na nossa condicional. Nós vamos utilizar o nosso mecanismo de controle contínuo, que vai nos levar paraa nossa condição. E aqui, no caso, a gente tá deixando a condição como verdadeiro, mas se a gente colocar a nossa condição entrada ser menor do que zero para que a gente saia do nosso programa, nós vamos obter exatamente o comportamento anterior. Então, veja só, se eu inseri o valor negativo, a gente encerra o nosso programa. Se eu inserir um valor positivo, ele vai continuar em execução. Portanto, lembrem-se, até agora a gente já viu o uso
desses mecanismos de controle relacionado à estruturas de iteração. O brake interrompe o nosso loop e o comando contínuo faz com que a gente pule pra nossa condição. No caso, pode ser a condição do wi ou a condição do do while. Em relação aos usos, vai depender muito do programa que a gente tiver utilizando. Então vai ser a prática que vai trazer pra gente essa Noção de qual utilizar. Muitas das vezes a gente vai poder utilizar tanto o break quanto o contínue ou a condição dentro do comando wi ou a condição dentro do comando do wh.
E a gente vai ter várias possibilidades e como a diferença de desempenho vai ser ali muito pequena, uma comparação ou duas comparações a mais no máximo, nós vamos tratar essa diferença como irrelevante. Então não vai ter problema em qual estrutura de interação você escolher utilizar. Faça uso daquela que você se sentir mais familiarizado, mas entenda aí o funcionamento de todas elas para que você consiga ler códigos de outras pessoas também. Vou salvar esse nosso arquivo, mecanismo de controle continue. E vamos aprender agora a nossa última estrutura iterativa, o comando for, que nós vamos utilizar quando
a gente quiser executar um conjunto de instruções um determinado número de vezes. O for, pessoal, ele é o queridinho aí da maioria dos programadores e eu vou explicar o porqu disso acontecer para vocês. A nossa sintaxe, ela é muito parecida com o comando io. A gente vai ter o nosso comando, a gente vai ter entre parênteses algumas coisas a mais no for e entre chaves a nossa sentença subordinada. Só que além da condição, nós teremos um contador e um salto. Eu vou explicar para vocês em detalhe o que que é isso. E tanto o contador
quanto o salto, geralmente eles aparecem mesmo quando a gente utiliza o comando wi ou o comando do while. Por isso, boa parte dos programadores acaba optando por utilizar o comando for por considerar ele um pouco mais organizado. Mas vamos Dar uma olhada no porquê. O contador, que é a primeira coisa que aparece entre parênteses, ele é a variável ali que vai controlar a quantidades de interações. Normalmente é só uma variável que a gente vai ter e é a variável que nós vamos utilizar na nossa condição. A condição é a expressão que vai determinar a interrupção
das nossas interações. A gente já vinha utilizando nos comandos anteriores. E o salto é uma expressão que vai determinar o quanto a variável do contador vai aumentar, né, ou vai se modificar. Isso vai ficar mais claro escrevendo o código. Então vamos tentar fazer um programa que imprime de 1 até 10. Nós vamos ter o for. Entre parênteses nós vamos ter o nosso contador que é uma variável. Então vou colocar aqui a nossa variável int i = 0. Aqui eu já estou criando a minha variável e declarando ela. Poderia também, se eu quisesse, criar a minha
variável aqui fora e atribuir um valor para ela aqui dentro ou já atribuir um valor para ela aqui fora e só colocar essa variável aqui dentro. E daqui a pouquinho a gente vai ver aí se existe alguma diferença em fazer isso ou não. Por enquanto vamos deixar dessa maneira. Estamos criando a variável I, inicializando ela com zero. Vamos colocar a nossa condição enquanto I for menor do que 10. condição que a gente já vinha utilizando aí nos nossos outros operadores de iteração. E o nosso salto. O nosso salto vai ser o nosso operador aí de
incremento. Se eu quisesse, eu poderia também colocar aqui igual a i + 1, não teria problema, mas nós vamos utilizar o nosso operador de incremento. Aqui nós já estamos obtendo um erro, porque observem bem, a separação do conteúdo entre parênteses, ela é feita pelo ponto e vírgula. E vamos então realizar uma impressão. Feito isso, vamos executar o nosso programa. Observe que esse mesmo código poderia ser escrito utilizando o comando vou deixar aqui comentado e vou fazer utilizando o comando eu criaria a variável int i = a 0. Eu iria ter como condição do meu loop
que fosse menor do que 10. eu executaria a impressão e então faria o incremento. Portanto, pessoal, a gente pode fazer uso do comando while, mas se a gente já vai criar essa nossa variável, já vamos ter uma condição, já vamos realizar o nosso incremento, nós podemos também fazer uso do comando for. Vamos deixar comentado o nosso código com IO. Eu vou falar um pouquinho com vocês sobre a ordem de execução. Quando nós utilizamos o operador de pós incremento no salto do nosso for, o que vai acontecer? Nós vamos realizar este incremento sempre ao final das
nossas sentenças. Então, o comportamento vai ser esse que nós estamos vendo dentro do Ion. Caso eu utilize o operador de pré-incremento, nós também vamos executar o nosso incremento ao final da nossa sentença. Observem, mesmo fazendo essa modificação, o valor zero ele é impresso. Então, fazendo o uso do for, a Gente fica limitado a realizar o nosso incremento sempre ao final das sentenças. Uma outra coisa, quando nós declararmos a nossa variável fora do comando for, ela vai continuar existindo mesmo após a gente sair desse nosso comando. Então aqui eu estou reservando o espaço na memória, salvando
uma variável, chamando essa variável de i. Dentro do nosso for, eu estou atribuindo o valor igual a zero. E eu poderia também já estar fazendo essa atribuição aqui fora e não colocar nenhuma informação. Se eu não colocar nenhuma informação no contador, eu ainda preciso fazer uso do ponto e vírgula, porque o nosso compilador vai entender que a expressão que está entre o ponto e vírgula é a nossa condição. Então vamos deixar dessa maneira e vou realizar a impressão após sair do nosso for. O valor de fora do for é porcentagem de pessoal, caso eu declare
a minha variável dentro do for, ela não vai existir quando a gente sair do nosso loop. Se a gente tentar compilar esse código, inclusive até mesmo antes de compilar, a nossa extensão já tá mostrando que tem um erro aqui. Eu estou tentando usar uma variável que não existe, pois as variáveis declaradas no contador do for, elas só vão existir dentro do comando for. E para ser mais correto, qualquer variável que for declarada dentro de uma estrutura de iteração, ela só vai existir dentro daquela estrutura. Ah, mas por que que você só deixou para explicar isso
quando você nos apresentou o comando for? Vou Mostrar para vocês que mesmo utilizando o comando wi, se eu declarar a variável i dentro do comando wi, ela não vai existir fora dele. Mas a gente não se deparou com esse problema porque não fazia sentido que a gente estivesse declarando dentro dessa nossa estrutura de repetição. Pensa comigo, se nós vamos estar entrando dentro do nosso e executando as nossas sentenças várias vezes, a gente iria cada execução está sobreescrevendo a nossa variável I e atribuindo o valor a ela novamente. Então, geralmente quando a gente vai utilizar o
comando nós deixamos a nossa variável fora dele, principalmente em casos como esse, já que a nossa condição depende dessa variável que ainda não existe. E mesmo que a gente estivesse utilizando o comando do wi as variáveis que a gente declara dentro da nossa estrutura de iteração não podem ser usadas para checar a nossa condição. Então vou deixar comentado aqui algumas coisas sobre esses três elementos que a gente vai encontrar dentro do comando for. O contador, ele é a variável que controla a quantidade de interações. Pode ser indicada mais de uma variável. a gente poderia declarar
também a variável J = a 8. Até por isso o compilador usa essa separação com ponto e vírgula. Não é à toa, tá pessoal? Quase sempre a variável vai ser do tipo original, intenum. E o que eu acabei de explicar para vocês, declarar uma variável dentro de uma estrutura, seja como contador ou nas sentenças, faz com que essa variável só exista durante a execução dessa estrutura. Isso sobre o contador, sobre a nossa condição, em geral, é aplicada sobre a variável do contador. Operações se encerram quando a condição for falsa, zero. E se nenhuma condição for
definida, a estrutura funciona como um laço infinito, sendo necessário medidas de controle. dentro das sentenças subordinadas. Vamos dar uma olhada nisso que eu falei para vocês. Se eu não colocar nenhuma condição, nós ainda vamos conseguir fazer uso do comando for. Só que aí nós vamos ter que fazer uso de alguma medida de controle. Vou verificar se o i vai ser igual a 10. E caso ele seja, eu vou utilizar o comando break para sair dessa nossa estrutura. Tá dando um erro por causa do nosso print f. A gente tá tentando imprimir a variável i e
ela só vai existir dentro do nosso for. Mas observe que a execução aconteceu e quando o i foi igual a 10, a gente interrompeu o nosso programa. Posso vir aqui e fazer alteração para 100. Já em relação ao nosso incremento, pessoal, como a gente tá falando aí de mecanismos de controle, eu vou mostrar para vocês quando é realizado, porque se a gente tiver o, você concorda comigo? Se eu botar aqui Um contêiner, a gente vai chegar nesse ponto, a gente vai pular pra nossa verificação e a gente nunca vai realizar esse incremento. Então a gente
vai ficar sempre pulando para cá. Já no comando fora isso não acontece. Por quê? Porque o nosso incremento, a gente tá utilizando ele como um parâmetro desse comando. Então, toda vez que a gente utilizar o comando contínui, a gente vai sempre realizar um incremento. Pensa que a gente voltaria pra nossa condição, mas ao lado da nossa condição a gente também vai realizar um incremento. Então, seriam esses dois elementos aqui. Então, vamos trocar uma ideia aí sobre esse nosso salto. Determina quanto o valor da variável vai aumentar. A gente não precisa ir sempre aumentando de um
em um, tá pessoal? Eu posso, por exemplo, vir aqui e aumentar aí o nosso valor de dois em dois. Observe, 0, 4, Se não for definido, também precisaremos de uma medida de controle, porque basicamente a nossa condição nunca vai ser atendida. Se houver mais de uma variável contadora, então pode haver mais de uma expressão de salto. Por exemplo, vou iniciar aqui a variável I e a variável J, ambas começando com zero. Vou fazer com que o salto da variável I seja de um em um e vou fazer com que o salto da variável J seja
de cinco em cinco. Vamos imprimir o valor de I. Vamos imprimir o valor de J e vamos executar esse nosso código que a gente não precisa utilizar a palavra int novamente, né? Então, deu esse errinho aí do compilador e pronto. Observem que o nosso valor ele tá aumentando de um em um até chegar no valor 100. E o nosso valor J tá aumentando de cinco em 5. Então a gente chega até 500. Vou remover isso aqui para ficar um pouquinho mais claro pra gente e vou colocar a nossa condição dentro do for. Em relação a
esse comentário aqui, é claro que eu poderia ter utilizado barra asterisco, mas eu optei por não fazer isso só por uma questão de hábito, tá? É porque na linguagem C++ não tem o barra asterisco, mas tem o barra barra. Última coisita que eu falei que eu ia mostrar para vocês, acabei já explicando, mas é sempre bom reforçar que no for o salto ele é executado após o contêiner. Então, se a gente encontrar ali o comando contêiner, a gente vai executar o nosso salto, o nosso incremento já no while, se a gente tiver o nosso incremento
depois do nosso comando contínuo, caso a gente encontre ele, a gente não vai estar executando. Nesse for, nós estamos criando a variável e atribuindo o valor zero. Nós vamos executar este nosso loop enquanto o valor de for menor do que 10 e o nosso incremento vai ser de um. Nós temos uma condicional que vai verificar se o nosso número é ímpar. Observe o símbolo diferente. Então, caso o resultado da divisão por dois tenha algum resto, isso só vai acontecer para números ímpares. A gente vai utilizar o comando contínuo. E se a gente vai utilizar o
comando contínuo, a gente vai realizar o nosso incremento, mas nós não vamos realizar a nossa impressão que tá ali embaixo. Então, esse for, ele só vai imprimir números pares. Quando ele encontrar o número ímpar, o mecanismo de controle contínuo vai ser ativado, ele Vai realizar o incremento, então verificar a condição ali em cima. No código abaixo, a gente vai ter um comportamento um pouquinho diferente. Por quê? Nós estamos declarando a variável I, assim como nós fizemos no for, olhando aqui embaixo pro, tá, pessoal? Nós temos uma condição e menor do que 10, assim como no
nosso for, essa condição vai ser verdadeira. Então nós vamos executar essas sentenças subordinadas. Esse if não vai ser verdadeiro, tá? Então a gente vai pular ele, já que o resto da divisão de 0 por 2 é 0 e 0 é igual a 0. Então nós vamos obter falso a partir dessa expressão. Portanto, nós não vamos executar o comando contínuo. Então a gente vai realizar a impressão do valor zero na nossa tela. Vamos fazer um incremento. O zero vai virar um e aí vai começar o problema. Quando o zero virar um, pessoal, a nossa condição I
menor do que 10 ainda vai ser atendida, porque 1 é menor do que 10. Mas agora a gente vai est entrando nesse if. E se a gente entrar nesse if, porque o resto da divisão de 1 por 2 é 1 e um é diferente de zero, então nós vamos obter verdadeiro, nós vamos executar o comando contínue. E o comando contínu nos manda pra nossa condição i menor do que 10. E já que a gente não alterou o valor de i, um vai continuar sendo menor do que 10. A gente vai continuar entrando aqui novamente. A
gente vai continuar executando o comando contínuo que vai nos mandar pra condição e nós vamos cair num loop infinito. Então fica esse alerta aí. Tomem bastante cuidado quando você tiver fazendo uso do comando contínuo e o seu incremento estiver vindo depois desse comando ao fazer uso do i tem essa vantagem, pois o salto sempre vai ser executado. Daí você não precisa se preocupar com a posição do Seu comando de controle. Vamos salvar esse nosso código e estamos finalizando o nosso estudo das estruturas iterativas. E agora a gente já tem total condição de estar realizando a
nossa prática, resolvendo a nossa lista de exercícios sobre estruturas iterativas. Bom, pessoal, então a gente vai começar agora os nossos estudos sobre a reis e strings. E a primeira coisa que eu gostaria antes de explicar o que são a reis e strings é a motivação por trás da gente estar estudando agora esse conteúdo. Então vamos dar uma olhada nesse nosso programa que calcula a média de três números. Nós vamos criar três variáveis. Nós vamos pedir para que o usuário digite três números. Então nós vamos receber a entrada desses três números. Dei a dica para vocês
de colocar o espaço antes do scan f. Em breve a gente vai entender exatamente o porquê disso. Aqui nós somamos o valor desses números e então dividimos por três e depois imprimimos a média. Agora imagine que você fosse responsável por calcular a média de todos os alunos de uma sala de aula, tendo em vista que salas de aulas costumam ter de 25 a 30 alunos. Como você poderia fazer isso? Você teria que declarar várias variáveis. que eu fiz até a 11 e iria recebendo esses valores. Percebam como isso fica inviável de ser feito. E caso
você, por exemplo, esqueça de colocar o nosso símbolo de endereçamento, a gente precisaria estar corrigindo isso em aproximadamente 30 variáveis. E é por Esse motivo que existem os arreis para que a gente possa desenvolver soluções mais eficientes, com maior legibilidade, cuja manutenção vai ser mais fácil. Vamos dar uma olhada no funcionamento de uma variável. Bom, aqui nós temos uma variável do tipo int. O tipo int ele possui 4 bytes. Então na nossa memória, quando nós criamos uma variável do tipo inteiro, nós reservamos quatro bytes. Com o conteúdo que nós vamos aprender, a gente vai ter
a capacidade de armazenar uma sequência de inteiros. Imagine que eu esteja criando quatro variáveis do tipo inteiro. Quando a gente faz isso, cada variável dessa vai reservar 4 bytes da nossa memória RAM. Mas nada nos garante que esses bytes vão ser reservados lado a lado. Pode ser que um esteja no começo, um esteja no meio, outro esteja no final, ou pode até acontecer deles estarem lado a lado. Agora nós vamos ter um método para reservar um número maior de variáveis do mesmo tipo lado a lado na nossa memória. E esse espaço reservado sequencialmente é o
que nós vamos chamar de arrei. Vamos dar uma olhada no código. Nós agora poderíamos fazer n colchetes 3. Nós estaríamos reservando três variáveis do tipo float de maneira sequencial na nossa memória. O tamanho desse nosso espaço reservado na nossa memória vai ser o seguinte: tamanho do arrei, que nesse caso é 3, vezes o tamanho do tipo de dado, nesse caso é o Float. Mas como a gente pode fazer uso desses espaços? Será que é da mesma forma que nós fazemos uso de variáveis? E é bem parecido, tá pessoal? Só que agora nós vamos precisar utilizar
os colchetes para informar em qual posição do nosso arrei essa variável se encontra. Isso para armazenar e também para quando nós formos utilizar o conteúdo dessa variável. N colchetes 0, N colchetes 1, N colchetes 2. Vamos executar este nosso programa. Digite três números. 8 7 5. A média é 6.67. Detalhe, quando reservamos três espaços, o primeiro é acessado pelo índice zero, o segundo pelo índice um e o terceiro pelo índice dois. Observe que o índice três não existe, o índice do tamanho do nosso arrei, porque nós não começamos a contar a partir do valor um.
Então vamos dar uma olhada nessa questão de índice através de uma imagem. Observe que à esquerda nós estamos criando um arreio do tipo inteiro com dois elementos. O primeiro elemento ele é acessado com o índice zero. O segundo elemento ele é acessado com o índice um. e índice é o que nós vamos colocar dentro dos colchetes para acessar o conteúdo do nosso arrei. Nesse caso da declaração, o índice vai indicar quantos espaços de memória daquele tipo nós vamos ter que armazenar, se são dois Espaços, três ou mais. E na hora de realizar o acesso, o
índice vai indicar qual posição do nosso arrei está essa nossa variável. Pessoal, é de extrema importância que você preste bastante atenção nisso, porque na linguagem C pode ser que você não obtenha nenhum tipo de erro ou aviso, caso você tente acessar um espaço de memória que você não reservou. Então, imagine que você tenha se esquecido que o primeiro elemento é o elemento zero, o segundo elemento é o elemento um e você tenta acessar o elemento dois por ter pensado que o primeiro elemento era o elemento um e o segundo elemento era o elemento de índice
dois. Se você fizer isso, na prática, você vai estar acessando este espaço aqui que não faz parte desse arrei. Depois a gente vai entender o porquê disso. E quando você não obtém um erro, aí fica muito difícil de perceber aonde está o problema no seu programa. Por quê? Porque você vai realizar a soma, você vai acessar esse valor que você não faz ideia do que está armazenado ali. Vai ser um número aleatório, tá? Igual eu expliquei para vocês, toda vez que a gente reserva uma variável, a gente não escreve zero naquele espaço de memória. Então,
a gente vai ter ali um lixo na memória. Então, é extremamente importante que a gente preste atenção nos índices dos nossos arreis. Por trás dos panos, por que que o arrei ele é sempre utilizado, tá sempre presente em várias linguagens? Porque a gente só precisa salvar o endereço do primeiro elemento. A gente verifica que o tipo é inteiro e vai caminhando de quatro em quatro. É isso que tá acontecendo ali por baixo dos panos. Então, caso você esteja acessando, por exemplo, o segundo elemento, basta pegar esse endereço Inicial mais o índice, que é um, vezes
o tamanho do tipo, que é 4 bytes. E aí a gente vai somado a esse endereço chegar neste endereço aqui. Isso aí é só para você entender como que as coisas estão acontecendo por baixo dos panos. Então, vamos escrever alguns códigos utilizando a reis. Eu quero mostrar para vocês como é que as nossas estruturas iterativas vão nos ajudar a trabalhar com esses arreis. Então, vou reescrever esse nosso programa, onde nós vamos calcular a média de três números, mas agora utilizando uma estrutura interativa. Nós vamos criar o nosso arrei com três números. Vamos criar a nossa
variável para calcular a média e vamos então utilizar um for que vai começar com índice i igual a 0. ponto e vírgula, nós vamos iterar enquanto o I for menor do que 3, nós vamos fazer o incremento de um em um. Aqui dentro do nosso for, nós vamos pedir para que o usuário digite um número. Vamos salvar esse número dando esse espaço aqui antes do scan f no nosso arrei de índice I. Percebam, o índice I ele vai começar sendo zero. Então, a gente vai armazenar primeiro na posição zero, depois ele vai ser um, a
gente vai armazenar na posição um, depois ele vai ser dois e a gente vai armazenar na posição dois, percorrendo as três variáveis que fazem parte do nosso arreio. Feito isso, a gente pode estar Calculando a média, acessando o valor de cada um desses elementos do nosso arreio e somando eles. Vou colocar os parênteses, já que a precedência da divisão é maior do que a da adição. a gente realizar essa divisão primeiro e só depois as operações de edição que nós vamos dividir por três. Depois vamos imprimir a média. Vamos executar esse nosso código. Digite o
número. Agora nós vamos inserir um número de cada vez. Oito. Digite o número 7. Digite o número seis. E nós vamos obter a nossa média. Ficou diferente porque antes eu tinha digitado 8 7 5, tá? Vou inserir aqui os mesmos valores. 8 7 5. Pois bem, tem um jeito da gente ainda melhorar a escrita desse nosso código, que é calculando a média dentro do nosso for. E como é que a gente vai fazer isso? Nós vamos adicionar a variável média, o conteúdo do arrei, o conteúdo da variável que está dentro do nosso arrei. Primeiro nós
editamos o valor oito. Então a gente vai armazenar o valor oito dentro da variável média somado do conteúdo que já estava lá na nossa variável média. Então nós vamos começar 0 + 8 e vamos armazenar oito na variável média. Na segunda interação, o usuário vai digitar mais um valor. No caso, nós vamos entrar com valor sete. A gente vai fazer oito, que é o valor da variável média, mais 7, nós vamos então ter 15 na nossa média. Depois, na próxima interação, nós vamos ter 15 + 5, nós vamos obter o valor 20 na nossa média.
E aqui nós vamos dividir o valor da nossa variável média por 3 e depois Realizar a impressão. Vamos ver. 8 7 5 O nosso programa tá funcionando corretamente. A gente pode pegar essa sentença abaixo e escrever ela da seguinte maneira, utilizando esse nosso operador, que é basicamente uma abreviação, mas eu disse para vocês que o arrei iria permitir com que a gente facilmente modificasse o nosso programa de cálculo de média para um número maior ou menor de alunos, certo? pra gente poder calcular a média aí de uma turma, por exemplo. E a maneira com a
qual a gente vai fazer isso é fazer no uso de uma constante nomeada. Vou chamar ela de qt de quantidade e vou armazenar aqui o valor 3. Agora, na hora de criar o nosso arrei, eu vou usar o conteúdo da nossa constante. Na hora de percorrer o nosso for, para determinar a nossa condição de quantas vezes a gente vai repercorrer, eu vou utilizar a nossa variável QT, quantidade. Na hora de atribuir um valor a nosso arrei, eu também vou utilizar a nossa variável QT. Na hora de somar a nossa média, eu também vou utilizar a
nossa variável QT. E na hora de dividir por um valor, eu também vou utilizar a nossa variável QT. Observem que eu simplesmente troquei a constante literal 3 por essa constante nomeada, onde o valor 3 aparecia, eu substituí pelo conteúdo dessa minha variável. Tá dando um errinho, né? Porque eu não coloquei o tipo. Essa nossa variável vai ser do tipo inteiro. Vamos executar esse nosso programa. 8 7 5 e beleza, funcionou corretamente esse erro que a nossa extensão está Indicando, pessoal, ignorem, tá? Não tem nenhum problema nesse código. E agora, se eu quiser aumentar a quantidade
para 10 alunos, basta eu vir aqui e alterar o valor da minha constante nomeada. Então, vamos lá. 8 7 6. Teve um outro aluno que teve média cinco, um outro teve média quatro, um teve média dois, outro teve média nove. O outro teve média 10, o outro teve média 8, o outro teve média 4. E calculamos a média da turma 6.3. Pra gente saber a quantidade que a gente tá digitando, eu posso melhorar essa nossa impressão. Digite o número que vai ser armazenado na posição porcentagem d e vou fazer uso da nossa variável I, que
só existe dentro da nossa estrutura interativa forte. Então vamos lá. Só alterei a nossa impressão. 8 2 4 6 5 9 7 8 10. Média 6.2. Vamos salvar esse nosso arquivo. A aplicação de um arrei. Bom, pessoal, caiu do futuro aqui. Eu quero falar para vocês que o mais correto é que a gente esteja fazendo uso do define qt e o seu valor e não da nossa constante int. Vocês vão reparar que eu não tive nenhum problema de compilação por causa da versão do meu compilador, mas caso você encontre algum problema, basta que você faça
uso da diretiva define, que vai simplesmente substituir todas as ocorrências de QT por 10 antes de realizar a nossa compilação. Bom, agora eu gostaria de falar com você sobre as três maneiras que a gente tem de inicializar um arrei. Lembrando que inicializar é atribuir o valor para um espaço que a gente já tem armazenado na memória. Primeira maneira, vamos criar esse nosso arreio e agora inicializar Ele acessando índice por índice, assim como a gente fez dentro do nosso for, tá pessoal? E atribuindo valores. Essa é a primeira maneira. Para imprimir o conteúdo desse nosso arrei.
Eu vou fazer uso do comando for. Vamos executar esse nosso programa. Vamos pôr nossa quebra de linha para melhorar a nossa visualização. E tá aqui uma outra maneira que a gente tem de atribuir valores a um arrei ao criar esse arrei a gente já inserir o conteúdo nele. 1 4 9 e 16. Fazendo uso dos colchetes com conteúdo separado por vírgula. Qual que é a vantagem da gente estar declarando e inicializando na mesma linha? Ou seja, na mesma sentença? Nós não precisamos colocar o tamanho do arrei porque o nosso compilador vai verificar quantos elementos estão
entre os colchetes e aí ele automaticamente vai vir aqui, ó, e vai colocar esse valor quatro. E outra maneira, pessoal, é se está fazendo uso do for. Essa maneira vai requerer de você um pouco de pensamento lógico, matemático, mas dá para ser feito. Observe aqui. Eu vou criar a variável inti começando no valor um. E eu vou querer que o nosso for execute de um até quatro. Poderia aqui deixar menor que cinco, né? Ele iria de 1 até quatro, mas vou deixar menor ou igual a 4. Vou fazer um incremento unitário. E agora, como a
Nossa primeira posição é zero, eu vou ter que fazer i - 1. Nós vamos inicializar o nosso for com o valor 1. I - 1 vai ser o valor 0. Sempre que você for trabalhar com a raiz, analise principalmente o primeiro elemento e o último elemento, tá bom, pessoal? Então, o primeiro elemento aqui vai ser o a0. E o valor que eu quero inserir aqui é o valor 1. Poderia ser somente i, mas eu vou colocar i x i porque 1 x 1 é 1. O segundo valor, na nossa segunda interação, o i vai ter
passado a valer 2. A gente vai est acessando a segunda posição do nosso arrei que possui índice 1. A gente vai chegar no valor 4. A terceira posição, o nosso i vai valer 3. A gente vai chegar no valor 9. E na quarta posição, o nosso i vai valer 4. A gente vai chegar no valor 16. Por que que eu disse que fazendo uso do for tem que escrever menos, mas precisa fazer uso da lógica matemática? O que me permitiu escrever o for dessa maneira é perceber que aqui nós temos os números sendo multiplicados por
eles mesmos. Então temos 1 x 1 dá 1, 2 x 2 dá 4, 3 x 3 dá 9 e 4 x 4 dá 16. Mas essas são somente maneiras de atribuir valor a essas nossas variáveis. Lembrando que nessa maneira três, ela bem parecida aí com a maneira um. A principal diferença é que nós não estamos fazendo isso à mão, estamos fazendo dentro de uma estrutura de interação. Vou deixar comentado que nós precisamos ainda declarar esse nosso arrei e tá aí arre na posição zero, posição 1, posição dois e posição três. Uma outra vantagem do Uso
dessa maneira três é que eu poderia também colocar a nossa impressão aqui dentro. E eu recomendo que você pause o vídeo agora e tente fazer isso. Vou salvar esse nosso arquivo como inicializar ponto C. Nesse caso, bastaria que a gente colocasse essa linha aqui dentro e percebesse que a nossa estrutura de iteração vai iniciar com i valendo 1. Se a gente tentar executar isso daqui, as nossas impressões vão ficar erradas, porque a gente tá atribuindo o valor na posição zero e tá imprimindo a posição um. A gente tá atribuindo o valor na posição um e
tá imprimindo a posição dois. A gente tá atribuindo, inicializando a posição dois e tá imprimindo a posição três. A gente tá inicializando a posição três, que é a quarta posição e última, e a gente tá imprimindo aqui a posição 4. Então, a gente precisaria vir aqui colocar o i - 1, porque nós estamos começando a nossa estrutura de iteração com o valor 1. Agora, pessoal, vou escrever um código aqui de um erro que é extremamente comum. É uma coisa que a gente já viu, mas que geralmente acontece. Então me acompanha aqui e vê se você
vai identificar qual que é o problema. Eu vou criar dois arreis tamanho quatro. Vou atribuir os valores 1, 2, 3 e 4. Lembrando que eu poderia não especificar o tamanho, já que no mesmo momento que eu estou instanciando, eu estou inicializando. Em outras palavras, na mesma sentença onde eu estou criando o meu arrei, eu estou atribuindo valor a Ele. 5 6 7 8. Agora me respondam, existe algum problema com esta operação aqui? Vamos tentar executar esse código. Opa. Opa, opa. A gente tem um problemão. Futuramente a gente vai entender o que que o nosso compilador
quer dizer com esse erro quando a gente vê o conteúdo de ponteiros. Mas o que eu quero que você entenda é que eu não posso somar um arrei a outro arrei. Vamos dar uma olhada. Lembrem-se que um arrei um espaço na memória que contém várias variáveis, ou melhor, que contém uma sequência de variáveis. Quando eu tento realizar a operação A + B, eu estou tentando somar um arrei, ou seja, eu estou tentando somar dois espaços da minha memória RAM, sendo que em cada um desses espaços nós temos várias variáveis. Isso não vai dar certo. O
correto, então, caso a gente quisesse somar cada um desses elementos, inclusive salvando o resultado em um novo arrei, seria que a gente utilizasse o comando de interação for. A nossa condição é o tamanho dos nossos arreis. Nós vamos fazer o incremento um por um. Não vou fazer uso das chaves porque vai ter só uma sentença dentro do meu for, onde C na posição I vai ser igual a A na posição I + B na posição I. Portanto, lembre-se, toda vez que a gente for realizar uma operação, nós vamos estar realizando essa operação com um elemento
de um arrei. E nós sabemos que nós estamos trabalhando com um elemento de um arrei pelo uso das chaves e do índice. Vou deixar aqui comentado. Atenção, não é possível tomar dois arreis. Agora nós vamos aprender sobre um tipo especial de arrei. Já vou mostrar ele aqui para vocês. Vou criar um arrei do tipo char. Vou atribuir aqui o meu nome. Agora, como é do tipo chá, a gente precisa estar utilizando essas aspas simples. Não é obrigatório que eu coloque a quantidade, mas se eu quiser, eu posso estar colocando. E observem que o que nós
temos aqui é um arreio do tipo chá. Mas esses arreis eles vão ganhar um nome especial. A partir de agora, nós vamos nos referir à reis do tipo char com a palavra string. Então, toda vez que você me ouvir mencionando a palavra string, lembre-se que é simplesmente um arrei, uma sequência de elementos do tipo char. E uma outra palavra que a gente pode estar usando, que eu quero que vocês entendam o significado, é a palavra vetor. Então, nós temos aqui arrei, sequência de variáveis, nós temos string, sequência de variáveis do tipo char, e nós temos
também um vetor. E o vetor é uma sequência de variáveis sem tamanho definido. Então vamos colocar aqui entre aspas que o que a gente já viu tem tamanho definido, tamanho definido, tamanho definido. E não confundam, tá? Você pode falar assim: "Ah, mas às vezes a gente tá criando sem colocar um valor no índice, né?" Não, não, não é porque a gente não colocou um valor que o tamanho não está Definido, igual eu informei para vocês, a gente não está colocando esse valor porque nós estamos criando o nosso arrei e atribuindo o conteúdo a ele na
mesma sentença. Então, o nosso compilador já sabe qual que é o tamanho e se nós temos um tamanho definido, nós temos um arrei. O termo vetor a gente vai utilizar quando ele estiver se referindo a um tamanho indefinido. Então vamos aprender um pouco mais sobre as nossas strings, que são uma sequência de caracteres adjacentes na memória do computador. Tudo que a gente utiliza, entre aspas duplas, são strings. E na linguagem C, para armazenar uma string em uma variável, é necessário criar um vetor do tipo char. Pensem que esse termo está aqui, porque a gente não
tá especificando aí, né? A gente pode armazenar em uma string de tamanho 10 ou uma string de tamanho 50 ou uma string de tamanho 100. Então, por esse motivo, a gente tá utilizando o termo vetor aqui. Vamos então começar analisando aí as possíveis maneiras de inicializar uma string, ou seja, de colocar valor dentro de uma string. A nossa primeira maneira vai ser a seguinte. Após a gente criar a nossa string, a gente acessa a posição e nessa posição a gente coloca a letra que a gente gostaria de armazenar. Todas as vezes que a gente tiver
trabalhando com strings, a gente vai ter um caracter especial, que ele serve para indicar que nós chegamos ao final da nossa palavra. Aqui nós reservamos 10 espaços, nós colocamos o valor oi e o ponto de exclamação em três desses espaços. E no quarto espaço, o de índice número três, nós colocamos esse caracter que serve para indicar o final da nossa string. E O que nós temos no nosso quinto espaço, o de índice número quatro. Não importa, a gente tem algum lixo, alguma informação que estava lá na memória RAM que não é relevante pra gente. Na
hora da gente realizar a impressão, vocês vão entender porque que esse caracter que marca o fim da nossa string é importante. Maneira número dois, a gente já tinha visto que é criando a variável e já atribuindo o valor. E nós temos também, pessoal, a maneira número três. Essa é muito parecida com a maneira número dois e nós só podemos utilizar ela para strings. A parte de criar essa variável é a mesma, mas como a gente já tinha visto, tudo que a gente coloca entre aspas duplas em Cunciona como uma string. E se tá em aspas
duplas, a gente não precisa colocar o nosso caracter especial, pois automaticamente ele já vai ser adicionado. Vamos salvar esse nosso arquivo. Vamos aprender agora sobre a entrada de dados em strings, ou seja, como armazenar algo que foi digitado no nosso terminal dentro da nossa string. E para isso nós vamos utilizar o símbolo porcentagem S. Não vamos esquecer de criar nosso string. E como eu disse para vocês, strings são a rais do tipo char e a rais eles têm um tamanho definido. A gente precisa definir qual vai ser o tamanho dessa nossa string. Eu vou colocar
aqui 50. Vamos então executar esse nosso programa. Digite uma string. A gente pode estar digitando aí uma string de tamanho até 50. Meu nome não é Johnny. E vamos colocar aqui a impressão dessa string digitada. Foi utilizando porcentagem S. Vamos utilizar aqui também o nosso operador de endereçamento, né? Futuramente a gente vai ver que quando a gente tá trabalhando com strings ele não é obrigatório, mas por enquanto vamos continuar fazendo uso. Meu nome não é Johnny, é a string é impressa. Pode estar se perguntando por que que você escreveu tudo junto? Vamos tentar escrever separado.
Meu nome não é Johnny e nós vamos salvar somente o meu. Eu vou explicar para vocês porque que isso acontece. Todas as vezes que a gente tem utilizado o scan f, nós colocamos porcentagem d, por exemplo, porcentagem d, a gente separa essas entradas por espaço. Então, quando a gente faz uso do scan f para uma string, ele também separa a nossa string pelo espaço. Em outras palavras, pessoal, eu poderia estar utilizando o porcentagem S para salvar duas strings separadas pelo espaço, mesmo que eu não coloque esse espaço aqui. Bom, como eu tô esquecendo de fazer
o do nosso operador para acessar o endereço, vou deixar comentado aqui opcional quando estivermos utilizando strings. Mas se você usar, não tem nenhum problema, tá? Quando a gente vê ponteiro, nós vamos entender porque que ele é opcional e porque que isso faz todo sentido. Vamos modificar a nossa impressão. As strings digitadas foram Vamos executar o nosso programa. Meu nome não é Johnny. E agora eu estou colocando muito a mais do que o meu Scan F vai receber, né? Mas a nossa impressão aconteceu corretamente. Eu só limpei o nosso terminal aqui, pessoal. Mas a gente pode
sim não estar separando, tá? Vou mostrar para vocês. Meu nome não é JN. As stringsadas foram meu e nome. Vamos deixar comentado que o Scan F não consegue armazenar espaço, pois separa as entradas por espaço. Nós vamos ver como a gente consegue receber entradas do terminal de strings com espaços. Mas antes disso, eu quero explicar para vocês porque que até então a gente tem colocado esse espaço no nosso Scan F. Vamos dar uma olhada nesse nosso programa que a gente acabou de fazer, onde eu estou digitando meu nome não é Johnny e somente o conteúdo
meu e o conteúdo nome estão sendo armazenados. Para explicar isso para vocês, vou fazer um pequeno desenhinho onde vou criar aqui um espaço que a gente vai chamar de buffer, uma seta que vai ser o nosso scan. F porcentagem espaço, porcentagem espaço. E aqui nós vamos ter nossas duas strings que no nosso programa possuem espaço para 50 caracteres, mas vamos fingir que aqui ela só tem espaço para 10. 1 2 3 4 5 6 7 8 9. Falta um 10. Essa string tem o nome de STR e essa tem o nome de STR2, assim como
o nosso programa. Todo conteúdo que chega do nosso terminal, ele passa por um buffer. Então, imagina que nós estamos digitando ali a partir do nosso terminal o seguinte conteúdo. Meu nome não é Johnny. O nosso scan f, ele vai receber só esses dois primeiros que estão sendo separados por espaço. O scan f lá, verifica no buff, ele identifica esses espaços e então salva esses valores dentro da nossa string. Então ele salvaria aqui o meu. Vou até deixar separado meu. E aí ele vai lá e coloca esse nosso caracter que indica o final da nossa string.
Depois ele salva o conteúdo da segunda palavra, nome. E aí esse conteúdo que ele acessou, ele é removido do nosso buffer, porém nós digitamos coisas a mais que não foram utilizadas. E isso vai fazer com que o nosso buffer tenha um lixo dentro dele. E pode ser que na próxima vez que a gente utilize o nosso scan F, ao invés dele pegar as próximas palavras que vão ser digitadas, ele pegue esse lixo. E o que a gente tem feito para evitar que isso aconteça é sempre utilizar esse espaço antes do nosso scan. Bom, para que
você entendesse exatamente porque que esse espaço faz com que a gente não pegue o lixo do buffer, nós teríos que perder um belo tempo aqui. Mas de modo geral, o que é importante é que você entenda a finalidade do uso desse espaço. Idealmente, pessoal, a gente quer sempre antes do usuário digitar algo, limpar o nosso buffer e depois que a gente receber essa informação do nosso usuário também limpar o nosso buffer, porque pode ser que o nosso programa continue executando e esse lixo acabe afetando a entrada. Então, nós vamos aprender como que a gente faz
para est limpando esse nosso buffer e a gente vai aprender também como que a gente faz para receber uma string que contém espaço. Se eu quiser, por exemplo, salvar o nome completo na string nome, né, usando o scan a gente não tem essa possibilidade. Então, bora lá. Vamos ver como que a gente faz a limpeza do nosso buffer. Limpeza do buffer. Vou simplificar esse nosso programa. Deixei aqui só uma string e vou colocar os métodos que a gente pode estar utilizando para limpar o nosso buffer. Nós podemos usar o comando FF flush std entre parênteses.
Só que esse comando só vai funcionar no Windows. Nós podemos utilizar o comando fps purge stdin. Só que esse comando só vai funcionar no Linux. E nós podemos também fazer uso do comando set buff. STDIN no ponto e vírgula, que vai funcionar nos dois sistemas operacionais. Vou deixar aqui Bolf. Esse comando set buff, ele faz parte de uma biblioteca que a gente já tá importando, que é a STDIO. Então, nós Vamos fazer uso principalmente dele, porque ele funciona nos dois sistemas operacionais. Se você lembrar aí do comecinho do nosso curso, né, eu expliquei para vocês
que você não vai conseguir gerar um ponto XZ que roda no Windows e roda no Linux, mas você pode escrever esse seu código, esse seu arquivo ponto C, mandar para um colega que está no Windows, ele não vai conseguir compilar. Então a gente faz uso aqui do set buff. Vamos limpar antes da gente receber a nossa informação e vamos limpar depois. E fazendo isso, a gente não precisa estar colocando esse nosso espaço aqui mais. Bom, a gente obteve um erro aqui, já tô vendo qual que é o problema, né? Eu coloquei a letra T e
é set buff, né? Ele já tá indicando aqui pra gente. Então, bora lá. Digite uma string. Meu nome não é Johnny. E agora a gente tá fazendo essa limpeza do buffer antes dessa informação do nosso terminal entrar nele e também estamos fazendo essa limpeza depois. salvar esse nosso arquivo e vamos aprender sobre os nossos outros métodos de entrada. Pessoal, a gente pode estar utilizando o método gets e a nossa variável, onde a gente vai armazenar essa nossa string. E o método gets ele armazena tudo menos a tecla enter. A tecla enter, ela é o barra
n em que a gente já vem utilizando, o quebra de linha. A gente tá recebendo um aviso aqui do nosso compilador, falando que Esse método aí ele não é tão seguro e sugerindo que a gente faça uso do fgets, mas não vamos nos preocupar com isso agora. Vamos digitar aqui. Meu nome não é Johnny. E agora sim, a gente armazenou uma frase dentro da nossa string. Um outro método que a gente pode estar utilizando é o fgets. O fgets ele vai ter três parâmetros. Então, a gente vai ter que colocar a variável que a gente
vai utilizar como entrada, a quantidade de caracteres que a gente vai processar na nossa entrada e vamos colocar STDIN para indicar que essa informação tá vindo do terminal. Você já percebeu que tem uma diferença entre o GETs e o FGETs, porque no FGETs a gente consegue especificar até quantos caracteres nós vamos armazenar. É por esse motivo que o compilador indica aí que o uso do GETS não é seguro. Agora eu posso digitar aqui vários caracteres excedendo aí bastante o número 50 e nós vamos armazenar somente 50 caracteres. Se eu digitar menos caracteres, nós vamos armazenar
a quantidade digitada e automaticamente o comando fgets adiciona o nosso caracter especial que indica o fim da nossa string. Por esse motivo, a gente consegue imprimir somente os caracteres que a gente digitou, não acessando nenhum tipo de lixo da nossa memória. Vou deixar comentado que o nosso FGS armazena tudo, incluindo a tecla enter. Para deixar isso mais claro para vocês, eu vou escrever aqui palavra. E na nossa string que a gente armazenou utilizando O fgets, a gente tem palavra barra n barra ou para indicar o fim da nossa string. Se a gente tivesse utilizando gets,
a gente teria palavra barra. Ele não armazena o barra n. Vou deixar um comentáriozinho aqui também sobre o nosso scan f. Não armazena espaços nem quebras de linhas, porque caso depois você queira utilizar esse nosso código para poder relembrar esses diferentes métodos, você possa só vir aqui e compará-los através desses comentários. Bom, em relação às entradas, pessoal, é isso. Strings, entradas, gets e fgets. Então, vamos ver alguns detalhezinhos sobre a saída de strings. Bom, nós temos aqui uma string com o nome João Castro. E a impressão convencional a gente já sabe fazer. Basta a gente
utilizar o nosso porcentagem S e o nome da nossa string. Mas imagine que a gente queira imprimir essa string somente a partir da quinta posição. Existe uma maneira para que a gente faça isso. Você pode tá utilizando o índice cinco, mas aí vai ser necessário fazer uso do operador de endereço. Dá só uma olhada. Agora a gente imprimiu Castro. Começamos a partir do quinto caracter. O índice que a gente utiliza vai ser o primeiro a ser impresso. E eu sei que pode estar parecendo um pouquinho confuso porquê da gente simplesmente agora tá utilizando o operador
de endereço no print f. E nós vamos ver isso com profundidade mais para frente. E eu prometo para vocês que Vai fazer todo sentido, mas por enquanto basta se lembrar que podemos imprimir uma string a partir de um determinado caracter, fazendo uso do índice no print f. Nesse caso, precisamos do operador. Veremos o porquê disso mais para frente. Fiquem em paz. Quem criou esse nosso compilador não estava maluco e eu te garanto que isso vai fazer total sentido. Beleza? Então vamos salvar esse nosso arquivo. Arquivo 09. Strings printf saída.c. E agora nós vamos nos dedicar
ao estudo de uma biblioteca chamada string. A biblioteca string.h traz alguns métodos prontos para utilizarmos em strings. Vamos ler alguns destes métodos. Nós vamos aprender o str len que verifica a quantidade de caracteres que temos na nossa string. Vamos aprender um método para comparar duas strings. Vamos aprender um método para copiar uma string. Vamos aprender a concatenar strings. Se você não sabe o que é concatenar, é quando a gente adiciona ao final. Imagina que a gente Teria numa string o nome hello, na outra a gente teria world e a gente quer concatenar, pegar o conteúdo
da segunda string e adicionar ao final da primeira. Então, para isso, a gente tem o método strat, adicionar o final o conteúdo de outra. Vamos ver também o str comp avalia se duas strings são iguais e existem muitos outros, tá? Se você acessar o seu navegador e procurar por string. Você vai encontrar diversos sites aí que vão trazer um manual dessa biblioteca. Todos são iguais praticamente, tá pessoal? Vou abrir aqui um em português e um em inglês. E aqui nós temos todos esses nossos métodos. Eu vou mostrar para vocês só alguns, mas já tô deixando
aqui o caminho para que você possa aprender outros. Vou mostrar esses aqui também, tá? Porque eu acho eles bastante úteis. encontra a primeira ocorrência de um dado caracter, encontra a última ocorrência de um dado caracter. E esse verifica se uma dada substring pode ser encontrada dentro de outra string. Então vamos lá. Vamos criar aqui três strings. Vamos colocar um conteúdo dentro dessas nossas strings. Vou Mostrar um site que geralmente a gente acaba utilizando quando a gente quer gerar alguns textos aleatórios. Chama lauren Y. Vamos gerar aí 10 palavras. armazenar nessa nossa string. Vamos criar uma
outra string com o mesmo conteúdo e uma terceira string vazia. A gente tá obtendo um erro, né? Eu vi um indicativo da nossa extensão, decidi compilar rapidinho porque nós temos mais caracteres do que nós temos espaço. Então vou aumentar aqui. Vou colocar logo 200, pessoal. Agora sim. Belezinha, tudo certo? Eu não sei exatamente quantos caracteres tem, mas eu posso descobrir. Nossa string 1 tem porcentagem de caracteres. E para descobrir, eu vou utilizar o método str len e vou enviar a string que eu quero verificar o tamanho dentro desses parênteses aqui. Bora dar uma olhada. A
nossa string 1 tem 91 caracteres. E que tal se a gente copiasse o conteúdo da string paraa string 3? conteúdo da string 3 é, vamos imprimir aqui a nossa string 3. Antes de realizar a nossa impressão, a gente vai chamar o nosso método str copy. Vamos colocar a nossa string número 3, a que vai receber o conteúdo. Ela vem primeiro, depois vamos colocar a nossa primeira string. Bora executar. Fizemos a nossa cópia. E se eu quiser concatenar o conteúdo, adicionar conteúdo ao final da nossa string 3, nós vamos utilizar o método strat, onde nós vamos
passar a string que a gente gostaria de armazenar o conteúdo e a string que nós vamos adicionar concateni. E vamos imprimir, né, conteúdo da string 3 após concatenar. É. Dá uma quebra de linha para ficar melhor de visualizar e bora. Concatenei. Adicionei ao final dessa nossa string. Comparando strings. Agora, como eu adicionei um conteúdo na nossa string 3, ela é diferente da nossa string 2 e da nossa string 1. Esse método, ele já vai retornar um valor inteiro. Então vou salvar aqui. Resposta comparação str e str. Aí nós vamos chamar esse método str com pair.
Vamos passar a nossa string 1 e a nossa string 2. E vamos imprimir aí o resultado dessa nossa comparação que eu disse para vocês que vai ser um inteiro. Já vou deixar aqui escrito que se a resposta for zero, as strings são iguais. Vamos fazer uma Comparação também entre a primeira string e a terceira. E aí, pessoal, vou deixar comentado, quero que isso fique bem claro para vocês, que se o valor for negativo, ou seja, resposta menor do que zero, o primeiro caracter diferente encontrado é menor na string 1. E menor aqui quer dizer que
tem um valor alfabético inferior. Isso aí, na verdade, tem a ver com a nossa tabela ASC, né? Vou mostrar para vocês aqui, pegar uma imagem aqui qualquer, tá? E cada letra ela tem um valor. E o valor ele é em ordem alfabética. Então observe, A é menor do que B, que é menor do que C, que é menor do que D e por aí vai. Então, se a gente retornar um valor negativo, isso quer dizer que a primeira letra que a gente vai encontrar de diferente, na nossa primeira string, nós vamos ter um valor menor,
ou seja, a gente vai ter a na nossa primeira string e, por exemplo, a letra m na nossa segunda. Vale a tentar, né, que as letras maiúsculas elas sempre vão ter um valor menor do que as letras minúsculas. Se o valor for positivo, ou seja, a resposta for maior do que zero, o primeiro caractere diferente encontrado é menor na string 2. Aí você pode estar se perguntando: "Eita, mas o que que vai acontecer no nosso código?" Porque dá uma olhada, as Nossas duas strings, elas são exatamente iguais até este ponto aqui, né? Essa aqui é
a nossa primeira string. Esse daqui vai ser o conteúdo da nossa terceira string, né? a gente copiou o conteúdo da primeira pra terceira e depois eu concatenei esse C. Pessoa falar: "Poxa, mas aí como é que vai comparar isso?" Vai comparar do mesmo jeito. Observem que na nossa tabela askim do texto, tá? E o valor do fim do texto, né? O valor decimal é 3. Então a gente vai est comparando 3 com o C maiúsculo, que tem o valor 67. Então nós vamos obter um retorno negativo, tá? Vamos ver se é isso que vai acontecer.
Só adicionar uma quebra de linha aqui comparando string com string 2, comparando string com string 3. E foi exatamente isso que aconteceu. Bom, esse é o método aí que esse str é o que pode nos confundir um pouco, mas a maioria das vezes aí você só vai precisar verificar se esse valor é zero ou não. Se for zero é porque as nossas strings são iguais. Porém, por uma questão de completude, eu acho importante trazer essa explicação. Vamos dar uma olhada agora strá que a gente vai usar para encontrar um determinado caracter. Também tem o strrchá,
que a gente vai utilizar para encontrar a última ocorrência de um dado caracter. Na verdade, na verdade, pessoal, eu não vou mostrar esses métodos para vocês agora, não. Eu vou só deixar ele aqui e vou explicar ele para vocês depois. A gente vai pegar a nossa string e vai verificar a primeira ocorrência do caracter P, por exemplo. Depois a gente pode estar realizando a impressão, o caracter, a gente usou o P, né? Foi encontrado e temos a partir dele porcentagem S. E vamos colocar aqui o P. Dá só uma olhada. A gente agora consegue ver
tudo a partir da primeira ocorrência deste caracter, que é o P. Tá? E aí a gente acabou utilizando aqui esse nosso operador que a gente ainda não viu, asterisco. Por esse motivo eu vou deixar para explicar para vocês depois esse método. Já esse daqui é a última ocorrência dessa letra, uma quebra de linha, né? E aqui eu não preciso estar declarando novamente. E beleza, beleza. Temos a partir dele, a gente encontrou aqui a última ocorrência da letra P. Fiquem tranquilos que vocês vão ver que isso aqui é bem de boa. Só não é o momento
agora. Vamos entender depois. E agora a ocorrência de uma substring. E nessa nossa verificação, a gente passa a nossa string, a palavra que a gente quer encontrar dentro dessa nossa string. E aqui a gente pode passar, por exemplo, a palavra sit. Se a palavra existir, vai retornar um ponteiro para a ocorrência dela. Se não existir, vai retornar nulo. Nosso caracter foi encontrado. Procuramos a palavra sit e ela está aqui. Pessoal, essa parte de strings, ela é extremamente importante, Tá? Processamento de caracteres. Todo mundo aí conhece o chat GPT, faz uso de chat bot, né? em
sites de compra, em outros lugares. Então, é importante que a gente aprenda esses métodos. Depois nós vamos exercitar tudo isso. String H biblioteca. C. Agora, pessoal, eu quero mostrar para vocês como que a gente pode receber uma string e transformar uma string em uma variável de um outro tipo. Ou seja, dá uma olhada aqui. A gente vai pegar ali do nosso buffer e vamos supor que a gente pegue como uma string, né, um determinado valor. 6,18, 7, 106 9.23 23, o barra N o barra O. Se a gente quiser transformar isso daqui, a gente vai
salvar dentro de uma string, vou imaginar que tudo isso tá aqui dentro da nossa string. E se a gente quiser, a partir disso, obter três variáveis, uma com o valor 6.18 do tipo float, outra com valor 7.67, outra com o valor 9.23. Como que a gente pode estar fazendo isso? Para realizar essas conversões, pessoal, a gente vai fazer uso de algumas funções do método stdlib. Então, vamos incluir ele aqui e vocês vão ver que é bem tranquilinho. Vamos criar uma string a de tamanho 4. Nela nós vamos colocar o valor 100. E para fazer essa
conversão, a gente vai chamar o método A toy. Vou explicar pr vocês Porque que ele tem esse nome, tá? Aí já fica a dica para vocês lembrarem. Esse nome vem de uma abreviação arrei to integer, por isso a toy de arrei para inteiro. Nós vamos ver também o arrei to float, arrei para inteiro. E vamos ver o array to long int, né? Array para long int. Deixar comentado. Convertendo string para inteiro. Convertendo string para float. A rate float. Vamos passar aqui a nossa string. Acabei chamando com o mesmo nome, vou trocar aqui para B. Realizar
a impressão. Convertendo string para long int. Vou só copiar porque é bem parecido. Então a gente tá fazendo long int. Aí vou deixar um valor um pouco mais alto. True long. E aqui nós vamos imprimir a nossa variável value long int. Se a conversão acontecer, a gente vai ver a impressão, já que a gente está utilizando o nosso símbolo de porcentagem no nosso print F. Inclusive, pessoal, o porcentagem D, lembrando que ele é só para inteiros, né? Ou é o porcentagem D ou é o porcentagem I. E nós temos o LI. Bora dar uma olhada.
Bom, e aqui a gente tá tendo um probleminha, né? Porque eu estou declarando essa variável do tipo float. Inclusive, eu acho que esse valor que eu coloquei aqui, tá? Ele vai estourar o meu LI, igual eu expliquei para vocês, dependendo da arquitetura, né? O conjunto de rádio que você tá executando não é só o processador, mas a placa mãe, o barramento, as peças do sistema operacional. Nosso long intmazenar Números tão grandes. Então eu vou baixar o tamanho do número aqui também. Não sei se daria problema, mas acredito que sim, né? E mudar o valor da
nossa variável. Bom, tudo certo. Conversões acontecendo. Bora salvar esse nosso arquivo. String to tipos primitivos de string para tipos primitivos. Bom, pessoal, mostrei para vocês aí como é que a gente faz essa conversão com uma string, né? Aí, nesse nosso exemplo, eu mostrei como é que a gente faria se a gente tivesse recebendo uma entrada ali com várias strings. Só que aí a gente ia ter que est usando aqueles métodos, né, que eu não vou explicar para vocês agora porque faz uso de um conteúdo que a gente vai ver lá na frente, mas fiquem tranquilos
que a gente vai voltar aqui depois e por enquanto vai ser suficiente a gente saber converter uma string simples, que só tem um número dentro dela para um dos nossos tipos primitivos. Dito isso, dito isso, é muito importante que a gente saiba também fazer o caminho inverso, converter de um tipo primitivo para uma string. Então, vamos dar uma olhada em como que a gente faz isso. Convertendo de um tipo primitivo para uma string. Na biblioteca STD libre, a gente também tem os métodos inversos. A gente viu que a gente tinha o array to int, array
to float, array to long int. E a gente tem também o int to array, a gente tem o Float to array, a gente tem o long intay, mas nós também temos um método para que a gente não tem que ficar gravando tudo isso, que a gente consegue fazer uso nas mais diversas situações, pessoal, que é o Sprint F. E inclusive esse daqui vai ter várias aplicações, mas não vou deixar de mostrar os outros não, só vou passar por eles um pouquinho mais rápido. Na conversão de int para um arrei, a gente tem aqui a criação
da nossa variável. a gente tá atribuindo um valor, a gente tem aqui a nossa string e aí a gente simplesmente chama o nosso método in to array, né, inteiro para um array, onde a gente passa a nossa variável ou um valor literal inteiro, a gente passa a nossa string e aqui nós temos a base, tá? Vou deixar comentado. Variável inteira, string e base. Pode ser base 2, base 8, base 16. E ele já faz essa conversão aí, salva numa string. A base mais comum de todas, né, é a base decimal, que é que a gente
usa aí para contar no nosso dia a dia. Vou mostrar para vocês o Ftoa. E no Ftoa a gente passa a nossa variável com float, a gente passa a nossa string e a gente passa também quantos números a gente quer depois da vírgula. Já long int para um array, a gente vai estar passando variável long int, a nossa string e a nossa base, da mesma forma que a gente fez na hora de converter um inteiro. E aqui a gente tá obtendo um erro, tá pessoal? Porque essa nossa função fto não é das nossas bibliotecas básicas,
mas não [roncando] tem problema, igual eu falei para vocês, nós vamos fazer uso Mesmo é do nosso Sprint F. De toda maneira, fica esses dois métodos quando a gente quiser converter inteiro ou long int. Então vamos aprender o nosso sprint f. Pessoal, eu gosto de pensar no sprint f como uma maneira de printar, porém sem ser no nosso terminal, mas sim dentro de uma variável. Então imagine que a gente tem a nossa string com 100 caracteres, sem nada dentro. A gente tem o nosso número inteiro, 25. A gente tem o nosso número float 3.14. E
a gente quer salvar não só o nosso número inteiro, mas também o nosso número float com algum texto dentro de uma string. O sprint fai permitir que a gente faça isso. A gente vai passar a nossa string primeiro, tá? E depois a gente vai passar a nossa impressão, assim como a gente fazia com o print f. Vou colocar aqui float porcentagem f int dois pontos porcentagem I. Coloco a vírgula depois das nossas aspas, pio. Agora para imprimir no nosso terminal, porcentagem S, string, tá feito, basta executar e problema resolvido. Então, pessoal, sempre pensem aí na
no sprint f Uma maneira de printar dentro de uma string e não no terminal. A única diferença é que a gente coloca a nossa string primeiro e depois a gente faz o uso como se fosse um print f, tipo primitivo para string e sprint f. Para você lembrar aí do nome, né? Esse S vem de string. A gente tá fazendo um print F e uma string. Bom, até aqui então, pessoal, a gente viu a rais, vimos strings, vimos limpeza de buffer, melhoramos a nossa capacidade de receber informações. Agora que a gente sabe manipular strings, aprendemos
a converter tipos para string, string para tipo, mas a base de tudo isso foram os arreis. acontece que existem arreis multidimensionais. Então eu posso fazer aqui um arrei tipo inteiro com dois colchetes. Mas antes da gente dar uma olhada nesses arreis multidimensionais e nas strings multidimensionais, porque se strings são arreis, a gente pode também ter essas strings de duas ou mais dimensões. Vale a pena que a gente pratique um pouquinho e aí a gente vai entender com facilidade o arrei multidimensional. Então, acessa o link que tá na descrição e vá paraa sua prática. E já
a gente tá de volta para conversar sobre arreis multidimensionais. Bom, vamos dar continuidade ao nosso estudo de arreis. Agora nós vamos ver arreis multidimensionais. Imagine só que eu vim aqui, né, e criei um arrei do tipo int, chamado arrei com 12 espaços reservados na minha memória. O que nós teríamos acontecendo seria algo parecido com o que a gente tá vendo na nossa tela. a gente teria 12 espaços armazenados e a gente acessaria cada um desses espaços modificando o nosso índice. Então, se eu quisesse acessar o conteúdo do meu espaço zero, eu usaria aqui o índice
zero, certo? Se eu quisesse acessar o conteúdo do meu espaço que tá na posição, na última posição, eu utilizaria o índice 11, correto? Existe uma maneira da gente usar diferentes índices para acessar esse nosso arrê. E com diferente, eu quero dizer, ao invés de ser só um número, a gente pode acessar através da combinação de dois números. Então, se eu fizesse algo desse tipo aqui, 3 e 4, o tamanho desse arreio e o tamanho desse arreio aqui seriam o mesmo. Na minha memória, eu ainda teria 12 espaços do tipo inteiro reservado, sendo que o inteiro
tem 4 bytes, né? Só que agora, ao invés de eu acessar esse espaço fazendo uso do índice, variando ali de zero até 12, 11, né? Eu vou acessar fazendo o seguinte, eu vou colocar zero e depois esse meu segundo índice vai variar de zero a três, quatro números. Então aqui eu vou ter o acesso aí às quatro primeiras posições. Se eu quiser acessar as quatro posições que estão no meio do meu arreio, eu vou colocar aqui o um e o que vai estar entre a primeira chave vai variar de zero até três. E por último,
para acessar essa última parte do meu arreio, né, eu vou colocar aqui o número dois e esse outro índice vai variar de zero até três. Na nossa memória, tudo tá sendo armazenado em um bloco, porém o que muda é o nosso acesso. Mas prestem atenção, algumas coisas acontecem de maneira comum. Todas as vezes que eu queria acessar um arrei de uma dimensão, eu tinha que colocar um número inteiro. Então, no arreio unidimensional, eu dou um inteiro e eu posso receber algo lá dentro que não necessariamente é o inteiro. Por exemplo, se esse meu arrei fosse
do tipo float, eu vou acessar o índice 1 e aí eu vou pegar um número -363,1024. Eu vou acessar a minha posição 7 e eu vou pegar o número - 0,35. Eu vou acessar a minha posição 5 e vou pegar o número 3,14. Então imagine como se a gente tivesse uma maquininha, né? uma maquininha onde a gente dá ali o índice, deixar comentado aqui. O índice te leva ao dado. E o dado pode ter qualquer tipo, mas o índice que vai te levar até lá, ele vai ter que ser do tipo inteiro. Com a rais
bidimensionais, a gente vai ter um índice que agora vai ser a combinação de dois inteiros, inteiro, inteiro. E aí ele vai me levar ao meu dado, que pode ser de qualquer Tipo. E por que que eu tô explicando isso daqui para vocês? Porque isso vai ajudar vocês a identificarem certos problemas que vocês vão poder estar utilizando arreis para resolver e se vale a pena de fazer uso de um arrei de mais de uma dimensão. Nessa nossa imagem aqui a gente pode enxergar um pouquinho melhor o que eu acabei de explicar para vocês. Em um arreio
unidimensional, a gente tem ali o nosso índice e o nosso índice seria aí o que tá nesse azul mais escuro. Então a gente tem o índice zero, o índice um, índice dois e o índice três, que ele é um número inteiro, mas o conteúdo, né, o que vai estar aqui dentro, né, quando a gente acessa através do uso desse índice, pode ser de qualquer tipo. Por coincidência, né, por coincidência, o que tá aqui é do tipo inteiro, mas poderia ser um float e futuramente vai poder ser até um tipo que a gente vai aprender a
criar, que é a nossa próxima matéria. Então, imagine que eu criei ali o tipo pessoa, né? Quero armazenar uma pessoa dentro de um arrei. Isso vai ser possível. Então, prestem bastante atenção que o nosso conteúdo que vai estar lá dentro, ele vai poder ser de qualquer tipo, até de tipos que nós vamos aprender a criar. No arrei de duas dimensões, os índices estão em azul escuro. E a gente vai usar esses índices. Esses índices vão ser sempre do tipo inteiro para poder saber aonde ir. Então, se a gente tiver querendo ir ali, por exemplo, nessa
posição aqui, a gente só vai chegar aqui através do índice, o índice 1 do, né? Então esse ali seria o m1, mas o conteúdo que está efetivamente aqui dentro da minha memória, ele pode Ser do tipo float, ele vai poder ser de qualquer tipo, tipos que nós vamos aprender a criar. E por que que isso é importante? Isso é importante porque você vai perceber que existem vários problemas que você consegue resolver através da combinação de números inteiros. Então, vamos dizer, por exemplo, que você aí tá fazendo um projeto 60 dias, né? Você tá, sei lá,
focado em perder ou ganhar peso e aí [roncando] você tem uma balança de ultra precisão muito fera em casa. E aí em 60 dias você vai colocar o seu peso ali todo dia, né? você vai armazenar esse seu peso. Então, se você perceber que dias só podem ser contados com números inteiros, você vai falar: "Pô, fazer um arrei ali de uma dimensão só. Vou botar um arrei de tamanho 60, vou acessar ali com os índices 0 a 59. E aí todo dia, né, eu vou ir lá, vou acessar uma posição e vou atualizar o meu
peso." Por que que você vai fazer isso? Porque, ah, vamos supor que eu queira ali verificar quanto meu peso mudou entre o dia zero e o dia 30. Então, é só pegar, entro no índice zero, no índice 30 e aí eu subtraio ali o valor, né, o maior pelo pelo menor. Eu vou saber aí quanto que eu perdi de peso até o dia 30. Ah, mas e se eu quiser saber quanto que eu perdi até o dia 45? Então você vai fazer esse mesmo processo. Só que se você der um passinho a mais, falar: "Pô,
não quero fazer um projeto de 60 dias. Acho que 60 dias é muito pouco. Já amadureci essa minha ideia, né? Inclusive é pouco até para estudar a programação. Vou fazer um projetinho de estudo aí e eu quero fazer um projetinho de estudo de um ano. Então Você poderia ir lá e criar um arre, mas aí vai ficar um pouquinho mais difícil, porque se você quiser ver ali, ah, será que eu estudei mesmo em fevereiro? Então fica mais complicado você acessar. Pô, deixa eu acessar os arreis ali do valor eh 32 até até os 50 e
poucos. Poderia pegar, né? Vou dar uma simplificada aqui e criar um arrei bidês, porque o mês é um número inteiro, não tem mês do tipo float e o dia, porque o dia também é um número inteiro. Então você criaria um arrei 12 por 31. E lembrando, né, que o tamanho, né, o tamanho do seu arrei quando ele é unidimensional, é o tamanho do índice. Então, se você criou de índice 4, o tamanho é quatro. Se você criou de índice 365, o tamanho é 365. Agora, no arrei bidimensional, você vai pegar lá a sua calculadora, vai
fazer 12 meses, coloquei 31, deu 372. Passou um pouquinho, mas não tem problema. Então, a sacada que você teve que ter para ver se era bom ou não usar uma rei foi perceber se você precisaria ali dar dois inteiros, né, para chegar nessa informação. Seria interessante, seria fácil, seria cômodo para você e seria porque de um lado você vai ter o mês, do outro lado você vai ter o dia. Aí qual o conteúdo que eu vou pô lá dentro? Tá? Depende. Então, no meu projeto, eu quero colocar ali quantas horas eu estudei. Então, você coloca
um float, coloca que você estudou, você pode ter estudado 2 horas, 2.5, né, 2.6, vai depender de você. Ou você pode colocar ali que basta que você tenha estudado naquele dia. Então, é mais interessante você usar o tipo intmazenar ali zero e um. Zero eu não estudei, um eu estudei. E vão existir problemas, pessoal, que você vai perceber que você vai poder usar ali três entradas inteiras. F. Ah, não, cara. Eu percebi ali que um ano é muito pouco, né, pra minha carreira, o tempo que eu vou investir em mim mesmo. Então, vou gastar ali
6 anos. Então, vou fazer um arrei de três dimensões, né? Vamos, vamos ignorar isso aqui por enquanto. Vamos escrever um codigozinho aqui com um arrei um pouco maior, né? Você quer verificar ali quanto tempo, quantos dias você vai estudar em 6 anos. Você faz um arrei ali, estudo 6 anos. Cada ano tem 12 meses e coloca cada mês com 31 dias. Aí você vai saber, você vai falar: "Poxa, se eu quiser acessar ali o ano um, né, você vai ver, pôs tudo ano um, o mês vai ser o segundo mês." Então, inclusive o um aqui
vai ser, seria zero, né? Lembrando que começa com zero. Então, ano um, o mês dois e você quer acessar ali todos os dias, né? Ver quanto que você estudou nesse ano um. Pode pegar e fazer um for, né? Faz um for int i = a 0. Enquanto i for menor do que 28, porque a gente nem vai querer acessar os últimos dias. A gente já sabe que é o mês de fevereiro. A gente faz e mais. E aí a gente colocaria ali um Print f, como a gente fez do tipo inteiro, porcentagem I. Vamos colocar
dia porcentagem I. Estudei ou não? Porcentagem I. E aí a gente faria a impressão aí usando o I, certo? que vai percorrer aí de 0 até 28. E a gente vai ver a impressão desse mês específico aí, mês de fevereiro, do primeiro ano que você começou armazenar essas informações sobre os seus estudos. Então, percebam, importante que você consiga identificar e sempre que você puder dar essa sacada para armazenar como um inteiro, então o inteiro já pode ser a coordenada e a informação que vai est lá dentro pode ser outra coisa. E eu disse para vocês
que a gente vai aprender a criar novos tipos, né? Então, pode ser literalmente qualquer coisa. Então o conteúdo que a gente vai est realmente acessando na nossa memória, ele pode ser de qualquer tipo, mas os índices, né, o que a gente vai usar para chegar até esse conteúdo, ele vai ter que ser do tipo inteiro. Então, pegando essa ideia, você vai perceber que você consegue modelar várias coisas, né, inclusive a tela, né, ou uma imagem. Por exemplo, a gente tem aqui, né, à esquerda, uma imagem de 100 pixels. Por que 100 pixels? Porque é o
tamanho da nossa matriz. Então ela tem 10 quadradinhos aqui na coluna e 10 quadradinhos na linha. Então de espaço efetivamente na nossa memória ali, a gente tem 100 pixels, ou seja, 100 desses quadrados aqui. E aí você pode perceber que se você tiver uma tela Preto e branco, né, de tamanho 100, né, imagina que você tem uma tela antiga aí preto e branco de tamanho 100 e você quer representar números. Aí você vai ver que você pode pegar a terceira linha, né? A posição zero, 1 2 3, vai ser tudo zero, né? Que zero seria
branco. Éí a posição 4 e c você colocaria o valor um e aí ela seria preto. Por aí você vai, você consegue representar várias coisas fazendo uso de matriz. Se a gente extrapolar isso daí, a gente consegue fazer aí uma imagem de 1000 pixels, né? E aí o pixel ele só serve pra gente acessar essa posição, né? Qual lugar, né? O que vai tá lá pode ser qualquer coisa. Nesse aqui poderia ser o inteiro. Nesse daqui pode ser, sei lá, uma coordenada RGB, uma outra coisa de um outro tipo. É, ou uma cor, pode ser.
Depois a gente vai ver. Poderia ser um texto, a gente poderia colocar qualquer coisa lá dentro. Então aqui a gente poderia fazer uma imagem aí de 1000 pixels, 100 x 100, ou seja, seria 1000 quadradinhos. E aí cada quadradinho desse, ele vai ter uma cor, podendo aí ser três cores, verde, o marrom e o vermelho. E a gente consegue fazer o desenho de uma maçã, inclusive, né, a sua tela, né, a tela que a gente chama de Full HD, se a gente for ver quantos pixels ela tem, né, seria 1080 de altura vezes 1920. Então,
ela tem essa quantidade toda aí de pixels. A única diferença é que a proporção aí da tela, ela não é quadrada, né? Então aqui a gente tá fazendo um quadrado. Então seria uma matriz aí de 1080 por 1920. Aí Você teria a matriz da sua tela, onde lá vão estar armazenados aí todas as informações de cor que você tá recebendo e vendo aí na sua tela, ok? E aí, pessoal, eu tinha colocado um monte de coisa aqui de aplicação matemática, só que eu resolvi tirar, tá? Porque eu não quero assustar vocês, mas se a gente
for ver aí, a matemática já faz uso de matrizes há muito tempo. Você pode usar matriz para várias coisas, né? Não sei se vocês já estudaram esse conteúdo, então não coloquei aqui para assustar, mas as aplicações, se a gente for entrar aqui, vão ser as mais diversas possíveis, mas que agora não vai ser tão relevante. Só serve aí como uma motivação pra gente estar estudando esse conteúdo. O que realmente vai ser útil para vocês é entender quando a gente faz uso da matriz, a gente tem entradas inteiras e uma saída qualquer. E a gente pode
sempre aí usar essa linha de raciocínio que eu fiz pr vocês de que dividir é melhor pr organizar. Fiz um arreio muito grande. Poxa, eu pensei aqui que é mais fácil, né? Eu consigo dar sentido. Pô, em vez de eu fazer 365 dias, vou fazer 12 meses, cada um de 31 dias. Vão ter casos, pessoal, onde não vai ser possível, né, você pensar dessa maneira. Aí eu vou trazer alguns exemplos também para que vocês percebam isso. Bom, uma uma coisa que é bem comum aí a gente dar dois valores de entrada e obter um valor
de saída, né, seria se a gente tivesse pensando sobre coordenadas no nosso planeta, latitude, longitude e a gente armazena ali se tá chovendo ou se não está chovendo, por exemplo, né? Então imagina que você com a latitude, com a longitude, você consegue mapear todo o nosso planeta Terra e aí você conseguiria ali ter um programa que vai salvar se está chovendo ou se não está chovendo. Pode parecer inicialmente que é bacana você fazer uso ali de uma matriz, mas a latitude e a longitude elas não são números inteiros, mas sim ângulos, né? Então, esses ângulos
aí, eles não são números inteiros, porque você pode ter um ângulo ali 66.3 e aí não daria certo. Bom, vamos ter várias situações também que parece que não dá pra gente fazer uso de matriz, mas a gente acaba conseguindo. Como por exemplo, imagina que eu quero fazer aí uma matriz para verificar se algumas pessoas se conhecem ou não. Então, eu poderia fazer aí, imagina uma matriz 10 por 10. Vamos fazer 3x 3 aqui só para facilitar. Aí eu coloco ali que zero seria o João. Um seria Bia e dois seria Karine. E aí para verificar
se essas pessoas se conhecem ou não, eu simplesmente acesso pelo índice dessas duas pessoas e vejo se o conteúdo que tá armazenado lá é um ou zero. Então, por exemplo, quero ver se a Karine conhece o João, ou melhor, se o João conhece a Karine. Então, entra aqui 0 do se o valor que tiver lá dentro for um, eles se conhecem. Se o valor que tiver lá dentro for zero, eles não se conhecem. E isso tudo, essas aplicações de matrizes, vocês vão aprender ao estudar algoritmos e estruturas de dados que, infelizmente, não dá pra gente
abordar nesse curso. Mas agora, o que é importante para nós é entender essa Motivação do por que existe essas matrizes, essas aplicações e aprender a manipular elas, como é que a gente vai realmente estar fazendo uso, armazenando coisas, retirando coisas. E como nós já vimos um pouco sobre o propósito e eu dei algumas dicas de como identificar quando é interessante a gente estar fazendo o uso, a gente vai ver agora como manipular essas matrizes bidimensionais. Então vamos lá, pessoal. Vamos dar uma olhada aí em como que a gente pode tá inicializando um arrei. Então imagine
que eu criei aqui o meu arrei multidimensional e eu fiz com que esse meu arrei fosse de tamanho aí 10x 10, totalizando 100 espaços reservados na minha memória. Mas a gente sabe que quando eu aloco esse espaço na nossa memória, ele pode ter qualquer valor lá dentro. Então, a primeira coisa que a gente vai fazer é tentar substituir esses valores por zero. Colocar aqui como inicializar um arrei. Para fazer isso, a gente vai fazer uso das estruturas iterativas. Claro que você poderia vir aqui e colocar o arrei 00 vai receber o valor zero. Aí você
faria o arrei 0 1 vai receber o valor zero e por aí vai, né? 100 vezes você teria 100 linha de código fazendo isso, o que apesar de ser possível é totalmente inviável. Então você perderia aí uma tarde inteira praticamente, só colocando valores de zero dentro de um arreio. E nós temos uma maneira de fazer isso utilizando aí quatro linhas de código. Antes de mais nada, eu vou deixar uma dica aqui para vocês, que é a seguinte: quando quisermos entrar em todas as posições de um arreio bidimensional, a gente vai Utilizar dois fors, onde o
tamanho do primeiro for vai ser igual ao número de linhas e o tamanho do segundo for vai ser igual ao número de colunas. E aí eu deixei aqui para vocês o que seriam as linhas e o que seriam as colunas. Mas antes da gente dar uma olhada nessa minha dica, vamos tentar também pensar sozinho, ignorando um pouquinho ela. A primeira coisa que você deveria perceber quando você tiver escrevendo na mão, atribuindo esses valores na mão, é que seria possível você fazer um for int = 0, onde j vai ir de 0 até 9 e você
vai fazer um incremento unitário. Você poderia simplesmente vir aqui e fazer aí o arrei na posição 0 J. vai receber o valor zero. Tendo essa sacada, você conseguiria aí reduzir o seu código de 100 linhas para 20 linhas. Bastaria você vir aqui e colocar também pra linha um, pra linha dois, pra linha três e por aí vai. Então, a gente já conseguiu aí fazer uso de uma estrutura interativa para facilitar a nossa vida. Só que se você der uma analisada, você consegue fazer uso de uma outra estrutura iterativa que vai cumprir esse papel de ficar
aumentando o valor da linha. Então você faria um outro for onde i seria igual a zero, enquanto i fosse menor do que 10 e mais. Aí basta fazer o uso do for alinhado e substituir a linha por i. Agora, em cinco linhas, a gente conseguiu resolver O nosso problema. Vamos dar uma olhada no que que tá acontecendo, realizando uma impressão I, J, quebra de linha. E vamos executar esse nosso código. Aqui acabei esquecendo de colocar na nossa impressão, né, o I e o J. E também de fazer uso das chaves, já que a gente vai
ter mais de uma sentença. Prontinho. Então, bora executar. E observem que agora nós temos todas as combinações para acessar os 100 espaços de memória que a gente reservou. 003 04 05 até o 09. Depois a gente vai 10 1 2 até a gente acessar a última posição. 99. A primeira posição era 0. Portanto, a gente acessou aí 100 posições e atribuímos o valor zero. Vamos verificar se o que eu disse para vocês realmente é verdade. Eu disse para vocês que quando a gente cria um espaço na nossa memória e não atribui nenhum valor, a gente
vai ter um lixo lá dentro, correto? Então vamos agora tentar imprimir esse conteúdo para ver qual lixo a gente vai ter lá na nossa memória, qual o valor que vai estar lá. E olha só, a gente começou aqui a imprimir 100 valores, né? Imprimimos aqui sem valores aleatórios estavam lá na nossa memória. E se eu executar esse programa de novo, os valores vão ser diferentes. Depois eu imprimir as minhas posições que eu acessei. E se eu quiser ainda posso tá vindo aqui, vou tirar essa quebra de linha também, vou tirar essa impressão daqui e vou
fazer uma nova impressão depois que a gente atribuiu esses valores. Olha só aqui a gente imprimiu Esses nossos valores. adicionar uma quebra de linha depois de sair do for para ficar melhor da gente visualizar. Então, primeiro nós criamos o nosso arreio, depois a gente imprimiu os valores que estão lá dentro, depois a gente acessou todas as posições do nosso arrei, colocou o valor zero em cada um desses espaços de memória, ou seja, em cada uma dessas areáveis e agora a gente imprimiu todas elas. Então pessoal, essa é a maneira mais comum da gente estar inicializando
um arrei, que é fazendo uso dessas estruturas iterativas, mas eu sei que pode ser um pouco difícil de entender isso, por isso eu deixei essa explicação do que que tá sendo feito. Estamos passando em todos os nossos espaços de memória e deixei essa dica aqui para que você faça uso dessa estrutura interativa aninhada, onde você vai ter aqui o número de linhas, o número de colunas e aí você sabe que você tá percorrendo todo esse seu espaço na memória. Antes da gente falar da nossa próxima maneira, vou salvar esse nosso arquivo. Arrei 2D, inicializando com
iteração. E eu vou mostrar para vocês um arrei de três dimensões. Esse daqui eu vou fazer um pouquinho menor, 2, 4 e 10. Mas de cara, se você pegou a ideia, você vai perceber que não vai ser muito difícil. Basta que você coloque mais um for. Enquanto x for menor do que 2, a gente faz x +. E aí nós vamos ter três for alinhados. Vamos estar percorrendo todo esse nosso arreio. Você pode fazer isso para atribuir um valor. Você pode fazer isso para realizar a impressão. Aqui eu Vou fazer para est atribuindo um valor
X e J. E o valor que eu vou atribuir a esse nosso arreio vai ser um. Depois se eu quiser acessar todas essas minhas posições, é a mesma estrutura. E aí eu vou est realizando uma impressão na posição. Vamos imprimir as nossas variáveis X e J. E o conteúdo vai ser o nosso arrei. Posição X I e J. Bora executar esse nosso código. Faltou a gente adicionar uma quebra de linha para ficar melhor de visualizar. Agora sim, a gente tem aqui o preenchimento desse nosso arreio de três dimensões. Ele acontece e depois acontece a impressão
do valor em cada uma dessas posições. Vamos salvar esse nosso arquivo. E o que eu espero de vocês nesse momento é que vocês se lembrem que pra gente saber o tamanho de um arrei unidimensional, basta a gente olhar o índice na hora de criar esse nosso arrei. Se a gente quiser saber o tamanho de um arreio multidimensional, basta que a gente multiplique esses índices na hora de criar esse nosso arreio. Então, 2 x 4 dá 8, 8 x 10 dá 80. Então, a gente tem 80 espaços reservados do tipo inteiro. E também é importante que
você tenha essa noção de como estar percorrendo cada uma dessas posições, fazendo uso de uma estrutura interativa. Uma outra maneira que é extremamente útil também, vou explicar para vocês em qual contexto ela é mais útil, é uma bem parecida com o que a gente já tinha visto aqui no nosso arquivo três de arre strings. Quando nós vimos arreis e vimos como inicializar eles, a gente viu que a gente poderia inicializar ali cada Variável do nosso arrei pelo índice. E a gente viu que tinha essa nossa maneira número dois e a nossa maneira número três, que
era iterando e basicamente aí fazendo uso da nossa maneira número um. Essa maneira número dois também existe para reis multidimensionais. Então a gente tinha ali a maneira número dois para reis unidimensionais e já para reis multidimensionais nós podemos fazer a mesma coisa. Mas agora a gente vai duplicar os nossos colchetes. Eu então teria aqui 1 2 3 4 1 2 3 4 v 1 2 3 4. Nós temos aqui então três linhas, correto? E quatro colunas. Eu posso estar atribuindo esses valores ao inicializar, da mesma maneira que eu posso não fazer isso, porque o nosso
compilador simplesmente vai vir aqui no nosso conteúdo e vai verificar quantas linhas e quantas colunas nós teremos. Repetindo, então, para garantir que você compreendeu, aqui nós temos três linhas e quatro colunas, que é o conteúdo de cada uma dessas linhas. Nós podemos fazer o mesmo pra rais de três dimensões. Nesse caso, nós teremos um colchetes a mais. E nesse caso a gente teria algo parecido com isso daqui que eu vou mostrar para vocês. Pra gente determinar o tamanho desse nosso arreio, mesmo que não seja necessário, eu vou começar pelo número de colunas. Então, o número
de colunas, batendo o olho aqui, eu consigo dizer para vocês que vão ser duas colunas. Agora, o número de linha a gente pode fazer uso saindo desse nosso colchete mais interno e vendo quantos deles a gente tem. No caso aqui são três e nós Também vamos ter três do mais externo, que a gente vai sair desse nosso colchete que a gente acabou de verificar essas vírgulas. E vamos ver aqui no azul que também vai ser três. Pessoal, mesmo eu tentando formatar, percebam que quando a gente tá trabalhando aí com três dimensões, já não fica tão
intuitivo a gente estar fazendo essa leitura, mas eu queria dizer para vocês que existe essa possibilidade. Então vou deixar aqui comentado. Assim como a reis unidimensionais, a reis multidimensionais podem ser inicializados com o uso de chaves. Muito bem. Então, qual seria a utilidade da gente estar inicializando o nosso arrei fazendo o uso dos colchetes? Pode ser que você vá se deparar aí com algum exercício, alguma atividade onde você vai ter matrizes, né, a reis de duas dimensões, que elas já vão estar preenchidas. E aí elas vão vir nesse formato com um colchete. Então você simplesmente
vai pegar e vai atribuir esse conteúdo a uma matriz ou um arrei bidal. Geralmente quando for parte do seu papel estar inserindo esses valores, você vai fazer aí uma interação passando por cada uma dessas posições e pedir para que o usuário digite. Mas caso já tenha essa informação e ela já esteja formatada dessa maneira, hoje com uso de a, com modelos geradores de texto, você consegue estar gerando aí para poder praticar esses arreis multidimensionais com esses valores. Vou mostrar isso para vocês rapidinho. Então eu posso vir aqui te fazer uso aí de qualquer LLM, né?
Larga de language model. No caso que eu vou fazer do chat pt mais popular. E vou pedir aqui, ó, me dê duas matrizes 3x3, Uma com todos os valores iguais a um e outra com todos os valores igual a zero, de modo que eu consiga inicializar um arrei na linguagem C. E pronto, eu já consigo pegar e tá fazendo aí uso desses dois arreis. Bom, não lembro se eu havia comentado com vocês. Esse nome matriz ele é muito recorrente, né? Quando a gente tá falando aí de arreis de duas dimensões, é um sinônimo, tá? matriz.
Vamos entender aí como um sinônimo de arreis de duas dimensões. Pessoal, antes de existirem os computadores, matrizes, arreis bidimensionais, já haviam sido em muito estudadas e a gente já havia encontrado várias aplicações para esse conteúdo. Então, nessa nossa lista de agora, vocês vão encontrar alguns termos, como soma essas duas matrizes. E caso vocês tenham alguma dúvida, porque é um conteúdo de álgebra linear também, além de ser um conteúdo de programação, eu sugiro que vocês procurem do que se trata a soma de uma matriz, do que se trata a multiplicação de uma matriz e tentem aplicar.
Se vocês não conseguirem, não se desesperem. Vocês podem estar acompanhando a nossa correção, mas enfrentem esse problema com segurança. Acreditem que vocês são capazes e mesmo que você gaste um tempo a mais, saiba que essas operações, esse conhecimento que você vai adquirir, você vai utilizar com certeza em diversos outros momentos. E agora, pessoal, a gente vai dar início aos nossos estudos sobre structs ou registros em português. Eu já havia comentado com vocês que existe a possibilidade de que a gente esteja criando um novo tipo. E é exatamente isso que nós vamos ver agora. Vamos dar
uma olhada na motivação por trás do nosso estudo de strugs. Imagina um programa que cadastra dados aí de uma pessoa com nome, idade e altura. Agora a gente já tem total condição de fazer Isso utilizando strings, o tipo inteiro e o tipo float para armazenar a nossa altura. A gente pede pro nosso usuário que ele digite o nome. A gente salva esse nome utilizando gets, que pode armazenar espaços. A gente pede pro nosso usuário digitar a sua idade. A gente salva a sua idade em uma variável do tipo intário editar sua altura e salva sua
altura numa variável do tipo float. Depois a gente consegue estar realizando a impressão do nome, idade e altura. Agora, o que que o nome, a idade e a altura tem a ver? O que que garante que juntos são os dados de uma pessoa? No nosso código nada. Inclusive, o local onde isso vai estar armazenado na memória pode ser totalmente diferente. Onde a gente tem o nosso arreio do tipo char, uma string, a gente vai ter 50 espaços reservados para um caracter. Depois, quando a gente criar a nossa variável inteira idade, não necessariamente ela vai vir
logo em seguida. Provavelmente ela não vai vir logo em seguida, mas pode acontecer, seria uma coincidência e a nossa altura também. Então, nada conecta essas variáveis. Como programador, seria muito difícil para você estar trabalhando com os dados dessa forma, já que você teria que, por exemplo, nomear essas variáveis com alguma palavra parecida para que te ajudasse a identificar. Pensa bem, porque a gente não sabe qual que vai ser o nome dessa pessoa, nem a idade, nem a altura. o usuário ainda vai inserir isso. Para resolver esse tipo de problema, a gente tem as structs ou
os registros. A gente pode agora criar o tipo pessoa, onde a gente vai unir essas três características: nome, idade e altura. A sintaxe do nosso comando, a Gente está vendo aqui à esquerda, a gente vai ter struct, o nome desse nosso novo tipo e esses tipos primitivos ou tipos que a gente vai criar também, que vão compor esse nosso struct. Vamos ver como é que isso ficaria no código structa, a gente vai ter uma string capaz de armazenar 50 caracteres. A gente vai ter uma idade do tipo inteiro e a gente vai ter uma altura
do tipo float. Alguns detalhes sobre as stricts, elas são criadas fora das funções do programa. Então, a partir de agora, a gente vai começar a escrever código fora da nossa função main pela primeira vez. Repare que o registro termina com ponto e vírgula e os atributos também. E lembre-se que nós estamos criando um tipo e não uma variável. Então vamos dar uma olhada no código. Nós vamos agora escrever fora da função main structõe uma pessoa. Nome é uma string de tamanho 50, int, idade, float, altura. Eu disse para vocês que quando a gente usa o
comando structável. E quando eu digo isso, o que que eu gostaria que você interpretasse? que nós não temos nenhum espaço reservado na nossa memória, então a gente ainda não pode armazenar nada ali dentro. E aí, como é que a gente faz para criar uma variável? Quando a gente quer criar uma variável do tipo inteiro, a gente faz intra variável, certo? Quando a gente quer criar uma variável de um tipo que nós criamos, a gente vai utilizar a palavra struct. Presta bastante atenção que essas duas palavras aqui, structa, elas estão correspondendo ao que seria a palavra
int. Então agora a gente reservou esse nosso espaço de memória, essa nossa variável do tipo pessoa e um dos atributos dessa nossa variável é o nome e o nome da nossa variável é P. Vamos agora pedir para que o usuário digite o nome e vamos fazer uso do comando gets pra gente poder receber esse nome. Se você se lembrar bem, quando a gente utiliza o comando gets, a gente não precisa fazer uso do operador de endereço. Dá uma olhada aqui. Então, voltando, vamos fazer uso do Gets. E aqui, pra gente acessar o conteúdo da nossa
variável P, a gente vai usar o ponto. O Visual Studio Code já mostra as opções e o nome pra gente poder estar armazenando o nome dentro dessa nossa variável pessoa. Vamos pedir para que digite a idade. E aqui vamos utilizar o SCAN F. Endereço P idade. Agora só falta a altura. Scan F, operador de endereço. Pon altura. Pra gente tá realizando a impressão, nós vamos fazer da seguinte forma. Para imprimir o nome, nós vamos usar o porcentagem S. Para imprimir a idade, a gente vai utilizar o porcentagem I. Para imprimir a altura, a gente vai
utilizar a porcentagem F. E aqui nós vamos utilizar o P nome, P idade, Paltura. Pessoal, agora quando a gente cria a nossa variável, ela vai estar toda junta. a gente acabou de criar um novo tipo e quando a gente armazena ali, por Exemplo, a nossa variável int A tem 4 bytes. Esses 4 bytes, cada um deles está lado a lado. Quando a gente faz a nossa instruct pessoa P, a gente tá criando uma variável P do tipo pessoa. O nosso compilador vai calcular ali o tamanho em bytes e vai alocar eles lado a lado, OK?
Pra gente acessar esses diferentes pedaços que estão lado a lado, mas que compõe essa nossa variável na nossa memória, a gente vai fazer uso do ponto. Então, ponto nome, ponto idade, ponto altura. Pra gente estar realizando a impressão também. E é importante você entender isso, porque se a gente tentar se imprimir fazendo uso de somente pessoa, nós não temos um porcentagem P pra gente imprimir esse tipo que a gente acabou de criar, mas a gente consegue imprimir strings, inteiros, floats. Por esse motivo, a gente usa aqui então o ponto nome. O erro que a gente
tá recebendo é porque eu não coloquei o ponto e vírgula. Igual eu informei para vocês, nós temos que fazer uso do ponto e vírgula na struct. Bora executar esse nosso código. A gente tinha ali um símbolo de porcentagem a mais. Escrevi sem querer. Tá dando aquele nosso aviso, né, pra gente não usar o gets, porque o gets não limita a quantidade de coisas que eu posso digitar, mas eu vou digitar menos de 50 aqui, então a gente não vai ter problema. Pronto, caio. O gets recebe espaço, beleza. Idade 26, altura 1,80. E tá aí, pessoal,
nosso programa funcionando. E agora nós resolvemos aquele nosso problema de que o nome, idade e altura não estavam associados. Agora eles estão todos amarrados. Vou salvar esse nosso arquivo. Agora nós vamos criar uma nova pasta e vamos dar uma olhada nas possíveis maneiras de inicializar uma variável. Então, nesse ponto aqui, eu criei a minha variável. Agora eu quero inicializar ela, ou seja, eu quero adicionar um conteúdo dentro dela. Quando a gente não inicializa uma variável, ela é só um espaço reservado na memória que pode ter qualquer tipo de lixo como conteúdo. Eu posso vir aqui
e fazer p nome igual a João, por exemplo. Eu não posso. Ah, e eu não posso porque eu estou fazendo uso de um struct. Não, não tem nada a ver. Se a gente for dar uma olhada na nossa matéria de strings e como inicializar, a gente vai perceber que o jeito que a gente tem de est inicializando a nossa string, né, quando a gente já declarou ela, então eu já declarei aqui, ó, acabei de reservar esse espaço. Agora eu preciso ir caracter por caracter. Nessa maneira número dois e na maneira número três, eu já estou
atribuindo valor na mesma sentença onde eu estou reservando esse espaço. Então eu posso fazer dessa maneira aqui, só que caso eu já tenha criado essa maneira aqui, não vai funcionar. A mesma coisa acontece aqui. A gente já criou um espaço, a gente tem um arrei e aí eu tô tentando fazer como se fosse uma string igual à outra. Isso não funciona. O que a gente precisa fazer, então, é utilizar o nosso método str copy. A gente manda a nossa primeira string p nome e a gente manda a nossa Segunda string que vai ser copiada para
aquele nosso espaço que vai ser João. Agora, em relação à idade, eu posso simplesmente vir aqui e colocar valor 25 em relação à altura também. E da mesma maneira que a gente fazia quando a gente criava variáveis do tipo inteiro, nós podemos criar mais de uma variável em uma só sentença. P e M. Trocar aqui para J. J de João. Vamos usar agora M de Maria. Então, STR COP M nome Maria M M Alura, 1.55. Então essa é a primeira maneira da gente estar atribuindo valores. Primeira maneira de atribuir valores. Uma outra maneira é bem
parecida com o que a gente já vinha fazendo, né, quando a gente tinha ali uma string. Se a gente criar a nossa string e atribuir conteúdo para ela numa mesma sentença, o nosso compilador resolve isso e permite que a gente não precise fazer uso do STR COP. Segunda maneira, com o tipo que a gente acabou de criar, a gente pode fazer o mesmo. Struct pessoa P vai ser igual. Aí a gente faz uso aqui dos colchetes Pedro 21.75 75 instanciando e atribuindo na mesma sentença. Lembrando, se você estiver confuso em porque que a gente tem
que fazer uso do strop, isso não está relacionado ao nosso strike e sim as strings. E o importante de você tá entendendo nesse momento é o conteúdo de strike. Então tá bom, pessoal? Aqui a gente já viu como a gente pode inicializar as nossas variáveis. Faltou Só a gente importar aqui a nossa biblioteca, né? Aí include string.h pra gente poder fazer uso do método strop. Beleza? Então vamos agora ver alguns erros muito comuns. Se eu tentar fazer algo do tipo if m for maior do que J, print F Maria é maior do que João. Eu
não vou conseguir, pessoal. Por quê? Porque eu estou tentando verificar se uma pessoa que tem nome, idade de altura, ela é maior do que outra pessoa. E o nosso compilador, pessoal, ele não vai saber se você tá querendo verificar se a idade da pessoa é maior, se a altura é maior ou até mesmo se o número de caracteres do nome daquela pessoa é maior do que o outro. Caso eu tente fazer m altura é maior do que J altura, aí sim esse nosso programa vai funcionar. Já a atribuição pessoal, ela é permitida, então a gente
consegue fazer struct pessoa P e X, P nome, P idade igual a 25, Pura = a 1.8 e X é igual a P. Vou realizar a impressão para mostrar para vocês que esse nosso código vai funcionar sem nenhum tipo de problema e tudo certo. Então pessoal, resumo da obra. Quando a gente cria um novo tipo, a gente não consegue fazer uso dos nossos operadores relacionais porque o nosso compilador não vai saber o que que ele vai ter que comparar. Então, imagina só, eu fiz o tipo prédio, structédio, e aí lá dentro Desse meu structo, eu
vou colocar várias informações, como por exemplo metragem, int, número de andares, int, número de pessoas comportadas no nosso prédio. E aí a gente estancia, cria dois prédios. Como é que a gente consegue comparar? Ah, esse prédio é maior do que esse? O nosso compilador não sabe se você quer ver qual que tem a metragem maior, se você quer ver qual que tem um maior número de andares, qual que é mais alto, qual prédio vai comportar mais pessoas. talvez seja um prédio comercial, ele vai comportar mais pessoas, apesar de ter uma metragem menor. Então, nossos operadores
relacionais não podem ser utilizados quando a gente cria uma strike. Porém, a atribuição ela é permitida que a gente tá reservando um espaço de memória que é do tipo prédio. Aí vamos supor que esse nosso espaço tá vazio ou tem alguma outra coisa lá, a gente vai só tirar as informações do outro prédio e colocar nessa nossa nova variável. Agora, pessoal, eu quero mostrar pr vocês uma coisa que eu já havia comentado, que é possível que a gente crie um arrei desse nosso novo tipo. Então, eu faço aqui uma struct pessoa p array e eu
vou colocar aqui cinco pessoas. Para poder armazenar todas as informações, eu vou fazer um for e vou a zero. Enquanto o I for menor do que 5, a gente faz o I mais. E coloquei aqui um print f digite o nome, gets para receber esse nome, print fit idade, scan f para receber essa idade, print fit altura, scan para receber essa altura. Vamos executar esse nosso programa, esse aviso padrão aí quando a Gente faz uso do gets. Nome da primeira pessoa, Pedro, idade 2 anos, altura 30 cm. Observe, pessoal, que mesmo fazendo uso desse nosso
espaço, né, a gente tá tendo aquele probleminha dos buffers. Então vamos limpar os nossos buffers antes e depois de usar os nossos operadores. Set buff stdin nul. Vamos colocar antes e depois de receber essas informações. Então, agora sim, a gente pode estar executando o nosso programa e inserindo a informação de cinco pessoas que vão estar armazenada em um arrei. Então, o nome aqui pode ser Pedro, idade 28, altura 1.8, Caio, idade 70, altura 1.6, Karine, por aí vai. Uma outra coisa que é interessante é a gente estar realizando a impressão. Vocês vão ver como vai
ser bem rapidinho também a gente realizar a impressão. Só para facilitar de alterar, eu vou criar aqui uma constante nomeada QT e vou deixar 2 igual a 0 quanto i for menor do que QT. Fazer I+ mais. E para imprimir o nome do nosso arrei foi p arrei. P arrei. A gente vai acessar a nossa posição I. E vamos acessar cada um desses nossos atributos. Nome, P, arrei na posição I, idade, P, arrei na posição I. Altura. Eu deixei aqui pra gente poder estar cadastrando duas pessoas e ele já vai estar realizando a impressão dessas
duas pessoas. Esqueci de colocar a nossa variável I no nosso print f. Vamos a mágica da edição. E pronto, nosso programa agora acabou de cadastrar pessoas e realizar a impressão de todas as pessoas que foram cadastradas neste nosso arrei. Então, bora consolidar tudo que a gente acabou de ver sobre struct. Quando nós criamos um novo tipo, a gente usa o termo struct, o nome desse nosso novo tipo. Fazemos uso das chaves e lá dentro a gente coloca atributos. Ao declarar um novo tipo, nenhum espaço é armazenado na nossa memória RAM, nenhuma variável é criada. Mas
agora o nosso compilador vai ter o poder de criar essa variável, porque ele vai saber qual tamanho ele deve reservar. Quando a gente quer instanciar uma variável, nós vamos dentro da nossa função main usar a palavra reservada stret, o tipo que nós acabamos de criar pessoa e o nome da nossa variável, que nesse caso vai ser P. A partir desse momento, a gente tá reservando um espaço na nossa memória RAM e a gente vai ter uma variável do tipo pessoa. Essa variável do tipo pessoa, ela composta por três atributos, sendo eles um arrei de caracter
ou uma string que ocupa 10 espaços. Lembrando que cada caracter ocupa 1 byte. Então a gente tem 10 bytes reservados, o atributo idade, que é do tipo inteiro, que ocupa 4 bytes, e o atributo altura, que é do tipo float, que também ocupa 4 bytes. Não é porque nós reservamos esse espaço na memória que a gente já tem algum conteúdo ali dentro, então a gente precisa estar armazenando algum conteúdo dentro dessa nossa variável. E para fazer isso, a gente pode pedir para que o usuário digite no terminal ou nós podemos fazer internamente. A maneira de
fazer internamente vai ser usando o strop, porque a gente já havia criado esse espaço anteriormente. Então não dá para usar aquela maneira dois, onde a gente consegue já atribuir o valor enquanto a gente reserva tudo em uma só sentença. Para fazer isso, a gente vai usar o método str copy, onde a gente vai copiar uma string para esse nosso espaço reservado na memória. Nesse caso aqui, a gente tá copiando o deixa like. E para Mandar o endereço da nossa string, a gente vai utilizar o nome da nossa variável ponto o nome do nosso atributo, que
é o nome. Se a gente quiser atribuir um valor para a idade da nossa pessoa P, nós vamos ter que fazer o mesmo. A gente vai ter que acessar essa nossa variável através do nome dela e depois nós vamos acessar esse nosso atributo através do ponto idade. E a gente vai conseguir atribuir esse valor aí 27 a esse atributo da nossa variável. Reparem que o conteúdo que está dentro do nosso tipo, eu vou chamar de atributo. Pra gente poder tá fazendo a atribuição de um valor à altura, o mesmo vai ter que ser feito. Nós
vamos ter que acessar esse espaço utilizando a nossa variável p e acessar esse atributo utilizando ponto altura, já que esse foi o nome que nós demos pro nosso atributo ao criar o nosso tipo. Espero que agora esteja ficando mais claro algumas operações e porque elas funcionam ou não. Então, se eu tiver, por exemplo, duas variáveis e eu quiser utilizar uma operação de atribuição, eu vou simplesmente pegar o conteúdo que tá num outro espaço da minha memória RAM e copiar para cá, sobrescrevendo que já está lá. Mas e se a gente tiver falando de um arrei?
Se a gente estiver falando de um arrei, o nosso tipo aqui é o mesmo, tipo pessoa, mas o espaço que a gente vai estar reservando agora não vai ser só de uma variável, nós estamos criando uma sequência dessa variável. Nesse caso aqui, eu utilizei o comando struct. Então, nós vamos estar reservando o espaço da memória suficiente para armazenar duas pessoas. Se a gente Quiser atribuir um valor ao conteúdo de um arreio desse nosso tipo pessoa, nós precisamos ficar atentos para fazer uso dos colchetes. Observe aqui a gente tem o strop e a gente tá utilizando
P, que é o nome do nosso arrei. Colchetes posição zero, nome, deixa like. Se a gente quiser estar atribuindo a idade, nós vamos fazer o mesmo. P cochetes 0 acessa o nosso atributo ponto idade. Pra altura p cochete 0 ponto altura. E a gente atribui um valor ao nosso atributo à altura da variável na posição zero do nosso arrei. Essas três linhas aqui eu acabei escrevendo errado. Eu queria ter colocado P1 pra gente estar atribuindo valores à segunda posição do nosso arrei, mas eu acho que foi até melhor. Então vamos dar uma olhada no que
que tá acontecendo no nosso código. Como eu estou utilizando P0 nome e copiando essa string se inscreva, nós vamos estar substituindo o conteúdo da nossa primeira variável do nosso arrei. Então, a gente vai estar tirando o conteúdo, deixa like e substituindo por se inscreva. A mesma coisa em relação à idade, mas agora o valor é o mesmo e em relação à altura também. Agora para finalizar o nosso estudo sobre stores, eu quero mostrar para vocês um novo comando, o type def. Existe uma maneira na linguagem C da gente estar criando apelidos. Então, por exemplo, se
eu vier aqui e fizer type def inteiro, esse comando de criar apelido é o type def, tá? A partir de agora, eu a partir de agora eu vou poder utilizar o nome inteiro para criar variáveis. Então eu poderia, por exemplo, fazer um programa onde a gente tá digitando dois inteiros e somando, mas agora não mais utilizando a palavra int, mas sim um apelido que nós acabamos de criar, que é a palavra Inteiro. Vou executar. Digite dois inteiros, 2 e oito. Observe que o funcionamento do nosso programa é o mesmo que utilizando a palavra int. Trouxe
aqui mais um programa fazendo uso do struct, onde agora eu criei o structo. Ponto. Ele tem sempre duas coordenadas. E eu vou mostrar para vocês como é que a gente pode estar fazendo uso do nosso comando type def para facilitar a nossa leitura. Percebam que até então toda vez que a gente declara uma variável, a gente usa sempre uma palavra, ou é int, ou é float. Às vezes nós fazemos uso do long double, que é o tipo de uma variável que faz uso de duas palavras reservadas, mas geralmente é uma só palavra. Então, que tal
a gente combinar o comando que a gente acabou de aprender com o structpnt. E a partir de agora, em vez de ter que usar essas duas palavras struct, eu simplesmente posso usar pnt para criar a minha variável ponto. É claro que isso é só um apelido, você pode ainda fazer uso daquele comando structure, mas a elegibilidade do código, à medida que nossos programas forem ficando mais complexos, ela pode acabar sendo muito afetada. E se a gente tem esse recurso, por que não fazer uso? Então, vou salvar esse nosso programa. Acontece que eu posso estar digitando
da maneira que eu fiz aqui, mas nós podemos também fazer uso do type def junto da declaração do nosso novo tipo. Basta que a gente coloque type def struct e aqui embaixo a gente coloca o nosso nome PNT. Sobre esse tipo de abreviação, eu tenho alguns comentários a serem feitos. A gente pode fazer uso do mesmo nome aqui em cima e aqui embaixo. Inclusive a gente pode não fazer uso de nome nenhum. E a gente vai sempre tá acessando esse nome através do nosso apelido, caso a gente não coloque nenhum nome depois do nosso strict.
Desse jeito aqui o nosso programa funcionaria tranquilamente, porém eu sempre recomendo que vocês coloquem. Não tem problema que você coloque o nome repetido. Em alguns compiladores já teve, mas você não vai encontrar esse problema. E por que que eu recomendo que você faça isso? Futuramente a gente vai se deparar com situações onde a gente vai criar um tipo e dentro desse nosso tipo vai ter uma variável desse próprio tipo. Então para fazer isso, a gente vai ter que usar esse nosso nome e o strctou. Eu sei que pode não fazer muito sentido agora, mas vai
fazer mais pra frente. E a necessidade é porque a gente não pode estar utilizando essa nossa abreviação, já que ela nem apareceu ainda, mas o nosso strict pntim. Fica tranquilo que quando a gente for fazer isso eu explico um pouco melhor. O que é importante você saber é que você pode colocar uma palavra qualquer aqui, pode colocar palavra nenhuma e que eu recomendo que você coloque. Eu geralmente faço uso da mesma palavra. Uma outra coisa, não é porque a gente está escrevendo a nossa abreviação desse jeito, que estaria errado a gente fazer uso do structo,
pnt, desde que a gente tenha colocado o nome aqui em cima. Observe que se eu remover esse nosso nome e tentar executar o nosso código, a gente vai ter um problema. Mas caso eu adicione, sem problemas. É importante que vocês estejam praticando para se acostumarem com o uso do ponto e com essa ideia de est acessando atributos e não somente uma variável. Então, dá uma olhada na nossa lista que tá aparecendo aqui na descrição e eu vejo vocês daqui a Pouquinho. Então, agora a gente vai est dando início ao nosso estudo de funções e eu
quero adiantar para vocês que a gente já vem fazendo uso de funções desde o nosso primeiro código e agora a gente vai poder estar entendendo muito melhor tudo que nós já fizemos até agora. Pois bem, antes de mais nada, vamos criar um programa com o menu. Vou dar play nesse nosso programa. Por enquanto, o que a gente tem são só essas impressões. Coloquei o que corresponde a um cabeçalho para ficar mais bonitinho e listei as nossas opções. Aqui eu criei a variável opção, vou receber ela com scan f e agora vou fazer uso do comando
switch. Já vou escrever aqui todos os nossos possíveis casos, caso zero, caso um e caso dois, e adicionar os breakes. Se o usuário escolher sair do meu programa, eu quero imprimir o meu cabeçalho e uma mensagem avisando que o programa tá sendo encerrado. Agora só adicionar a mensagem. Se ele escolher a opção soma, eu quero imprimir o meu cabeçalho, pedir para ele editar dois números e então informar o resultado da soma desses dois números. Já fiz as impressões. Agora basta eu criar minhas duas variáveis e receber o valor delas. Feito isso, posso realizar a impressão
e já vou fazer a soma dentro do nosso print. Caso o nosso usuário escolha a opção dois, eu quero imprimir o nosso cabeçalho, pedir para que ele digite o número. Vou calcular o fatorial desse número e depois vou imprimir esse valor. Impressão feita, vou criar a minha variável para receber a entrada do usuário. Depois de receber, vou fazer o cálculo do fatorial. E para isso, eu vou criar uma variável fatorial que vai começar com o valor um. Aqui nós temos uma iteração que vai ir de um até o número inserido pelo nosso usuário e vai
Ir somando todos esses valores. Agora basta realizar a impressão. Vou colocar também um caso default. Imprimindo o nosso cabeçalho. Imprimir o nosso cabeçalho informando que a opção inserida é inválida. Vamos ver esse nosso programa em execução. Vou escolher a opção soma. Vou digitar dois números para serem somados e aí eu vou obter esse resultado. Se eu tiver executando, escolher a opção fatorial, vou digitar um número e vou obter o resultado do fatorial desse número. E agora, pessoal, para melhorar a nossa impressão, eu quero limpar a nossa tela. Existe uma biblioteca que a STD libero do
comando system, a chamada de uma função agora, ou melhor, o uso da função system. E a função system serve para que a gente consiga executar algum comando dentro do nosso terminal, ou melhor, que o nosso programa em tempo de execução, enquanto ele tá sendo executado, ele execute um comando no terminal. No Mac e no Linux, a gente vai fazer uso do comando clear. Já no Windows, a gente vai ter que fazer uso do comando CLS. Apesar de eu estar utilizando aqui o Power Shell, que nos permite fazer uso desse comando clear, que funciona tanto no
Mac quanto no Linux, se eu tiver fazendo uso do cmd, eu não vou conseguir. E também por causa dessa nossa biblioteca STD lib, como ela foi pensada pro Windows, pro cmd, a gente vai ter que usar o comando CLS, mas que tem o mesmo funcionamento. Vou mostrar para vocês. CLS, ele simplesmente limpa a nossa tela. Então, system CLS. E agora eu vou ter que adicionar isso em todas as vezes que eu utilizei o meu cabeçalho. Então bora lá. Então vamos executar para vocês verem como é que funciona esse nosso comando CLS. Bom, eu não sei
por, tá, pessoal, não tô conseguindo replicar, mas quando eu tentei executar pela primeira vez, eu recebi um erro ao tentar executar esse meu código com code runner. E basicamente o que eu fiz foi fazer uso dos nossos comandos de compilação padrão gcc main.c e main. Tentei executar o meu arquivo para verificar se o problema era realmente com a extensão code runner. Ele executou e agora vou tentar fazer uso. Tá simplesmente rodando. E observem, agora nós estamos limpando o nosso terminal antes de realizar qualquer impressão. Então nosso programa está começando a ficar com uma cara melhor.
Vou escolher a opção dois. Ele vai pedir para que a gente entre com o fatorial. Vou adicionar aqui o fatorial 9 362.000. Percebam, quando eu quis fazer uso deste comando para poder limpar a nossa impressão, o nosso cabeçalho, eu tive que copiar ele e fazer uso dele quatro vezes. E imagine que eu tivesse errado aqui o meu título ou quisesse fazer um cabeçalho um pouquinho diferente, aumentar o tamanho, trocar o que tá escrito, centralizar ele um pouquinho melhor. Eu teria que est vindo aqui e removendo esses espaços em cada uma das vezes que eu fiz
uso desse código. Então aqui nós temos um baita problema na hora de estar reaproveitando o nosso código e aprendendo a fazer uso de funções, a gente não vai ter mais esse problema. A gente vai conseguir reaproveitar nosso código com muito mais eficiência. Então vou mostrar para vocês e claro que logo em seguida a gente vai ver uma explicação em detalhes sobre isso. Vou criar aqui a função void imprime cabeçalho e ela vai ter uma aparência bem parecida com a nossa função main. Aí ela vai vir com a palavra void, o nome da nossa função, parênteses
e chaves. E agora eu vou Copiar o nosso código de limpar o terminal e imprimir o nosso cabeçalho. E tá aí, acabamos de criar a nossa função. Como é que a gente faz para est executando essa nossa função? Agora a gente pode substituir o nosso código aqui pela chamada dessa função, utilizando o nome da nossa função e os parênteses. Vou fazer o mesmo para todos os outros casos. Bora executar o nosso programa para ver se o funcionamento se manteve igual. Soma dois números, soma igual a 17. Observem que agora o nosso código ficou mais curto,
mais legível e eu posso melhorar ele ainda mais, criando, por exemplo, a função imprime menu inicial, chamando essa minha função, imprime menu inicial. E agora fica melhor de visualizar. Eu sei que a minha função main vai primeiro imprimir o meu cabeçalho, depois ela vai imprimir o meu menu inicial. Dessa vez não foi por uma questão de reaproveitar o nosso código, mas para facilitar a leitura. Mas como é que a gente faz para est fazendo uso de funções quando a gente tiver que realizar, por exemplo, uma soma? Antes da gente dar uma olhada em como a
gente cria funções um pouquinho mais complexas, vamos aprender sobre a sintaxe da criação de uma função. Na criação de uma função, a gente vai ter sempre esses quatro elementos: o tipo de retorno, o nome da função, os parâmetros e o corpo da função. O nome da função nós escolhemos. No nosso código, por exemplo, a gente escolheu o nome imprime cabeçalho. Os parâmetros de uma função são as informações que a gente vai estar enviando para essa nossa função. Nós já fizemos uso desses parâmetros quando a gente aprendeu sobre string e fizemos usos de algumas funções que
foram Importadas da biblioteca string. Se você analisar bem, a gente fazia uso do nome da função str copy e passava como parâmetro duas strings. Ou seja, entre parênteses, nós teremos o conteúdo que a gente vai est enviando para essa nossa função. Entre chaves, a gente vai ter o código que compõe a nossa função. E o tipo de retorno que eu deixei para falar por último é aquilo que a gente está menos familiarizado, porque as funções que a gente vem utilizando até agora são em sua maioria, sem nenhum tipo de retorno. Para essas que não possuem
nenhum retorno, a gente vai usar o termo void. Vamos dar uma analisada na função que a gente criou desde a nossa aula um, a função main. E se é uma função, não importa se ela é amém ou não, ela vai respeitar a nossa sintaxe. A gente tem o tipo de retorno. Nós viemos utilizando o tipo de retorno como void, que significa que a função não tem nenhum retorno, o nome da função que é main, não temos nenhum parâmetro na nossa função main, então podemos dizer que ela não possui entrada. Já o corpo da nossa função,
que é tudo que o nosso programa vai executar, vai estar entre chaves. E se você se lembrar bem, eu disse para vocês que o nosso compilador sempre vai procurar pela função main, ou seja, a gente pode ter várias funções do nosso código, mas quando a gente compila, a gente vai gerar um arquivo executável que vai inicializar sua execução pelo conteúdo da nossa função main. Bora ver na prática que vai ficar um pouco mais claro. Eu vou criar aqui uma função que vai retornar um número do tipo inteiro. O nome dessa minha função vai ser calcula
fatorial. O parâmetro que ela vai receber, que neste caso é um Parâmetro único, vai ser do tipo inteiro. E eu vou dar um nome para esse meu parâmetro, n. E agora a gente vai poder trabalhar com o conteúdo dessa nossa função. Pessoal, todo parâmetro que a gente recebe, ele vai funcionar como uma variável dentro da nossa função. Pra gente realizar o cálculo do fatorial, a gente vai criar uma variável. Vou chamar essa nossa variável de fat. Vamos começar com o valor um. Vou fazer uma iteração. Vou inicializar a variável i com a variável n, que
é o parâmetro que foi enviado. Então, vamos supor aí que a gente tenha recebido na nossa função o valor 5. Então, a gente vai fazer com que o i comece valendo 5. Depois nós vamos continuar executando a nossa iteração enquanto i for maior do que 1 e vamos fazer um decréscimo. Então esse nosso for vai executar se o n for igual a 5, uma vez com i valendo 5, uma vez com i valendo 4, uma vez com i valendo 2 e uma vez com o i valendo 1. E o fatorial é exatamente essa operação, onde
a gente vai começar multiplicando o conteúdo da nossa variável fat primeiro por 5. Depois nós vamos multiplicar esse resultado que é 5 pelo nosso novo valor que é 4. Depois nós vamos multiplicar pelo nosso novo valor que é 3. depois por dois, depois por um novamente. Então basta que a gente coloque aqui a nossa variável i. Se você estiver com dúvida, dá uma pesquisada sobre fatorial e tenta desenhar o que tá acontecendo aqui. Mas lembre-se que o importante nesse momento é a gente entender o funcionamento das funções. Bom, como eu coloquei um retorno do tipo
inteiro ao final da minha função, eu preciso retornar algo. E o que eu vou retornar vai ser o valor da minha variável fat. E agora, como que a gente faz uso dessa nossa função? Para simplificar, eu remover aquele switch para que a gente possa focar somente na criação de uma função e no uso dessa função. Pra gente fazer uso, a gente precisa lembrar que a gente vai obter como retorno um valor inteiro e a gente olha verificando a palavra que antecede o nome da nossa função. Se o nome fosse void, a gente não receberia nada,
mas como o nome é int, a gente vai estar recebendo algo. Então, vou criar uma variável aqui inteira, vou chamar ela de resultado e vou armazenar na minha variável resultado o que a nossa função vai retornar. Eu então vou chamar a minha função calcula fatorial. E aí, como que eu sei o que eu tenho que passar? Eu olho entre parênteses na criação da minha função o que ela precisa receber e eu vou estar enviando para ela aí um número inteiro. Posso estar enviando aí o número cinco. Feito isso, eu vou realizar a impressão. Resultado de
vai ser igual porcentagem I. Aqui eu vou colocar o valor 5, porque é o fatorial de C que a gente está chamando, e vou colocar a variável resultado. Vamos executar esse nosso programa para ver como é que ele tá funcionando. E a gente obteve um erro porque na hora que eu mudei para esse nosso novo arquivo, eu coloquei ali o valor zero, então corrigi pro valor 1. E agora sim obtemos o fatorial de 5. 120. Uma outra coisa que eu gostaria de falar para vocês sobre as nossas funções é que vamos mudar ela de lugar,
vamos colocar ela depois da nossa função main. Se a gente tentar executar esse nosso programa, nós vamos ter um problema, porque eu disse para vocês que o nosso compilador ele executa primeiro as Instruções da nossa função main. Então, a gente está chamando a nossa função calcula fatorial, mas ela ainda não existe porque ela está vindo depois. Então a gente precisa estar sempre criando a nossa função antes da nossa função main ou fazendo uso das assinaturas, que é o que a gente vai ver agora. Porém, percebam porque que todo código ele tem o seu include antes
da nossa função main. Quando nós fazemos uso do include, a gente tá basicamente pegando um código, copiando e colando nesse nosso código. E esse código que a gente tá copiando, ele vai trazer várias funções. E aí a gente importa fazendo uso da include stdi ou a função print f. Percebam que a função print f ela não retorna nada, ela recebe uma string, ela recebe esses valores que vão ser substituídos dentro dessa nossa string. E esses são os parâmetros dessa nossa função. Então a função print f, ela é uma função cujo retorno é void e que
os parâmetros são esses daqui. A gente pode estar enviando um ou vários, né, pra função print f. A função scan f também é uma função que recebe um parâmetro, que é uma string e depois duas variáveis. Por exemplo, a função str copy recebe duas strings, ou seja, dois arre. A função str cat para concatenar o conteúdo, recebe duas strings também. A função limpeza de buffer também tem os parâmetros que ela acaba recebendo. Esses aí a gente só decorou, não expliquei para vocês. E as funções de entrada também. A função fgets, ela recebe a string onde
ela vai armazenar, a quantidade que ela vai ter que armazenar e este outro parâmetro aqui que é o STDIN. Mas voltando no nosso problema anterior, caso a gente crie uma Função depois da nossa função main, a gente pode fazer uso de uma assinatura de função que vai nos permitir chamar essa função, mesmo que ela tenha sido criada depois da nossa função main. E para fazer a assinatura, a gente vai colocar o retorno da nossa função, o nome da nossa função e os parâmetros da nossa função. A diferença na nossa assinatura é que a gente não
precisa dar nome para esse nosso parâmetro. dessa variável que a gente vai receber e nem colocar as chaves para colocar o conteúdo. Dessa maneira, a gente criou uma assinatura. Na assinatura a gente coloca então o retorno, o nome e a entrada, ou seja, o que a nossa função vai receber. Vamos agora tentar executar o nosso código. E mesmo com a nossa função calcula fatorial tendo sido criada depois da nossa função main, pelo fato da gente ter essa assinatura, o nosso código está sendo executado. Assinatura, então só serve para que eu possa criar funções depois da
minha função main? Com certeza não. Pode parecer que só serve para isso, mas eu vou te mostrar que não. Dá uma olhada nessa situação aqui. Nós temos duas funções. Uma função se chama imprime par. Ela não tem retorno nenhum e ela recebe um inteiro. Ela verifica se esse nosso número é par e se ele for par, a gente imprime é par. Se ele não for par, a gente vai chamar uma outra função chamada imprime ímpar. Essa função imprime ímpar não tem retorno. Ela recebe esse número, verifica se o número é ímpar e se ele for
ímpar, ela imprime. Se ele não for ímpar, ela chama a função imprime par. Na nossa função main, a gente está chamando a função imprime par e passando o valor número três. Tudo bem? A nossa função imprimir par tá acima da função main. Nós vamos entrar nela e vamos começar a nossa execução. Parâmetro que a gente vai receber vai ter valor três. Nós vamos então verificar 3 módulo 2 vai ser igual a 1 e 1 é diferente de zero. A gente vai obter o valor verdadeiro. Obtendo o valor verdadeiro, vamos chamar a função imprime ímpar. Opa,
agora a gente tem um problema. A nossa função imprime í impar está abaixo da nossa função imprime par. Então a gente obteria um erro. Em um código mais complexo, é muito comum que funções chamem outras funções. Inclusive, não fique assustado, porque você já viu isso acontecer e levou numa boa. Lembra aquele primeiro código que a gente fez do cabeçalho? Então, lá nós criamos a função imprime cabeçalho e a função imprime cabeçalho chamava função print f e isso não te assustou. O que é importante que você perceba aqui é essa utilidade. Às vezes funções vão estar
chamando outras funções e a gente não sabe em qual ordem necessariamente isso vai acontecer. Por esse motivo, é importante que a gente faça uso das assinaturas. Aqui nós temos algumas informações sobre as assinaturas, como por exemplo, que a assinatura não faz uso das chaves. A gente não tem o conteúdo, como essa nossa função vai se comportar. Nós só temos o retorno, o nome e os parâmetros, seguido de ponto e vírgula. Usando as assinaturas, as funções podem ser implementadas em qualquer ordem. E o uso de assinatura é sempre recomendado, pois ele serve como Um sumário, um
resumo das funções que existem no nosso arquivo. Isso pode não parecer, mas é uma das coisas mais importantes sobre funções. Vamos entender o porquê. Deixando de lado tudo que a gente sabe sobre programação, se a gente for entender funções na matemática, a gente vai perceber que as funções são como máquinas que realizam algum tipo de operação interna que muit das vezes a gente não precisa entender profundamente, mas que recebem o valor, aqui a gente tá recebendo o nosso valor X e que devolvem o valor Y. Percebam, eu não estou falando de programação, e sim da
matemática. Então, dá uma olhada nesse exemplo. Sem possuir um computador, a gente consegue criar uma função matemática que vai calcular o salário. A gente vai enviar para essa nossa função o número de horas extras do nosso funcionário e nós vamos obter o salário. Se o nosso funcionário entregar 0 horas extras, ele vai receber o valor de 4.600. Basta que a gente resolva essa nossa função matemática aqui. Se o nosso funcionário entregar 40 horas extras, ele vai obter o valor de 4800 + 4.600, dando um total de R$ 9.400 de salário. Aqui nós temos uma outra
função, agora uma função quadrática, né? a gente tem o x elevado a 2. Se vocês não estiverem lembrados nem um pouco disso, recomendo que vocês revisem o conteúdo de funções, funções do primeiro grau, funções do segundo grau. Voltando aqui, então nós temos o custo de produção de um tênis. Aqui nós vamos informar a quantidade de tênis que nós vamos produzir e a gente vai obter quanto a gente vai est gastando para produzir esse tênis. Funções de custo de produção geralmente tem um comportamento igual aquelas funções de segundo grau que a gente aprende na escola quando
a gente vê Báscara. E vamos dar uma extrapolada aqui e um pouquinho além do que você viu no ensino médio, mas eu prometo que você vai entender. No ensino superior, geralmente quando os alunos estudam conteúdo de cálculo dois, eles aprendem funções de múltiplas variáveis. São funções que agora não recebem um número e retornam outro, mas sim recebem dois números e retornam o outro. Nesse ponto, a gente não vai nem se interessar em analisar como é que é o funcionamento interno dessa função, mas imagine uma função onde você pode inserir a latitude e a longitude. Aí
você vai ter uma posição na Terra e ela vai te retornar qual que é a temperatura da Terra naquela posição. No ensino superior, quando a gente aprende sobre funções de múltiplas variáveis, nós vemos funções que recebem não só duas entradas, mas três ou até mais. Por exemplo, a gente poderia ter uma função que vai receber a latitude e a longitude. A gente vai ter uma posição no nosso planeta e também vai receber o ano, já que depender do ano, a temperatura também varia e a gente consegue obter a temperatura. Percebam que até então eu estou
usando o termo entrada e o termo saída e eu estou falando com vocês sobre um conteúdo da matemática. Vamos trazer isso aqui pro universo da programação. Agora uma habilidade que é essencial pr você, para que você trabalhe com funções, é a abstração. E o que é abstração? Abstração é a capacidade de fazer uso de algo ignorando, esquecendo o seu funcionamento. Então imagine que eu implementei uma função desse tipo, onde você informa a latitude, a longitude, o ano e você obtém média da temperatura naquele local. Será que é necessário que você saiba exatamente todas as operações,
como eu estou fazendo o meu Cálculo interno? ou se você souber qual que é a entrada, qual que é a sua saída, você já vai conseguir estar fazendo uso desta função. Muitas vezes, até mesmo na matemática, a gente consegue tá fazendo uso da abstração. Talvez você vai se deparar com um pedaço da resolução daquela função que você sabe resolver, mas que você gastaria muito tempo tendo que analisar todos os porros menores. Então você resolve ali usando uma calculadora e deixa esse funcionamento de lado. A programação é a mesma coisa, porém nós vamos usar termos diferente.
Agora, as nossas entradas serão parâmetros, parâmetros da nossa função e a nossa saída será o nosso retorno. E a gente já vem trabalhando essa nossa capacidade de abstração desde o começo do nosso curso. Afinal de contas, quando a gente faz uso da função print f, a gente aprende o que que a gente deve enviar para ela, o que que vai acontecer, que é uma impressão no nosso terminal, mas a gente não sabe o funcionamento interno. Se a gente fosse parar para analisar aqui o funcionamento interno da função print, eu poderia praticamente fazer um curso inteiro
que a gente ia ter que entender sobre sistemas operacionais, sobre requisição do sistema operacional, já que o nosso sistema operacional é o que detém o controle de tudo que está ligado nele. E não é necessário, a gente pode abstrair. Então, lembre-se sempre da maquininha que eu mostrei para vocês, da importância de abstração e dessa ideia de entrada e saída, que aqui nós vamos utilizar como parâmetro e retorno. Na declaração da nossa função, nós vamos ter as nossas entradas entre parênteses, Ou seja, os nossos parâmetros. Nesse caso, nós temos duas entradas e a nossa saída, a
gente precisa estar informando o tipo dela. Percebam então a importância da assinatura de uma função. Ela basicamente permite com que você faça o uso daquela função, mesmo que você não entenda profundamente o funcionamento dela. E analisando essa nossa assinatura, eu vou perceber que a gente tem uma função de nome imprime par. Eu já sei que o que essa função precisa de entrada é um número inteiro e eu sei que ela não vai me retornar nada. Então eu não preciso criar uma variável para poder receber o que essa função estaria me retornando. E com esse conhecimento,
eu já sou capaz de chamar essa minha função, de fazer uso dessa função dentro da minha função main. Agora bora aprender um pouquinho mais sobre essas entradas, estes parâmetros das nossas funções. Esses parâmetros eles são variáveis que armazenam os valores de entrada de uma função. Uma função, ela pode ter qualquer quantidade de parâmetros, inclusive ela pode ter nenhum parâmetro. Dá uma olhada nesse código que tá aparecendo aí pra gente. Nós temos a função soma, que recebe dois parâmetros. A gente precisa estar sempre colocando o tipo desse parâmetro, já que é uma variável que vai armazenar
esse valor que será recebido. E nós vamos fazer a separação desses parâmetros por vírgula. E percebam que mesmo tendo aqui dois parâmetros do mesmo tipo, a gente precisa estar declarando esse tipo. Uma função que não possui parâmetros, ela não recebe nenhum valor quando é chamado. E essas funções que não recebem nenhum valor, a gente simplesmente não passa nenhum parâmetro. Você poderia também fazer uso da palavra void, mas ela é opcional. Dá uma olhada nessa função imprime linha tracejada. Na nossa função main, a gente está chamando a função imprime linha tracejada. E a função imprim linha
tracejada está Imprimindo ali 80 traços no nosso terminal. É uma função que não tem nenhuma entrada, nenhum parâmetro e que também não tem nenhuma saída, nenhum retorno. Agora vamos aprender um pouco mais sobre o nosso retorno. O retorno ele acontece através da palavra reservada return. A palavra return, ela é obrigatória em funções cujo tipo não é void. Já em funções do tipo void, ou seja, que não tem nenhum retorno, caso você utilize a palavra return, você não deve colocar nenhum valor, nenhuma variável à sua direita. Por exemplo, a função main pode conter o comando return,
conforme a gente tá vendo aqui abaixo, mas só se a gente não colocar nenhum valor para ser retornado. Inclusive, é muito comum de você ver pessoas criando a função main com um retorno do tipo int. E aí elas colocam return zero. Essa prática aí é comum porque em alguns compiladores mais antigos você não conseguia tá criando a função void main, mas se a gente não criar a nossa função main para receber um retorno do tipo int, a gente não precisa estar utilizando return e o nosso programa para quando ele atinge a chave. Pessoal, percebam que
o tipo de retorno ele é totalmente independente dos parâmetros. Por exemplo, a gente tem aqui uma função que recebe um número inteiro e ela não retorna nada. A função imprime linha tracejada, ela recebe o número. Na função ME, a gente tá enviando o número 20 e a gente tá imprimindo, então, 20 traços. O nosso parâmetro, nada tem a ver com o nosso retorno. Mesmo que a gente esteja recebendo o inteiro, a gente não precisa estar retornando inteiro. E o comando return, ele pode aparecer mais de uma vez em uma função. Porém, quando a gente atingir
esse comando, a gente vai Executar ele e vai sair da nossa função retornando o valor. Observa essa nossa função primo. A gente recebe um número e a gente retorna ou zero ou um. Caso a gente retorne zero, ele não é primo. Caso a gente retorne um, ele é primo. A implementação dessa função é a seguinte. Nós vamos ter uma iteração. Essa nossa iteração vai acontecer até a metade do número enviado. Então vamos supor aí que eu mande o número 11. A gente vai pegar 11, vai dividir por 2, isso vai dar 5,5. a gente ignora
o meio, a gente trunca esse valor e a gente vai executar aqui até o cinco. Então essa iteração, esse nosso for, ele vai acontecer de dois até metade do número enviado. Isso acontece dessa maneira, porque para você verificar que um número é primo, basta você tentar dividir esse valor por todos os números a partir de dois. E se você chegar na metade e não conseguir fazer nenhuma divisão exata, você pode dizer que esse número é primo. E aqui a gente vai ter uma verificação. Se a gente conseguir fazer alguma divisão exata, a gente vai retornar
que esse número não é primo. Se a gente sair do nosso for, é porque a gente não conseguiu fazer nenhuma divisão exata, a gente retorna que ele não é primo. Vou explicar aquele algoritmo rapidinho com o desenho. Vamos supor que a gente entre com o número sete. Se a gente entrou com o número sete, a gente vai ter 7/ 2, que dá 3. 3 quer dizer que a gente vai fazer duas iterações, que a gente vai começar de Dois, vai fazer a iteração com o D valendo 2 e a iteração com D valendo 3. Beleza?
Então, como a gente vai ter duas, eu já coloquei aqui mais ou menos o que a gente vai ter nessas duas iterações. A gente vai pegar o sete, vai dividir por 2. 7/ 2 dá 3 e sobra resto 1. Como sobrou o resto, nós vamos fazer a verificação daquele if lá e não vamos dar o return. Depois a gente vai fazer o nosso incremento. O dois vai virar 3. 7 módulo de 3, a gente vai obter que 6 di por 3 é 3 e tem resto 1. Como o resto é 1, a gente vai verificar
lá que o nosso resto não é zero, não é uma divisão exata. a gente não vai retornar a nada. Daí a gente vai sair da nossa função e retornar verdadeiro, que sete é primo. Se eu tivesse o número 11 aqui, por exemplo, que também é o número primo, a gente iria estar fazendo 11/ 2, que dá 5,5, truncando, dá 5. Quatro execuções de 2 até 5, colocar aqui os valores 2, 3, 4 e 5. Tem resto 1. 11/ 3 dá 9. e a gente tem resto 2. 11/ 4 a gente vai ter resto 3. 11
div por 5 a gente vai ter resto 1. Então todas as vezes a gente vai estar fazendo a verificação se o resto da divisão é zero até a metade do valor. Como o resto da divisão não vai ser zero, a gente vai sair da nossa função e vai retornar que o número não é primo. Se eu tentasse fazer isso com o número 10, a gente teria a mesma quantidade de execução. Porém, logo na primeira divisão, a gente obter resto zero. e obtendo o resto zero, a gente já iria sair da nossa função e falar: "Este
número não é primo, porque para ser primo o número só pode ser divisível por um e por ele mesmo." Pois bem, compreendemos aí o funcionamento dessa nossa função e vimos que o comando return ele pode ser utilizado mais de uma vez na mesma função, mas assim que ele for atingido, a gente vai interromper a nossa função e vai devolver esse valor para quem chamou ela. E baseado em tudo que a gente aprendeu, vamos dar uma melhorada no nosso código. Agora a gente pode fazer com que a nossa função imprime menu inicial receba um valor e
retorne esse valor que será do tipo inteiro. Como a nossa função imprime menu inicial vai estar retornando um valor, nós vamos então criar uma variável para que ela possa estar recebendo esse valor. Vamos criar também uma função de soma que vai retornar um valor inteiro e não vai receber nada como parâmetro. Essa nossa função de soma vai chamar a função imprime cabeçalho. Vai imprimir um texto pedindo para que o usuário digite dois valores inteiros. E após receber esses valores, a gente vai estar realizando a impressão do resultado. Caso o usuário escolha a opção um, a
gente vai simplesmente chamar a nossa função soma. E nesse caso aqui, como nós fizemos o uso do int, nós teremos que fazer uso obrigatório do return, mas eu acho que vai ser mais interessante que a gente faça essa função do tipo void, porque senão a gente vai ter que retornar um valor e depois realizar a impressão dentro do nosso sutch. Aqui nós precisamos fazer uso do ponto e vírgula. Vamos agora fazer a função fatorial. Também vou criar ela do tipo void. Imprime fatorial. Nós não vamos receber nenhum valor. E o conteúdo dessa nossa função, nós
vamos chamar a função imprime cabeçar. Nós vamos imprimir uma mensagem insira um número. Vamos criar uma variável. Vamos armazenar este número nessa nossa variável. Depois vamos calcular o nosso fatorial e realizar a impressão dele. Agora basta que a gente chame a nossa função imprime fatorial. Eu quero manter esse nosso programa em execução. Então vou fazer uso do comando do while e vou verificar para manter esta execução enquanto a opção for diferente de zero. No comando do i a gente precisa fazer uso do ponto e vírgula. Nós estamos obtendo um erro, já que a nossa variável
faz parte das sentenças subordinadas ao comando do I. Então a gente precisa estar criando ela aqui fora. Intis bem observe agora como o nosso código ficou mais legível, inclusive facilitando para que a gente adicione novas operações nesse nosso programa. E bom, vamos dar uma olhada aqui se a gente precisa tá fazendo uso das nossas assinaturas para que o nosso programa funcione corretamente. A primeira função que a gente vai chamar imprime cabeçário está acima da função main, nós não vamos ter problema. A segunda função que nós vamos chamar é a imprime menu inicial. Da imprime menu
inicial a gente vai estar retornando inteiro. Opa, opa, opa. Nós temos um retorno inteiro, não fizemos uso da palavra return. Então o nosso programa aqui iria ter um problema de execução. Fiquem sempre atento ao cabeçalho das suas funções, mas nós não teríamos um problema ali. A gente ia chamar nossa função imprime menu inicial que também Está ali em cima, que chama função imprime cabeçalho. Depois chamaríamos a imprime soma, que também faz uso da imprime cabeçalho, que a gente criou o primeiro, né? E a imprime fatorial também faz uso da imprime cabeçalho, que também está ali
em cima. Então, a gente não teria nenhum problema de execução aqui, mas caso eu mudasse a ordem da minha função imprime e cabeçalho, aí sim nós teríos um erro. Mesmo sem essa obrigatoriedade, eu vou fazer uso do cabeçalho. Vou copiando as declarações da minha função e só não copio o conteúdo. E tá aí. Já consigo ver que o meu programa, além da função main, tem mais quatro funções. E bora executar ele. Agora eu posso digitar a opção um. Digit dois valores inteiro 8 e 9. Observem que o nosso programa tá funcionando corretamente, mas a gente
não tá conseguindo ver esse valor. Na verdade, não existe problema nenhum no nosso código, só que a execução dele é muito rápida. Então, quando nós chamamos a opção imprime fatorial, a gente calcula o nosso fatorial, realiza a impressão do nosso fatorial e finaliza a nossa função. Quando a gente finaliza a nossa função, a gente atinge um break no nosso sutchificação e depois já imprime o cabeçalho e imprime o menu inicial novamente. Então, a gente não tá conseguindo nem ver essa impressão. E para resolver esse problema, a gente vai fazer uso da nossa função slip. A
gente pode estar adicionando o nosso slip dentro do nosso comando sutch ou dentro da nossa função. Eu vou adicionar aqui dentro do nosso comando sutch antes do break. Slip dois. E aí o nosso terminal vai ficar paralisado por 2 segundos. Acho que quando a gente editar a opção zero não precisa, né? Pra gente fazer uso desse comando, pessoal, a gente precisa também estar incluindo uma nova biblioteca. E se a gente estiver no Linux, a gente vai fazer uso da função un stand. H e o nosso SDP vai funcionar em segundos. Unix Linux. Como a gente
está no Windows, a gente vai importar a biblioteca Windows. E aí a gente vai ter uma diferencinha, função slip, entrada em segundos, onde quando a gente importa na função Windows, Windows função sleep, entrada em milisegundos. E aqui no Windows a gente vai ter uma diferença, né, no nome da nossa função slip. para Windows vai ser com S maiúsculo, Windows e para Linux o S minúsculo. E a gente tem uma diferença e também essa entrada aqui vai ser em milissegundos. Agora sim, vamos executar o nosso código. Fatorial 8. Aguardou 2 segundos. Voltamos pro nosso menu inicial,
opção zero e finalizamos o nosso programa. OK? Então, agora a gente já viu aí a sintaxe de uma função, já vimos a assinatura, já entendemos o conceito por trás, de onde vem esse nome. Conversamos sobre parâmetros, parâmetros múltiplos, funções sem parâmetros, falamos sobre return. E uma coisa que eu não deixei tão clara, mas quero deixar explícito a partir de agora, as nossas funções em C só podem Retornar uma única variável. a gente não pode retornar mais de um conteúdo. Então, a gente sempre vai tá retornando ou um valor ou um caracter ou um endereço, mas
sempre somente uma única coisa será retornada. E depois da gente ter conversado sobre o nosso return, a gente ainda foi lá e escrever um pouquinho de código para consolidar tudo que a gente viu. E depois de tudo isso, chegou a hora da gente entender realmente o funcionamento de uma função. Vamos dar uma olhada nesse nosso programa que começa pela função main. A gente está criando uma variável N. Essa nossa variável então vai pro nosso endereço de memória RAM. O nosso usuário vai digitar um valor. A gente vai atribuir esse valor a essa nossa variável. Depois
nós vamos estar criando a nossa variável f e chamando a nossa função fatorial n. Observe que ela tá destacada aqui embaixo dentro da nossa função main. Quando a gente chamar a nossa função fatorial, nós vamos estar passando o parâmetro, a entrada. A partir desse momento, quando a gente chamar essa nossa função e nós começarmos a sua execução, a nossa função fatorial está recebendo a entrada, o parâmetro int y. E o que vai acontecer é que nós vamos primeiro criar essa variável, esse espaço Y. A gente vai receber esse valor que é o valor COC. Depois
nós vamos criar a nossa variável FAT e a gente vai ali trabalhando em cima dessa nossa variável fazer o fatorial. Percebam que Y apenas recebeu uma cópia do valor de N, mas apesar delas terem o mesmo valor, elas São variáveis independentes. Ou seja, se eu alterar a minha variável Y, a minha variável N vai continuar sendo a mesma. E aí, depois da gente calcular o nosso fatorial, nós vamos retornar à nossa variável fatorial. Quando a gente encontra o comando return, nós vamos apagar tudo que estava dentro da nossa memória. Então, essa nossa variável FAT, a
nossa variável Y, elas vão deixar de existir na nossa memória. Esse espaço vai ser liberado. E observem que na nossa função main, o espaço que a gente havia criado para receber o valor que a nossa função vai retornar recebe então o valor 120. Percebam que esse comportamento ele faz todo sentido e ele é extremamente necessário. Imagine que a gente chamasse a nossa função fatorial e aí a nossa variável ficasse lá com o valor 120. Aí depois a gente chamasse essa função novamente e aí a gente calculasse o novo fatorial. Só que o conteúdo dessa nossa
variável já seria 120. Então a gente tem um problema, um outro motivo, a gente não quer que essas variáveis fiquem armazenadas, sendo que a nossa função não vai estar sendo executada e talvez ela nunca mais seja chamada, ela seja chamada somente uma vez. Portanto, a gente vai observar esse comportamento de escopo e de tempo de vida nas nossas variáveis quando a gente tiver trabalhando com funções. E se você se lembrar bem, lá no comecinho do nosso curso, eu disse que toda variável possui essas seis propriedades e eu disse que a gente iria entender melhor o
escopo e o Tempo de vida no decorrer do nosso curso. Nós acabamos utilizando essa ideia antes de funções, mas agora ela vai ser fundamental e é preciso que ela esteja clara na cabeça de vocês. Quer ver só? Como a gente já fazia uso desses conceitos? Quando a gente utilizava o comando wi ou comando for, a gente não conseguia acessar a nossa variável se a gente estivesse fora da nossa iteração, porque o tempo de vida dessas nossas variáveis era limitado ao tempo de iteração. Então, quando a gente saía do nosso for, saía do nosso while, essa
nossa variável deixava de existir. E é exatamente isso que permite com que a gente repita o nome das nossas variáveis. Então, nós já escrevemos código onde a gente tinha vários forces, todos eles fazendo uso da variável de nome i. Por quê? Porque essa variável ela só era acessível dentro do nosso for e ela também deixava de existir quando a gente saía desse nosso for. Então, mais uma vez, só para garantir que você compreendeu, olha o nosso código, a nossa função main. Nós estamos criando a variável N. A gente tá armazenando um valor digitado pelo usuário.
Vamos imaginar que ele digitou cinco. A gente armazena esse valor cinco nesse nosso espaço de memória que a gente acessa através do nome N. Nós vamos chamar a nossa função fatorial e vamos enviar pra nossa função fatorial o conteúdo da nossa variável n. Agora observando na nossa função fatorial, a nossa função fatorial vai ter como entrada um inteiro e vai armazenar isso na variável int y, mas eu poderia também estar chamando de int n. Se eu tivesse chamando de int n, A gente ia criar uma outra variável de nome n. E aí você pode pensar:
"Poxa, mas como é que eu consigo ter duas variáveis com o mesmo nome?" É preciso lembrar do conceito de escopo. A gente não consegue acessar variáveis da função main estando na função fatorial. Então, a gente não poderia ter duas variáveis de nome n dentro da mesma função, mas em funções diferentes não tem problema. É como se em cada função existisse somente um espaço da memória do qual ela pode acessar. Então, por esse motivo, a gente poderia ter duas variáveis de nome N, mas elas só seriam acessadas em partes diferentes do nosso código. Aqui a gente
chamou de Y para facilitar. Então, a gente vai estar criando a variável Y e a gente vai estar recebendo o valor, foi o parâmetro enviado pra nossa função, que é cinco. A gente armazena esse valor CCO nessa nossa variável. Depois a gente calcula o nosso fatorial. Quando a gente atingir o retorno, nós vamos voltar para onde a nossa função foi chamada, retornando esse valor, que é o valor 120, e todo o conteúdo da nossa função vai ser apagado. Então, se a nossa função tá retornando um valor, é por esse motivo que a gente precisa criar
um espaço na memória para est recebendo esse valor, porque quando a gente sair dessa nossa função, ela vai ser apagada completamente. Bom, essa aí é a explicação do funcionamento de uma função. Na prática, pensem sempre que quando a gente está chamando uma função aí no nosso programa, a gente não tá realmente enviando as nossas variáveis. A gente tá pegando o conteúdo da variável real e copiando para uma nova variável que essa função vai fazer uso, que possui esse conteúdo que foi copiado Das variáveis que a gente passou e ela retorna um valor. Só que assim
que ela retorna, isso tudo aqui deixa de existir, né? A gente não vai ter mais a existência nem dessa função, nem dessas variáveis e nem mesmo dessa outra. Então, a gente precisa ter um lugar para poder pegar esse conteúdo que a nossa função retorna. Vamos dar uma olhada agora em funções usando tipos definidos por nós. Aqui, pessoal, o comportamento vai ser o mesmo. Tipos que a gente cria e tipos primitivos, eles vão funcionar exatamente como a gente espera. Então, observe, aqui a gente tem a declaração desse nosso tipo, que é o tipo ponto, né? Ele
possui duas coordenadas, X e Y. Nós temos a nossa função soma pontos. Quando a gente se depara com uma função, vamos sempre dar uma olhada na sua entrada e saída. A gente vai ter como entrada o ponto A e o ponto B. E a gente vai ter como saída um outro ponto. Vamos ver o que essa nossa função está fazendo. Ela cria um outro espaço de memória além desses dois para receber esse valor na hora que a gente entra aqui chamado ponto S. Ela então soma as coordenadas desses dois pontos, né? Coordenada X e armazena
nesse ponto que ela acabou de criar, que é o ponto S. soma as coordenadas Y dos outros dois pontos, ponto A e ponto B, e armazena na coordenada Y do nosso ponto S. E depois a gente retorna o ponto S. Observem que S é do tipo ponto, então tá bem alinhado aqui o nosso retorno com o tipo que a gente declarou que seria retornado. Na nossa função main, nós temos a criação Do ponto P1, do ponto P2 e a criação da variável ponto P3 para receber o que a nossa função vai retornar. E aí nós
temos a impressão das coordenadas da soma desses dois pontos. Pois bem, agora a gente já pegou bem como é que funciona uma função. Vimos aí os parâmetros, os retornos, fazendo uso de tipos criados pelo programador, mas a gente ainda não viu como a gente passa um arrei como parâmetro e como a gente recebe um array. E nós vamos ver isso agora, pessoal. pra gente tá passando uma string vai ser bem tranquilo. Imagina que a gente tem aqui uma função. Essa função a gente pode estar usando o stren que vai calcular o tamanho, ou a gente
pode implementar a nossa própria função que calcula o tamanho de um string. Então a gente vai receber essa string e a gente vai retornar um inteiro, que é o tamanho dessa nossa string. Percebam que a gente não precisa nem est declarando o tamanho dessa nossa string. Então a gente tera aí sobre todos os elementos da nossa string, começando pelo 0 1 2 3, até a gente encontrar o nosso caracter que serve para indicar que chegamos ao final da nossa string. Depois a gente retorna o nosso tamanho. Aqui nós temos na função main a criação da
string Mateus Viana. E lá no print f a gente está chamando a nossa função tamanho. Lembrando que o print f já é um lugar para receber esse retorno, né? esse retorno inteiro. Cálculo do tamanho vai ser feito, passando por cada um dos elementos. 1 2 3 4 5 6 7 8 9 10 11 12 13 E o elemento 15 vai ser o nosso símbolo que indica o final da string. Então a gente vai ter aí que o tamanho da nossa string vai ser 14. Agora, se a gente não estiver tratando de strings, nós não vamos
ter aquele elemento que vai indicar o final da nossa string. Então, por esse motivo, a gente vai precisar mandar o nosso arrei e mandar também o tamanho do nosso arrei. Pensa comigo, eu recebi um arrei. Eu vou iterar sobre ele. Nós vimos em arreis que a gente pode tentar acessar posições que não foram armazenadas. Então você pode ter uma rei de tamanho quatro e terar sobre ele até a posição 100, já que você não vai saber quando parar. Na string a gente tem um caracter que indica que a gente deve parar. Os arreis não. A
reis do tipo float, do tipo int ou até mesmo de tipos criados por nós. Por isso, nós vamos ter dois parâmetros. Vamos dar uma olhada nesse nosso código aqui, onde nós estamos chamando a função soma elementos. A gente está enviando um arrei que contém cinco números e a gente tá enviando aí o tamanho desse nosso arrei. Aqui no nosso código a gente tá iterando aí sobre esse nosso arreio, começando pelo tamanho que é cinco. Vamos realizar o decremento. Vai 5 4 3 2 1 e 0 até chegar no -1. e vamos realizar a soma de
todo esse conteúdo acessado nesse nosso arrei dentro dessa variável soma. Então nós vamos retornar o conteúdo da variável soma e encerrando a nossa função, tudo que existia dentro dela vai ser apagado. Bem, para manter os nossos arquivos organizados, já trouxe todos eles para cá. Aplicação de funções, assinatura, um outro arquivo mostrando uso de assinaturas. Return, return, tipos criados, strings como parâmetro, array como parâmetro. E agora, pessoal, nós vamos dar uma olhada em arreis multidimensionais como parâmetro. E se a gente quiser tá passando um arreio multidimensional como um parâmetro, como por exemplo esse arrei ns, que
tem três colunas e três linhas, aqui está o conteúdo dentro do nosso arrei. Essa daqui é a primeira linha, essa daqui é a segunda linha e essa daqui é a terceira linha. Percebam que na nossa função eu declarei que a gente vai estar recebendo um arrei do tipo inteiro de duas dimensões. Mas se a gente tentar executar esse código, nós vamos obter um erro, porque ao passar a raiz de mais de uma dimensão, nós precisamos informar o tamanho de todas as dimensões, exceto a primeira. Dá só uma olhada. É exatamente isso que o nosso compilador
está nos dizendo. Na declaração de arreis multidimensionais, nós precisamos informar todas as dimensões, exceto a primeira. Então, vou informar todas as dimensões, exceto a primeira, e o nosso código vai executar sem nenhum problema. Aqui não realizamos a impressão porque estamos usando o símbolo de porcentagem errado. Agora sim, soma 45. Percebam que em arrez multidimensionais, como a gente vai tá informando todos os tamanhos, exceto primeiro, é interessante que a gente receba um outro parâmetro nos informando o número de linhas desse arrei. E aí eu posso fazer uso desse tamanho para determinar o que eu vou estar
percorrendo. Vou deixar comentado Essa necessidade de informar o tamanho de cada dimensão a partir da segunda e vamos salvar esse nosso arquivo. Pessoal, se a gente tivesse aqui um arrei de três dimensões, teríamos o mesmo comportamento. A gente informaria o tamanho de todas as dimensões, exceto a primeira, e a gente ia estar enviando também o tamanho para que a gente pudesse estar percorrendo aí esse nosso arreio. Então, teríamos três for. O primeiro a gente aí terá sobre ele de acordo com o tamanho. O segundo a gente saberia simplesmente analisando aqui o nosso arreio. E se
a gente quiser estar retornando um arreio, por exemplo, eu teria ali uma função obter arrei. Essa nossa função iria criar um array ou mesmo receber um array. Lembrando que quando a gente recebe, a gente simplesmente copia esse conteúdo para uma variável interna dessa nossa função. Queremos modificar esse nosso arreio. Aqui eu vou simplesmente atribuir valores de zero a 5 e retornar esse nosso arreio. Como que a gente poderia estar fazendo isso? A gente sabe que todo o conteúdo, todas as variáveis que a gente criar aqui dentro, elas vão desaparecer. E nós sabemos que a gente
só pode estar retornando uma única coisa. E aqui eu estou dizendo que eu estou retornando o inteiro. Será que eu poderia simplesmente fazer uso dos colchetes e entender que agora eu estaria retornando um arrei, criar um arrei e chamar essa minha função obter arrei? Bom, já falei para vocês que isso não é possível, porque a gente só pode criar um arrei sem informar o tamanho dele quando o nosso compilador já sabe qual vai ser o tamanho. Dá uma olhada no nosso arquivo a raiz e como inicializar. Nós só conseguimos fazer uso da maneira dois caso
a gente já tenha os nossos elementos. E se a gente lembrar de como funciona a nossa função, a gente primeiro está reservando esse espaço na nossa memória e depois tentando receber um conteúdo dentro deste nosso arrei, o que não é possível. De novo, uma vez que a gente tenha criado, ou seja, reservado o nosso espaço na nossa memória de um arrei, um conjunto de variáveis, nós só vamos poder estar atribuindo valor a esse nosso arreio, indo de posição em posição. E isso é o que a gente tá vendo nessa nossa maneira um e na nossa
maneira três, onde a gente tá simplesmente usando um iterador para fazer isso daqui. Mas neste nosso código, primeiro a gente reserva um espaço. Então, quando a gente cria lá int e chama a função obtém inteiro, primeiro a gente reserva o espaço na memória com o endereço, com o nome I. O tamanho desse espaço vai depender da nossa variável. Depois a gente chama a nossa função. Chama a função. A nossa função vai receber os seus parâmetros, vai criar suas próprias variáveis, vai fazer o que tiver que ser feito e ela vai retornar um valor. Aí a
gente vai armazenar esse valor nessa nossa variável aí. Mas como a gente tá criando primeiro, a gente não pode fazer algo desse tipo. Mas e se existisse uma maneira de você criar uma variável dentro da sua função e essa variável não desaparecer? Essa maneira existe, pessoal. E essa maneira é fazendo uso da palavra reservada static. Quando a gente cria uma variável utilizando esse nome, poderia aqui não Ser um arre inclusive contador de vezes que a função foi chamada. Mas nesse caso aqui a gente vai estar criando um arrei. Aí nós vamos conseguir estar retornando. Porém
nós não vamos fazer uso desses colchetes e sim do asterisco. E a gente aqui também vai estar utilizando o asterisco. Nós vamos ver em detalhe o que que isso aqui quer dizer quando nós formos estudar ponteiros. Mas por enquanto basta você entender que se você quiser retornar um arrei, você vai ter que estar fazendo uso dos asteriscos. Depois quando a gente aprender sobre ponteiro, isso aqui vai fazer muito mais sentido, mas só para que você tenha uma visão geral do que que tá acontecendo, né? Quando a gente está na nossa função main e a gente
cria esse nosso arrei asterisco, a gente tá criando aqui um espaço de memória onde a gente consegue salvar um endereço. Quando a gente chama a nossa função obter arrei e aí ela cria esse nosso arrei usando essa palavra estática, um arrei estático, ela vai criar aqui, eu botei de duas posições, né, só para facilitar, mas vamos supor aí que seria cinco, né, igual está no nosso código. A gente vai reservar esse nosso arrei com cinco espaços e vamos chamar ele de V. Quando a gente retorna esse nosso arrei, todo o conteúdo da nossa função deixa
de existir, tá? ele deixa de existir, mas esse arrei que a gente criou continua na nossa memória. E aí a gente salva como se fosse um acesso desse nosso arrei na nossa função main. O que que isso quer dizer? que se eu na minha função main começar a alterar o meu arrei, vou mudar aqui os valores das minhas posições e Coisa e tal, e eu chamar a função obter de novo em um outro momento. A gente coloca aqui então int arrei 2. Esse asterisco aqui tava na posição errada, tá? Intrei. Nesse momento, a nossa função,
ela vai estar acessando um arrei já existente. Então, ela não vai fazer um novo espaço de memória. Nós vamos ainda ter o mesmo arrei aqui dentro. E quando a gente sair, nós vamos retornar pro arrei do este endereço que a gente já havia criado. Então, o conteúdo do nosso arrei e do nosso arrei do vai ser o mesmo. Não se preocupe, a gente vai entender isso melhor. Só gostaria que você percebesse que só existe um arrei. Vou mostrar isso para vocês no código. Criei um arrei um, criei o arrei dois. Vou chamar a minha função
obter arrei. E se eu estiver correto, o conteúdo vai ser o mesmo. Tanto no arrei um quanto no arrei dois, nós vamos estar usando o mesmo espaço de memória. Então vou alterar aqui o conteúdo do arrei 2 e vou imprimir o conteúdo do arrei 1. Percebam que eu alterei os valores do meu arrei 2 e imprimi o conteúdo do meu arrei 1. Só que ao alterar o conteúdo do meu arrei 2, o conteúdo do meu arrei 1 também foi alterado. Exatamente. Porque nós estamos criando esse nosso arrei de maneira estática, ou seja, ele não vai
sumir da nossa memória enquanto a gente não terminar a execução do nosso programa e simplesmente armazenando aí acessos quando a gente chama essa nossa função. Uma outra maneira que a gente tem de est chamando uma função que vai modificar um arreio é fazendo uso de variáveis globais. Deixa eu mostrar para vocês aqui. Se a gente tivesse fazendo Uso de uma variável global, nós poderíamos deixar aqui void e chamar a nossa função, por exemplo, altera arrei. A nossa função altera arrei iria pegar e atribuir ao nosso arrei em cada uma de suas posições o valor 10.
Percebam que eu não estou enviando nenhum parâmetro, não estou criando e a nossa função altera rei tá simplesmente acessando esse arrei. Isso é possível se a gente criar uma variável global ou no caso um arrei global. E como que a gente faz isso? Em vez da gente criar a nossa variável dentro de uma função, a gente cria fora de qualquer uma dessas funções. Então vamos criar aqui o arrei V de tamanho 5. Vamos alterar o conteúdo desse nosso arrei na nossa função main. Vamos imprimir esse nosso arrei. E percebam que eu estou fazendo o acesso
desse nosso arreio da função main, porque ele é um arrei global. Vamos fazer a chamada da função altera array. E agora a gente não precisa retornar nenhum valor porque a gente já consegue acessar esse nosso arreio por ele ser um arrei global. Então bora fazer essa execução. Vou só adicionar uma quebra de linha para ficar melhor da gente visualizar. E percebam que agora tanto a nossa função main quanto a nossa função altera reay estão acessando o mesmo espaço na memória. Se a gente for analisar o que tá acontecendo, nós estamos criando esse nosso arrei e
deixando ele acessível através de qualquer lugar. Então a gente não precisa mais ter esses ponteiros, esses acessos que a gente estava fazendo uso com asterisco e o funcionamento vai ser bem semelhante e vai ser até mais fácil de compreender como a gente ainda não viu o conteúdo de ponteiros. O conteúdo desse nosso código oito, você já tem total condição de compreender ele. Eu deixei para falar sobre variáveis globais só depois que a gente vi funções, porque como a gente tava Fazendo tudo na nossa função main, não fazia muito sentido a gente se preocupar com uso
de variáveis globais. Agora que nós temos várias funções, aí sim faz sentido a gente fazer uso desse espaço vazio, tá? chamando aqui de espaço vazio, tudo que está fora de qualquer uma das nossas funções para criar essas variáveis que vão ser acessíveis de qualquer lugar do nosso programa, ou seja, de qualquer função. As desvantagens e limitações do que a gente viu, que a gente vai conseguir corrigir mais pra frente, é que quando eu vou criar uma variável global, que vai ser acessada por esses espaços em diferentes lugares, eu preciso informar o tamanho dessa minha variável.
Percebo, eu não consigo falar pro usuário: "Ah, me fala aí quantos inteiros você quer salvar, quantos alunos tem na sua turma, aí depois que ele me falar esse valor lá na função M, eu pego e altero aqui para, sei lá, 35 alunos ou 40 alunos." é uma desvantagem, uma limitação grande desses métodos que a gente viu agora, tanto da variável global quanto esse retorno de arrei dessa maneira é que a gente precisa saber o tamanho em tempo de compilação. Saber o tamanho em tempo de compilação quer dizer que a gente não pode perguntar pro usuário
enquanto o nosso programaver executando qual o tamanho do nosso arrei. Nós precisamos saber exatamente a quantidade de memória que vai ser reservada antes mesmo de gerar o nosso arquivo executável. E como agora nós temos várias coisas para serem escritas fora da nossa função main, vamos dar uma olhada numa estrutura de organização do nosso código. Nós vamos ter sempre as nossas diretivas, os nossos includes, os nossos comandos de define. Depois teremos os tipos definidos pelo programador e em seguida as variáveis globais. Observe que essa ordem faz todo sentido, já que você pode ter uma variável global
de um tipo definido, mas para que isso aconteça, é Necessário que esse tipo seja definido anteriormente. Em seguida, nós teremos as nossas assinaturas de funções que podem fazer uso dos nossos tipos definidos e as nossas funções. Aproveita esse momento para estar fazendo a nossa lista de exercício, praticando tudo que você acabou de aprender sobre funções, que em seguida nós vamos aprender sobre funções recursivas. Muito bem, pessoal. Bom, então agora a gente vai começar o nosso estudo de funções iterativas. Dá uma olhada aqui no comportamento de uma função normal, uma função aí que a gente já
viu, que a gente já aprendeu. A gente tem o nosso código principal. No nosso código principal, a gente realiza a chamada dessa nossa função. E aí essa nossa função nos retorna um resultado ali no lugar de onde ela foi chamada. E isso daí é o que a gente aprendeu até então. Porém, a gente viu que dentro de uma função, ela pode chamar uma outra função. Por exemplo, quando a gente tem a função gerar impressão, ela pode estar chamando ali a função print f. Nesse caso não vai ter nenhum retorno por ser uma função do tipo
void, né? Ela, o print f não retorna nada, ele simplesmente imprime uma mensagem no nosso terminal. Mas eu poderia também estar chamando uma outra função que vai retornar algo. E essa outra função poderia estar chamando uma outra função que vai retornar algo. E a esse comportamento a gente não dá nome nenhum, tá? Porque simplesmente aí são funções que estão chamando funções. Isso daí não é função recursiva. Porém, imagine que uma função ela chamasse a si mesma. Essa função não vai chamar uma outra função qualquer, ela vai chamar a Si própria. E aí nós teríos algo
mais ou menos desse tipo aqui. A gente vai ter o nosso código principal que vai chamar a nossa função. Só que a nossa função ela se chama a si mesma. Se vocês lembrarem, toda vez que a gente passa um parâmetro, né, a gente passa aqui uma informação pra nossa função, ela cria um espaço na memória e armazena o conteúdo desse parâmetro que a gente passou. Então, toda função ela vai ter aquele seu conjunto de variável. Então, na hora que ela chama a si mesma, ela vai criar um outro espaço na memória para armazenar tudo. E
se essa função tá chamando a si mesma, ela vai chamar a si mesma de novo. E vai chamar a si mesma de novo. E ela poderia continuar fazendo isso aí infinitamente. E aí o nosso programa simplesmente nunca iria retornar nada pro nosso código principal e a gente ia ficar aí nesse loop infinito dessa função, chamando a si mesma. Mas vamos imaginar aí que vai haver algum momento, alguma coisa vai acontecer, que essa nossa função, ao invés dela chamarse mesmo, ela vai falar assim: "Poxa, eu não quero mais fazer isso, eu vou retornar". E aí ela
retorna um valor. Lembre-se aqui no nosso código principal, quando a gente chama uma função, ela retorna o valor para onde ela foi chamada. Então, imagine que essa nossa função, por algum motivo, decidiu retornar um valor, ela vai retornar pra sua chamada. Assim que a gente retorna o nosso valor, isso vai sumir da nossa memória. A gente pode esquecer ali o conteúdo daquela nossa função, certo? Então, some. E aí essa nossa outra função, ela vai perceber, poxa, já que me retornaram esse valor, eu também vou retornar. ela vai retornar um outro valor e ela vai sumir
da nossa memória. E se o comportamento dessa função foi esse, o comportamento dessa daqui vai ser o mesmo, já que ela são a mesma função. Assim que ela recebe um retorno, ela também vai retornar um valor. E aí a gente vai então retornar um valor pro nosso código principal. Eu sei que nesse momento você pode estar confuso, né, se perguntando, mas o que que fez a nossa função ficar parando de chamar a si mesma e retornar o valor? Como é que eu posso implementar isso? E a gente vai ver tudo isso em detalhes, pessoal. Reparem
nessa nossa função e vamos aprender já a identificar se ela é uma função recursiva ou não. A gente tem aqui na nossa assinatura, né, no nosso cabeçalho, a declaração da nossa função, que ela é uma função que recebe o número inteiro como parâmetro, que retorna o número inteiro e que tem um nome fatorial. Para eu identificar se ela é uma função recursiva, em alguma parte que está entre crochetes, ela vai ter que est chamando a si mesmo. Então, a gente vai ter que ter a chamada pelo nome fatorial. E a gente vai ter que respeitar
essa regra dessa função que é enviar um parâmetro. E a gente tá tendo essa chamada aqui dentro. Esta função aqui int fatorial é uma função recursiva. Por quê? Porque ela está chamando a si própria. Agora vamos ver o código que está sendo executado dentro dessa nossa função. Nós estamos fazendo uma verificação. Se o número que essa função recebeu for menor do que um. Ou seja, se ele for zero, por exemplo, a gente vai simplesmente retornar o número um. Então aqui a gente tem a chamada dessa nossa função. Se eu estivesse enviando o zero como parâmetro,
a gente ia ter aqui uma chamada de uma função. A Gente ia simplesmente verificar que o zero é menor do que 1 e a gente retornaria o valor 1. Então, se eu chamasse aqui fatorial de zero, é o nome da nossa função e o parâmetro, eu iria entrar no meu if e eu iria retornar o valor 1. Porém, o que está acontecendo aqui é que a gente está passando, né, como parâmetro valor 4. Na nossa chamada de função, a gente cria um espaço para receber o retorno dessa nossa função e tá passando aí o valor
4. Como nós estamos passando o valor 4, a gente vai ter aqui o parâmetro n valendo 4. Não vai ser menor do que 1, então isso daqui vai dar falso. A gente não vai entrar no conteúdo desse if porque deu falso. E a gente vai retornar então 4 vezes uma nova chamada dessa função que é fatorial de 3 4 - 1. Percebam que aqui depois do nosso return a gente tem uma operação. Quando a gente criou as nossas funções, houveram momentos onde nós colocamos operações para serem realizadas depois do nosso return. Por quê? Porque existe
uma precedência. Primeiro a gente vai resolver tudo isso daqui e depois nós vamos retornar. Então aqui tem 4 vezes qual é esse valor? Não sei. Então preciso chamar essa função fatorial de três. E aí na hora que ela me retornar o valor eu resolvo quatro vezes o conteúdo que foi retornado. Então dá uma olhada que a gente vai ter essa nossa função, não vai entrar no nosso if. ela vai retornar quatro vezes o resultado da chamada do fatorial de 3. Se a gente chamou fatorial de 3, nós vamos criar um novo espaço na nossa memória
para receber esse parâmetro três, né? E vamos fazer a execução dessa nossa nova função. 3 não é menor do que um, então a gente não vai entrar nesse nosso if. E aí a gente vai retornar 3 vezes o fatorial de 2. A gente precisa primeiro resolver aí o que que é o fatorial de 2 para poder fazer essa multiplicação. Só que o fatorial de dois é uma nova chamada de função. Então a gente vai chamar essa nossa nova função criando um novo espaço na memória para poder estar armazenando o conteúdo do parâmetro que a gente
tá enviando. OK? Então bora resolver aí esse fatorial de 2 pra gente poder retornar um valor e depois fazer essa multiplicação. Nós vamos ter aqui que 2 não é menor do que um, então a gente não vai entrar no conteúdo do nosso if. E agora a gente vai ter que fazer ali antes de retornar duas vezes o fatorial de 1. O fatorial de 1 precisa ser resolvido pra gente fazer essa multiplicação e então retornar o valor. E aí a gente vai chamar essa nossa função passando um como parâmetro. E um também não é menor do
que um. Então a gente não vai tá entrando nesse nosso if. E paraa gente estar retornando o valor, a gente vai ter que resolver a multiplicação 1 x fatorial de zero. O fatorial de zero eu não sei, então vou chamar essa função. E aí, pessoal, nós vamos finalmente entrar no nosso if. Z0 é menor do que 1. Então nós vamos retornar o valor 1. Retornando o valor 1, que vai assumir esse lugar aqui do fatorial de zero, nós podemos deletar todo o conteúdo dessa nossa Função. OK? Deletei. E agora que eu tenho um, eu posso
retornar o resultado de tudo isso, que é 1 x 1, vai ser 1. Então vou retornar isso daqui para quem me chamou, para quem chamou essa função. Retornando esse valor, eu posso sumir, esquecer, deixar de lado todo o conteúdo dessa minha função. Não, eu já terminei a execução dela. E aí eu vou poder resolver essa minha multiplicação que também estava pendente 2 vezes o resultado que essa função aqui iria me retornar, que no caso é 1. 2 x 1 dá 2. Então agora que eu já resolvi essa operação, eu posso retornar o resultado dela, que
é 2. Retornando o 2, eu vou poder resolver essa minha outra multiplicação, 3 x 2, que é 6. E eu vou poder retornar o meu valor, claro, sumir com tudo isso aqui da minha memória, já que eu estou finalizando aí a execução dessa minha função aqui. Retornando o valor 6, eu vou fazer a minha última multiplicação aqui, que é 4 x 6, que dá 24. E aí eu posso retornar o valor 24 pro meu código principal, né, que foi a primeira vez que a gente chamou essa nossa função fatorial. E nós vamos armazenar isso no
resultado e deixar todo o conteúdo dessa nossa função de lado. E pronto, nós encontramos o fatorial de 4. Agora vou fazer uma afirmação aqui para vocês. Todas as funções recursivas elas podem ser implementadas utilizando uma interação, ou seja, um while, um do while ou um for. Poxa, por que que você não me disse Isso antes, né? Eu poderia já ter pulado o vídeo e simplesmente fazer uso da iteração. Nesse momento, alguns de vocês devem estar pensando: "Ah, já sei, eu vi que foram só duas linhas de código". É porque, provavelmente, caso eu utilize uma função
recursiva, o meu código vai ficar mais rápido, ele vai ficar melhor, né? Ele vai ficar mais otimizado. Então, é mais interessante que a gente utilize uma função recursiva do que uma iteração. Isso é verdade? Não, não é verdade. Inclusive, é o contrário, tá, pessoal? É o contrário disso. Vou mostrar aqui para vocês que nessa nossa função interativa, a gente tem o mesmo código que a gente viu antes lá, tá aqui, tá, pessoal? Todas as vezes que a gente chama a nossa função, a gente precisa criar um novo espaço na memória para poder estar armazenando esse
parâmetro que tá sendo enviado. E nessa nossa função fatorial iterativa, não precisa bater o olho aqui e tentar entender tudo em detalhes, não, mas só percebam o seguinte, a gente cria um espaço, tá? Que é o espaço F. É uma variável que está acessível aí de fora do for, né? Porque a gente criou ela aqui fora, mas é só um espaço na nossa memória. E esse espaço na memória, ele vai sendo atualizado a cada iteração. Então a gente não tá criando um novo espaço a cada iteração. A gente tá só atualizando um que a gente
já havia criado. Então apenas com esse espaço que a gente criou, que é o f, claro que vão ter outro, né, da variável i, que vai existir só dentro do for, mas ainda assim é só um espaço a mais e um espaço da chamada da função. Então vai ter três aí, mas o mais importante aqui é esse espaço f. a gente vai ir atualizando esse valor nessa nossa variável. Quando A gente faz uso de uma função recursiva, se a gente tiver passando aqui, né, o valor 10, por exemplo, aqui a gente vai ter três espaços
de memória que vão ser criados. Se a gente vai passando o valor 10, nós vamos ter 10 chamadas de função, OK? E aí para cada chamada a gente vai est criando aí um novo espaço. E se lá dentro dessa nossa função tiverem mais variáveis, a gente vai est criando aí mais espaço ainda. Então soluções que fazem uso de funções recursivas, elas ocupam mais espaço de memória e elas ainda são mais lentas. Por quê? Porque elas vão ter que ficar armazenando, criando esses espaços, tá? fazendo essas chamadas de função, então sim, a solução iterativa, ela vai
ter um desempenho maior e vai ocupar menos memória. Então, alguns de vocês com certeza vai estar pensando: "Ah, então vou dar preferência sempre em fazer uso da iteração e não da recursão, porque não tem nenhum motivo, né? A gente tá vendo aí que a recursão ocupa o maior espaço na memória, ela é menos eficiente, ela também pode resultar em um loop infinito, assim como a nossa interação. E as vantagens que aparecem e envolvem, né, a gente tá escrevendo menos código, ser mais fácil de implementar, só que esse fácil quando você se depara com esse conteúdo
pela primeira vez, é uma ideia que você não compra, você fala: "Poxa, eu já sei fatoriar o iterativo, eu já sei utilizar a iteração." Não, eu não acho que seja mais fácil, não. Achei um pouco mais complexo, mas acreditem em mim, aprender funções recursivas vai facilitarem muito a sua vida como um programador. E isso vai acontecer por futuramente vocês vão Aprender conteúdos aí como árvores, gráfos, que são estruturas de dados. Vocês vão aprender também algoritmos de buscas e ordenação. Vocês vão aprender dinâmica, né? um tipo de programação aí que resolve os problemas, dividindo eles em
subproblemas menores e entender a recursão vai facilitar a sua compreensão de todos esses algoritmos. Portanto, o que eu estou dizendo para vocês que se vocês aprenderem recursão, entenderem um algoritmo recursivo e depois transformarem ele em uma iteração, vocês vão ter que se esforçar menos no aprendizado de vocês. A sua experiência vai ser mais fácil, tá? Você vai poder aí relaxar enquanto os outros estão quebrando a cabeça do que se você simplesmente tentasse entender esses algoritmos pela primeira vez na sua vida. Já utilizando a iteração, à medida que os nossos códigos forem ficando mais complexos, a
diferença de número de linhas que você vai ter que escrever, fazendo uso da iteração e fazendo uso da recursão, vai ser gigantesca. E as explicações desses algoritmos que você vai encontrar, explicações aí com desenho, vão ser geralmente utilizando a recursão. Então, já criei aqui a pasta funções recursivas sobre esse nosso novo assunto. Vou criar aqui um arquivo 01 chamado fatorial iterativo versus recursivo. E vou deixar as nossas duas funções, uma com fatorial iterativo, uma com fatorial recursivo implementadas Dentro desse nosso código. E aqui dentro eu quero comentar tudo isso que eu acabei falando com vocês.
A iteração vai ser mais rápida, vai utilizar menos memória e apesar de não fazer tanto sentido nesse momento, já que a gente tá trabalhando aí com problemas mais simples, eu vou tentar sempre aí mostrar também como que a gente pode estar fazendo um código de maneira interativa para que a gente vá praticando essa ideia da recursão, que vai nos ajudar a entender as soluções de problemas mais complexos. que veremos no futuro. Bom, a gente pode também aí ter recursões que vão ser melhor escritas do que outras e a gente pode também classificar essas recursões na
hora que você pode se deparar com esses termos. Faço uma recursão direta em cauta. A solução desse problema se trata de uma recursão indireta. vai ver agora o que são essas classificações. Primeiro a gente vai ver a classificação entre recursão direta ou indireta e depois a gente vai ver aí recursão em cauda ou recursão que não em cauda. Bom, a diferença entre uma recursão direta e uma recursão indireta é que a recursão direta ela acontece quando a gente tem uma função que chama a si mesma. Não necessariamente a gente vai ter que ter um código
aqui em cima ou um código aqui embaixo. Basta que essa função chame a si mesma, assim como o exemplo que a gente viu anteriormente, que nós vamos estar nos referindo a uma recursão direta. Já a recursão indireta, ela Acontece quando a gente tem uma função que chama uma outra função e essa outra função chama essa função inicial. Repetindo, na recurão direta, a nossa função está chamando a si mesma. Não importa o código que tá aqui ou o código que tá ali embaixo ou qual posição esse código vai estar. Se a função chama a si mesma,
a gente vai ter uma recursão direta. Se a função chama outra função e essa outra função chama esta função inicial, nós vamos dizer que nós temos uma recursão indireta. Toda recursão pode ser classificada em direta ou indireta. Então vamos adicionar aqui um comentário, função recursiva direta e vamos fazer uma função recursiva indireta. Bom, aqui eu vou fazer uma função A, que recebe o inteiro, e uma função B, que também recebe o inteiro. Eu vou querer que essa minha função execute enquanto esse inteiro for maior do que zero. E eu quero que o mesmo aconteça aí
paraa minha função B, que ela execute enquanto o número for maior do que zero. Aqui dentro eu vou imprimir esse meu número e depois de imprimir esse meu número vou chamar a minha função B, só que enviando agora para ela como parâmetro o valor que eu recebi -1. Vou est fazendo aí um decremento. Esse decremento é importante, senão a nossa função nunca vai terminar, né? A gente não vai estar alterando aí o valor de n e a gente vai ficar função a chamando a função b e a função b chamando a função a. Então a
gente precisa aí ter esse decremento. Depois que eu chamar essa minha função B, eu quero que ela imprima esse número, né? Vou colocar aqui B pra gente ver na impressão que esse número tá sendo impresso dentro da função B e Ela chame aí a função A. Agora é só fazer a minha main e chamar essa minha função. E aqui eu posso começar tanto chamando a minha função A quanto chamando a minha função B. E bora fazer a nossa assinatura, né? Incluir a nossa biblioteca e fazer a nossa assinatura, porque senão a gente vai ter um
problema aí que a nossa função A vai est chamando a função B e a função B não está acima dela. Agora bora dar uma olhada aí na execução desse programa. Então vamos lá. A gente chamou a nossa função A, fez a impressão 20, depois chamou a função B, fez 19, A fez 18, B, 17 e por aí vai, até que a gente chama aí a nossa função A como valor zero e nós não realizamos mais nada. Então deixei comentado aqui que a função recursiva direta é aquela que chama a si mesma e a função recursiva
indireta é aquela que chama outra função que chama a função original. Agora vamos ver a recursividade em cauda ou ta recursion. E aqui a gente vai ter que prestar um pouquinho mais de atenção. Basicamente, a função recursiva em cauda é aquela que a chamada recursiva é a última instrução. Então a gente vai ter algo desse tipo aqui. Reparem que quando a gente encontra o nosso comando return, até aumentar o tamanho, quando a gente encontra o nosso comando return, a gente sai da nossa função. Então, exatamente no momento que a gente sai da nossa função, a
gente faz a chamada dela mesma, ou seja, na nossa última etapa, no último momento dentro dessa função, a gente realiza essa chamada. Se a gente tiver essa chamada acontecendo em outros momentos que não este último, a gente não vai ter uma função recursiva em cauda. E aqui nós temos que ter um pouco de cuidado. Vou vou copiar essa nossa função fatorial recursiva. E ao bater o Olho nela, você pode estar pensando, opa, assim que a gente retorna, né, ou seja, na nossa última instrução, a gente está chamando a função fatorial recursiva novamente. Portanto, isso daqui
deve ser uma função recursiva direta em cauda. E não, pessoal, ela não é. Por quê? Porque nós não temos essa chamada de função como a última coisa da nossa função. A última coisa da nossa função é uma multiplicação. Então, primeiro a gente chama a nossa função, depois ela retorna um valor e aí a gente tem que resolver essa nossa multiplicação. Existe um jeito da gente transformar essa nossa função recursiva direta, que não é em cauda, em uma função recursiva direta em cauda. S tem, existe. A gente vai fazer uso aí de uma função auxiliar, ou
seja, em outras palavras, a gente vai refatorar esse nosso código de modo que a gente suma com essa multiplicação. E a última coisa que vai ser feita vai ser simplesmente a chamada dessa função. Mas antes da gente entender como que a gente faz isso, inclusive não é tão trivial, tá, fazer essas transformações, eu quero explicar para vocês sobre o porquê da gente estar fazendo isso. Lembrando que esse é nosso primeiro contato com funções recursivas. Então, mesmo que você tenha dificuldade ali de compreender como que a gente vai transformar uma função recursiva em uma função recursiva
em cauda, eu quero que você termine essa videoaula sabendo identificar uma recursão em cauda e uma recursão que não está em cauda. E sabendo também as vantagens de se ter Uma recursão em cauda em relação a ter uma recursão não em cauda. Pessoal, quando a gente tem uma recursão que ela não é em cauda, a gente vai ter essa operação que vai estar sendo realizada depois do retorno. Observem aqui, quando a gente retorna um valor, né, a gente pega esse valor, faz a multiplicação e depois retorna. Depois a gente faz uma outra multiplicação e retorna.
Faz uma outra multiplicação e retorna. Faz uma outra multiplicação e retorna. Se a gente tivesse uma recursão em cauda, tá? a gente não teria essa operação se a gente conseguisse sumir com ela daqui. Basicamente, a nossa última função, ela ia retornar. Essa daqui não iria fazer nada. Só iria retornar. Essa daqui não iria fazer nada. Só iria retornar. Essa daqui não iria fazer nada. Só iria retornar. Então, a gente não precisaria manter armazenado o conteúdo dessas funções. Porque pensa comigo, se a gente vai estar chamando a próxima função e a única coisa que essa função
vai precisar fazer é retornar pra anterior, né? Então ela chama essa daqui, tá? E aí não tem nenhum valor, não tem esse quatro aqui dentro que tá sendo multiplicado, não tem nada a ser feito. Ela só serviria ali como uma ponte para essa comunicação acontecer. Então ela chama uma outra função que chamaria uma outra função e aí no momento que ela chamasse ela não teria ali mais nada de relevante lá dentro. Então a gente poderia basicamente, né, pegar essa nossa seta, esse nosso return e fazer ele acontecer de forma direta, tá? saindo aqui da Nossa
última função e vindo ali paraa nossa chamada principal. Uma outra coisa também que a recursão em cauda vai te permitir é que você simplesmente apague essa função a partir do momento que você entrar na próxima. Por quê? Porque você não vai precisar de nada disso. Você só precisa saber aí qual que foi a chamada principal. Então o nosso compilador automaticamente vai conseguir trabalhar muito melhor com recursões em cauda. Recuções em cauda vão gastar menos memória, tá? Porque a gente não vai precisar ficar voltando nessas nossas funções quando a gente retornar. Prestem bastante atenção. Se a
gente não tivesse essa multiplicação, percebam que o valor aqui vai mudando, né? quatro vezes, três vezes, duas vezes, uma vez, zero vezes. E a gente só consegue resolver essa multiplicação depois que a gente obtém o retorno da chamada dessa função. Se a gente não tivesse essa nossa operação todas as vezes que a gente chamasse uma nova função, a anterior, quem chamou não seria mais tão relevante. Quem seria mais relevante seria ali a chamada principal, que é para quem a última deveria estar retornando. este valor. Por esse motivo, por esse motivo, quando você consegue escrever uma
função recursiva em cauda, ela sempre vai ter um desempenho, um custo de memória menor, tá? e um desempenho maior também na hora de realizar esse retorno, que Não vai precisar ficar voltando de camada em camada e vai gastar menos memória, porque quando chamar a próxima, né, ela pode simplesmente sumir com o conteúdo de quem chamou. Por esse motivo, a recursão em cauda, ela sempre vai ser superior. Agora, pegar uma função recursiva que não está em cauda e transformar numa função recursiva em cauda, é tarefa fácil ou difícil? vai depender muito de qual função a gente
tá trabalhando. Então, nem sempre você vai conseguir enxergar aí como fazer essa transformação e nem sempre também vai ser possível realizar essa transformação. Mas nesse nosso exemplo aqui, a gente vai conseguir táar fazendo isso. Nós vamos na função fatorial, chamar uma função fatorial auxiliar, que vai receber dois valores inteiros, n - 1 e 1. Não se preocupem muito que eu vou explicar melhor essa daqui para vocês daqui a pouquinho. E agora nós vamos ter a nossa função fatorial auxiliar. Essa vai estar recebendo aí dois valores, o int n e o int acumulador. E essa função
que vai ser responsável aí para est resolvendo o nosso problema de encontrar o número fatorial. Se o n for menor do que 1, a gente vai retornar o nosso acumulador e não mais o valor 1. E agora sim, nós vamos poder fazer essa chamada da função fatorial auxiliar, sem estar realizando nenhuma operação depois dessa chamada. E a gente vai passar o valor n - 1 e vai multiplicar n pelo nosso acumulador. Percebam que a estratégia que a gente adotou para que a gente Pudesse transformar essa nossa função em uma função recursiva em cauda, foi realizar
a nossa multiplicação antes de instanciar essa função novamente. Eu até cheguei a gravar aqui uma explicação mais detalhada, pessoal, mas eu vou deixar ela de fora porque eu não quero dar a impressão que é necessário que vocês compreendam todas essas transformações agora, mas sim reforçar a ideia de que recursões em cauda são mais eficientes, gastam menos memória e que algumas funções podem sim ser transformadas em recursões em calda. Então, agora a gente já viu que é uma função recursiva. A gente já aprendeu a classificar funções recursivas. O próximo passo é nos familiarizar com a construção
de funções recursivas. E para que a gente possa fazer isso, eu trouxe aqui quatro problemas que nós poderíamos resolver. Inclusive, eles já estavam nas nossas listas de exercício utilizando a iteração, mas nós vamos criar essas soluções fazendo uso de funções secursivas. O primeiro problema pede para que a gente crie uma função que recebe um inteiro e um caracter e imprime esse caracter n vezes. Já que é uma impressão, o retorno vai ser void. O nome da nossa função recursiva vai ser imprime caracter. E nós vamos ter que receber um inteiro e um caracter. Primeira coisa
que eu vou fazer é realizar essa impressão e depois eu vou chamar essa nossa função novamente. Porém, dessa vez eu vou passar o valor n - 1 e o meu caracter. Da maneira que está escrito aqui o nosso código, a Gente vai ir reduzindo aí o valor de n, né? Se a gente passar o valor 10, a gente vai chamar a nossa função com valor nove. o caracter, valor oito e por aí vai, mas a gente nunca vai parar essa nossa função. Então a gente precisa ter a nossa condição de parada na hora de pensar
aí em funções secursivas. Como o retorno da nossa função é do tipo vazio, a palavra return é opcional, mas eu vou deixar ela aqui porque o nosso compilador já vai otimizar isso automaticamente. E agora falta a nossa condição de parada. Se o n for equivalente a zero, então nós vamos retornar sem realizar a impressão. Percebam, se eu mandar aqui um número 4, por exemplo, a gente vai realizar a nossa impressão. A gente vai chamar essa função novamente. Agora passando o número três e a gente vai realizar a impressão. Vai chamar novamente passando o número dois,
vai realizar a impressão. Vai chamar novamente passando o número um, vai realizar a impressão e vai chamar novamente passando o número zero. Quando a gente chamar o número zero, a gente vai simplesmente retornar e a gente vai ter algo parecido com aquela imagem que a gente viu, né, esses retornos. Mas eu disse para vocês que quando a gente faz uma função recursiva em cauda, né, o nosso compilador aí vai realizar uma otimização e a gente vai poder meio que deletar, né, essas chamadas e retornar direto aí pra chamada principal. Desenvolvemos aqui a nossa solução. Vamos
simplesmente classificá-la. A gente tem aqui então uma função recursiva direta, porque ela está chamando a ela mesma e em caudda. Bora testar isso na nossa main. Imprime caracter 10 Caracter C. Vou só adicionar aqui uma quebra de linha no final. A gente pode estar executando aí esse nosso código. Realizamos aí a impressão. 1 2 3 4 5 6 7 8 9 10. OK. Bora então pro nosso próximo problema. Faça um programa que calcule a média de n números informados pelo usuário até ele informar o número zero. Nesse momento, quando você estiver aprendendo a fazer uso
de funções recursivas, eu quero que você fique extremamente à vontade, livre, use todos os recursos que você já aprendeu, tá? Então eu vou tentar aqui me manter mais livre também nas construções das minhas soluções, pelo menos por enquanto. Então aqui é de cara assim, eu acho que eu quero fazer uso aí de uma variável global, né? Uma variável global que vai ser ali a soma e também vou fazer uma outra variável global que vai ser ali o contador de números informados. E agora eu vou criar a minha função calcula média. A função calcula média ela
não vai retornar nada. Vou fazer do tipo void. calcula média e ela também não vai receber nada. Vou imprimir aqui pro meu usuário. Digite um número ou zero para finalizar a execução. Scan F. Vamos receber esse número. Quando a gente receber esse nosso número, a gente vai fazer ali a adição do conteúdo desse número a nossa variável soma. Depois vamos fazer um incremento no nosso contador de números informados e vamos chamar aí a função calcula média. return, calcula, média. Porém, do jeito que está aqui, a gente não tem a nossa condição de parada, ou seja,
a pessoa Vai digitar o número, a gente vai somar o conteúdo digitado dentro dessa nossa variável soma, a gente vai aumentar o contador e a gente vai executar essa nossa função aí, vai chamar ela novamente. Então, a gente nunca vai parar, nunca vai terminar. Então, a gente precisa pensar na nossa condição de parada, que eu vou colocar aqui depois do nosso scan f. Se a entrada for equivalente a zero, quando o usuário digitar zero, aí nós vamos simplesmente sair dessa nossa função. Vamos dar uma olhada. Calcula média e vamos imprimir a nossa média. Vou colocar
aqui do tipo float, né? E vou dar um um cast na soma, que é o numerador dividido aí pela quantidade de números informados. Por que que eu tô fazendo isso? Porque o resultado dessa divisão aí pode ser um float. Bora rodar esse nosso código. Digite um número ou zero para finalizar a execução. 8 7 6 7 8 e 0. E imprimimos aí a nossa média. Opa, já tava indo aqui paraa nossa série de Fibonacci, mas vamos classificar aí essa nossa função recursiva, né? Então, a gente tem aqui uma função recursiva direta, chama-se mesmo, e em
cauda, já que a última operação a ser realizada é a chamada da função, né? Não é nenhuma multiplicação ou adição ou não tem nada, nenhum tipo de código aqui. Depois feito isso, bora resolver aí a série de Fibonacci, pessoal. a série de Fibonacci. Vou escrever ela aqui. 0 1. Ela começa ali com 0 e 1. E a gente vai realizando a soma dos dois números anteriores. Então a gente somaria ali zer e 1 e a gente obteria ali o número que estaria na posição três ou na posição dois, depende se a gente vai começar contando
do zero. Depois a gente somaria 1 e 1 e a gente tá obtendo ali o valor 2. Depois a gente somaria 2 e 1 e a gente já tá obtendo o valor 3. 3 e 2 a gente tá obtendo o valor 5. 5 e 3 a gente iria estar obtendo o valor 8. 8 e 5 a gente está aí obtendo o valor 13. 13 e 8 a gente estaria obtendo o valor 21. E por aí vai. Inclusive é bem interessante se você quiser dar uma pesquisada aqui no YouTube mesmo sobre aplicações e usos da série
Fibonacci, tá? Existem várias, ela é bem importante, mas vamos aqui paraa nossa solução. Nós vamos retornar qual que é esse termo. Esse nosso termo vai ser inteiro, né? Então o retorno aqui vai ser um int. A gente vai ter aqui fibonacci e nós vamos receber a posição do termo que a gente quer encontrar. Vai ser into. Quando a gente vai pensar aqui na sequência de Fibonacci, é interessante que você evite sempre confundir aí o conteúdo da posição, tá? Então, por exemplo, se eu pedir ali a posição número três, ela vai ter ali o valor um.
Se eu pedir a posição número quatro, ela vai ter ali o valor dois, né? Imaginando aí que a nossa posição tivesse começando com um. Vamos, vamos até para facilitar, vamos pensar aí que a nossa posição começa com zero, né? A Posição zero tem um conteúdo zero, a posição um tem um conteúdo um, a posição dois também tem o conteúdo um e assim vai. Então, a gente teria aqui a nossa posição quatro, teria o conteúdo três. E vamos tentar não confundir aí a posição com o conteúdo. Dessa vez eu já vou começar pela condição de parada,
né? Se a gente quiser aí a posição zero, nós vamos retornar o valor zero, porque na posição zero, o conteúdo dela é zero. Se a gente quiser a posição um, nós vamos retornar o valor um. Porque o conteúdo da posição um é um. Separem bem isso, a posição e o valor. Coincidentemente aqui, o valor da posição um é um também. E aí vamos supor que o nosso usuário aí pediu a posição quatro. Por exemplo, se ele pedir a posição quatro, como é que a gente vai descobrir qual que é o conteúdo da posição quatro? Ah,
a gente tem que somar a posição três com a posição 1. E aí, como é que a gente descobre o conteúdo da posição três? A posição três é a soma da posição dois com a posição 1. Posição quatro seria até aqui, né, que a gente tá começando em zero. Como é que a gente descobriria então aí a posição dois, né? A posição dois é a posição um e a posição zero somadas. Então a gente vai fazer algo que vai ter esse comportamento aí. Então a gente quer a posição quatro, por exemplo, né? Pra gente descobrir
ela, a gente vai ter que fazer a soma da posição 3. 4 - 1 é 3, com a posição 2. Não é assim que a gente descobre a posição 4? E aí, como é que a gente vai descobrir esse conteúdo? A gente vai chamar essa nossa função de maneira recursiva e aí ela vai descobrir lá o Conteúdo da posição três. Vai ser a soma desses dois. Ela vai chamar esses dois. O conteúdo da posição dois vai ser a soma desses dois. E aí quando chegar nesses valores vai ir retornando ali pra gente, OK? Vamos então
classificar aí essa nossa função. Uma função recursiva direta ou indireta. Direta, porque ela chama a si mesma. E aí, encaluda ou não. Aqui, pessoal, a gente tem que prestar bastante atenção que a última operação não vai ser a chamada dessa função e sim a soma, né? Quando a gente recebe esses valores, a gente ainda realiza algo que é a soma. Por esse motivo, a gente tem uma função recursiva direta, não em cauda. Agora vamos resolver o nosso próximo problema. O programa deve verificar se o caracter fornecido pelo usuário está presente na string. A gente vai
retornar aí verdadeiro ou falso, né? Está presente ou não está presente. Verifica caracter. E a gente vai aí receber uma string, né? e um caracter. Agora, pessoal, se a gente tiver ali o primeiro elemento da nossa string, tá? o primeiro elemento da nossa string equivalente ao nosso caracter, a gente vai retornar aí verdadeiro. Caso contrário, se a gente tiver esse nosso primeiro elemento aí como aquele nosso caracter que indica o final da nossa String, nós vamos retornar falso. E se for um caracter qualquer, aqui eu já trabalhei as minhas condições de parada, né? Mas e
se for um caracter qualquer, a gente tem que simplesmente verificar o próximo. E aí, pra verificar o próximo, para verificar o próximo, eu vou fazer o seguinte, vou fazer aqui string mais um. E a gente vai ver o que que é isso. A gente vai ver esse nosso operador aqui asterisco, é o nosso próximo conteúdo, tá? Mas só entendam, né, que quando eu estou fazendo isso, eu estou pegando a minha string a partir da próxima posição. Então, se eu tivesse uma palavra ali que tem 50 caracteres, agora eu vou est enviando aí uma palavra que
tem 49 e excluindo o primeiro caracter. E vou mandar aí o nosso caracter. Claro, preciso colocar o return. Não testei a função de Fibonacci, vou colocar ela aqui pra gente testar. E bora testar também a nossa função verifica caracter. Para isso, já vou criar aqui uma string. Meu nome não é Johnny, e um caracter J. E vamos imprimir J está na string. Porcentagem de fazer a chamada dessa nossa função. Verifica caracter. Enviando aí a nossa string e o nosso caracter. OK. Bora executar isso daí. It número ou zero para finalizar a execução. Essa a gente
já testou, então vou digitar zero. Valor Fibonacci na posição 4 é 3. Posição 4 tá aqui. É, o Valor é 3. J está na string? Sim, está na string. Então agora a gente pode salvar esse nosso arquivo 4, função recursiva prática. E chegamos aí ao final da nossa aula de função recursiva. Bom, a mensagem que eu gostaria de deixar para vocês aqui, quase que um pedido também para que vocês acreditem em mim, é que apesar da intereração ela ter um desempenho maior, ela gastar menos memória, eu gostaria bastante que vocês não se assustassem com esse
conteúdo de recursão e que vocês tentassem se familiarizar com soluções recursivas, mesmo que demore um pouquinho a mais, mesmo que você perca um pouco de desempenho, o benefício que você vai ter praticando a construção desse tipo de solução vai ser muito grande. Então, pessoal, não se assustem com esse conteúdo e pratiquem. Nossa lista já está aqui na descrição. Desenhem se necessário e eu vejo vocês o nosso próximo conteúdo. Muito bem, pessoal. Vamos começar o nosso novo assunto, um assunto que eu gosto bastante, vocês vão ver que ele vai dar sentido aí para muita coisa que
a gente já viu. Se chama ponteiros. Pessoal, quando a gente tem uma variável e a gente salva um conteúdo dentro dessa nossa variável, a gente sabe que a gente pode verificar o valor do que está salvo dentro desse nosso espaço de memória, utilizando aqui um print f e imprimindo aí a nossa variável. A gente sabe que as nossas variáveis têm seis propriedades. Elas vão ter nome, endereço, tipo, conteúdo, escopo e tempo de vida. E a gente sabe também acessar o endereço dessas nossas variáveis. Então, a gente pode fazer aqui endereço de A. A gente utiliza
aí o porcentagem X e faz uso desse nosso operador aqui, que vai nos retornar o endereço dessa variável. E o porcentagem X pra gente estar imprimindo aí endereços. Vamos executar esse nosso código, OK? E esse número que a gente tá vendo, ao realizar a impressão do endereço, ele é um número hexadecimal. Ah, mas por que que não usa ali a base 10, né? Os números que a gente aprende, que a gente utiliza normalmente são da base dessa. Por um único motivo, pessoal. Quando a gente faz uso de uma base hexadecimal, a gente consegue mostrar um
número maior com menos símbolos. Então, se eu tiver que mostrar ali 1 milhão usando a base 10, a gente vai ter 10 números, tá? No mínimo, né? 10 símbolos ali. Se a gente fizer uso de uma base hexadecimal, a gente vai conseguir mostrar ali de um jeito mais curtinho. E como a gente tem vários endereços de memória, geralmente a gente utiliza aí números hexadecimais. Não precisa se preocupar com isso, mas se você quiser entender um pouquinho melhor, você pode pesquisar aí sobre mudanças de base. Agora, pessoal, eu quero mostrar para vocês esse operadorzinho aqui, esse
asterisco, tá? na hora da gente estar criando um espaço na memória, ele serve para indicar que a gente vai armazenar como conteúdo um endereço e ele vai permitir que eu faça algo desse tipo aqui. Vamos dar uma olhada num desenho pra gente entender melhor o que que tá acontecendo. Então, imagina que cada um desses espaços aqui corresponde a 4 bytes, né, que é o tamanho de um inteiro. E aqui a gente Criou a nossa variável A e armazenou o conteúdo cinco. E é claro que essa variável, se ela existe, ela tem as seis propriedades de
uma variável e esse conteúdo número cinco. Então ela vai ter um endereço, seria ali um número hexadecimal. Vou inventar um aqui. Ela teria também um nome, vou colocar o nome aqui em cima, tá? E aí eu criei uma nova variável. Essa minha nova variável se chama P, tá? Ela tem o nome P. Então vamos colocar aqui o nome P. Ela vai ter o tipo dela, tá? Vou deixar aqui fora inteiro. E aqui eu vou colocar o tipo ponteiro para inteiro. Ela vai ter um endereço também. Vamos botar aí que o endereço seria, sei lá, 77.
Só que agora o conteúdo dessa nossa variável vai ser este daqui, um endereço de memória, tá? Um endereço de memória. Isso é o ponteiro. Então, a gente está salvando um endereço de memória dentro de uma variável. O conteúdo agora não é mais um número inteiro qualquer, mas sim um endereço, um hexade decimal. Observem que no nosso código a gente imprimiu o conteúdo de A e a gente imprimiu o endereço. Então agora a gente está salvando este endereço. Como é que eu posso provar isso para vocês? Eu vou imprimir aqui o endereço de P. E aí
a gente vai usar aqui endereço de P. Eu vou imprimir o conteúdo de P. também vai ser um endereço que a gente pode estar executando esse nosso código. Só trocar o cil aqui e adicionar uma quebra de linha. E observem, aqui está o endereço De A. Aqui está o endereço de P, que é diferente do endereço de A, porque é uma outra variável. E o conteúdo de P é exatamente o endereço de A. Nós temos também um jeito da gente conseguir imprimir o conteúdo de a se a gente tiver esse endereço, que é fazendo uso
do desreferenciamento. Vou mostrar isso aqui para vocês. Conteúdo da variável cujo endereço está salvo em P. Aí, como a gente tá imprimindo o conteúdo, a gente vai fazer uso do porcentagem D. A gente faz uso novamente do asterisco IP. Vamos ver aqui o que acontece. Observem, o conteúdo de A é 5. O endereço de A é esse daqui, que termina com 5C. O endereço de P termina com 5, o conteúdo de P é o endereço de A. E a gente consegue aí chegar no conteúdo de A, fazendo uso dessa operação que é o desreferenciamento. E
por esse motivo a gente imprimiu aqui o valor Sim. Percebam o que está acontecendo. É, eu tenho essa minha variável, ela tem um endereço, eu pego esse endereço, acesso ele, depois eu pego esse conteúdo e depois eu retorno esse conteúdo. Então, vou salvar esse nosso novo arquivo e vou explicar um pouquinho melhor tudo isso para vocês. A gente tem que ter muito cuidado quando a gente esver trabalhando com ponteiro, porque esse símbolo aqui, esse asterisco, ele pode significar três coisas diferentes. e vou deixar elas comentadas aqui. A primeira pode ser a Declaração de um ponteiro.
Por exemplo, se a gente fizer algo como float P, a gente está criando esse espaço na nossa memória que armazena o endereço. Segundo, pode ser o desreferenciamento de um ponteiro. Por exemplo, #p recebe o valor 10. O que a gente tá fazendo? A gente não está criando, né? não tem o tipo antes. Então, se a gente tá desreferenciando esse ponteiro, a gente tá pegando o endereço, acessando essa variável e lá dentro a gente está trocando este valor. E também pode ter um significado que a gente já viu antes, né, da multiplicação. Por exemplo, X x
Y. Vamos escrever um código para poder deixar claro essa diferença. Vamos criar uma variável do tipo float. Vamos colocar ali um valor 5.0. o F no final, né? Senão seria ali um double. A variável B. Vamos colocar o valor 2.0. Vamos criar os ponteiros. Ponteiro para A, que vai ter o endereço da variável A. Ponteiro para B, vai ter o endereço da variável B. Aqui a gente está declarando um ponteiro. Por quê? Porque a gente tem esse nosso tipo antes. Então não é um desseferenciamento, né? A gente tá declarando a nossa variável. poderia ter feito
ali em linhas separadas, mas eu posso também fazer junto. Vamos criar a variável float R1 e vamos fazer A x B. Então, vou só deixar comentado aqui. Primeiro, declaração de um ponteiro. Terceiro caso, multiplicação. E vamos fazer aqui um segundo caso, desreferenciamento E multiplicação. A gente vai criar a variável float R2. Vamos desreferenciar o nosso ponteiro para a, né? Ou seja, a gente vai acessar o valor da variável A e vamos multiplicar aí pelo conteúdo da variável B, que a gente vai acessar através deste nosso ponteiro para B. E vamos realizar a impressão R1 F,
colocar uma quebra de linha R2 porcentagem F. Colocar uma outra quebra de linha e vamos imprimir aqui R1 e R2. Como a expressão ficou curta, né, nem fazia tanto sentido, eu tá separando, vou deixar junto mesmo. Bora executar esse nosso código. E percebam que a gente tem o valor em R1 e o valor em R2 que também é 10. Então, a gente fez o desreferenciamento e quando a gente faz o desreferenciamento, a gente vai estar acessando o conteúdo da nossa variável A e B. Vamos salvar esse nosso arquivo. Claração, desreferenciamento, multiplicação ponto C. para garantir
que você entendeu. A gente tem algo mais ou menos assim acontecendo na memória. A gente tem a variável A, a gente tem a variável B. Lá dentro a gente salvou o conteúdo float, né? 5.0, 2.0, deixar escrito aqui que é um float. E aí nós temos as variáveis PA e PB, que são ponteiro para float, ou seja, elas guardam o endereço de uma variável do tipo float e aí vai ter o endereço da variável A e o endereço da variável B. salvo lá dentro. Se eu imprimir o conteúdo da minha variável PA, vai ser um
endereço. Mas se eu utilizar o operador de d referenciamento, aí eu posso Simplesmente considerar que eu tô manipulando a Então, é um outro jeito de se pensar quando você usa o operador de desse referenciamento. Na verdade, você pode fazer uso aí como se você estivesse manipulando a sua variável A. Vamos dar uma olhada agora em operações com ponteiros, né? Quando eu digo operações com ponteiros, é quando a gente não está fazendo o desreferenciamento. Porque lembra do nosso desenho, quando a gente tá fazendo desse referenciamento desse ponteiro PA, a gente está, na verdade, acessando a nossa
variável A, tá? A variável cujo endereço está salvo em PA. Só que o que que acontece se eu tentar realizar alguma operação sem fazer esse desse referenciamento? E será que isso tem algum sentido? Vamos dar uma olhada. Vamos criar a variável A e vamos criar um ponteiro pra variável A. Inclusive, pessoal, tem como fazer isso aqui em uma sol linha, tá? Seria assim, int a vulg e já coloca aqui o endereço de A. E vamos realizar a impressão desse endereço mais um e desse novo endereço. Aqui eu tô utilizando porcentagem X porque eu quero imprimir
o conteúdo que tá armazenado na nossa variável P, que é um endereço. Então vou imprimir aqui P e vou fazer p + 1 e também vou fazer aqui P e P - 1. E vamos dar uma olhada para ver o que que acontece. Percebam, pessoal, que a primeira impressão aqui é o endereço, né, que estava dentro da variável P, que é o endereço de A. E se eu fizer mais um, aumentar aqui o tamanho para vocês verem, a gente vai ter um aumento aí de 4 bytes. Então a gente saiu do final 94 e foi
pro final 98. Por quê? Porque um inteiro ele ocupa 4 bytes na memória. E se a gente fizer este endereço -1, a Gente vai ter aí uma diminuição de 4 bytes. Para que a gente tenha uma representação visual do que tá acontecendo, imaginem que a gente tem na nossa memória RAM a nossa variável P ou o nosso ponteiro, que é simplesmente uma variável que guarda um endereço. E aí nós fizemos p + 1. Quando a gente faz p + 1, percebam, né, que a gente tem aqui um endereço. Esse endereço termina com 04. Nós vamos
fazer essa soma e aí o final vai ser 08. Coincidentemente, ao lado da variável A estava a variável B. Então, quando a gente somou um ao nosso ponteiro, né, a gente fez aqui, vou escrever o código para vocês, P é igual a P + 1 ou P+, que significa a mesma coisa. Nós passamos a ter o endereço da variável B. Isso daqui é simplesmente uma coincidência e uma suposição que eu estou fazendo. Nada me garante que quando eu criar a minha variável B, ela vai estar aqui do lado. Pode ser que ela estivesse aqui, por
exemplo, el tivesse nada ali. OK? Nesse caso aqui, vamos pensar que ela vinha logo em sequência na nossa memória. Agora, e se eu tivesse aqui, né, esse conteúdo da minha variável A, ele fosse esse meu primeiro espaço de memória e eu fizesse P = a P - 1, pessoal, aí a gente iria reduzir aqui para 0 Z0 e a gente estaria fazendo uso desse espaço de memória aqui. Esse espaço de memória aqui, esse espaço de memória ele pode não fazer parte do meu programa. E é claro, pessoal, que Tem que existir uma segurança, né? A
gente tá rodando vários programas no nosso computador ao mesmo tempo. Como por exemplo, eu tô com esse navegador aberto, eu estou com um Visual Studio Code aberto, eu posso estar com um programa em execução no meu terminal. E o nosso sistema operacional, ele é responsável por impedir que você, né, dentro desse programa aqui, como se fosse um escopo mesmo, tente acessar outros espaços de memória. Por que que faz isso? Por uma questão de segurança, para preservar também o funcionamento de outros programas. Senão a gente conseguiria aí fazer um código em C para ficar observando aí
que que tá acontecendo em todos os nossos espaços de memória. Isso não é algo que deve ser possível. Então, se a gente tentar acessar um espaço de memória que não faz parte do nosso programa, o nosso sistema operacional vai nos barrar. E por que que eu estou dizendo isso para vocês? Vão existir algumas situações onde a gente vai ter endereços dentro dessa nossa variável, dentro deste nosso ponteiro, que não deveria estar sendo acessado pelo nosso programa. E a primeira situação que você pode pensar é quando você simplesmente cria ali essa nossa variável do tipo ponteiro
e não coloca nenhum valor dentro dela. Então, se o comportamento for igual de todas as variáveis que a gente trabalhou até agora, a gente vai ter ali um lixo na nossa memória, um valor aleatório. E se a gente tentar acessar um endereço para qual a nossa variável está apontando, isso poderia aí ser um erro muito comum. Imagina que a gente criou aqui o int ponteiro, a gente esqueceu de colocar o endereço nele e a gente poderia estar apontando aí para vários lugares na nossa memória. Poderia ser o endereço de qualquer lugar da nossa memória. E
se a gente tentar simplesmente usar essas variáveis, aí a gente teria um problema. Para evitar isso, o nosso compilador simplesmente por padrão coloca o valor zero toda vez que a gente cria um ponteiro. Vamos dar uma olhada nisso, nessa tentativa aí do nosso compilador de nos impedir, né? Então vamos criar aqui dois ponteiros. Ao ponteiro X eu não atribuí nenhum valor. Ao ponteiro Y eu atribui o valor zero, só que escrevendo a palavra nulo. E na linguagem C, tá pessoal? Essa palavra é simplesmente como se ela fosse aqui um define. O nosso compilador vai passar
aqui substituindo nulo por zero. Percebam que o valor de x e o valor de y são zero. Vamos dar uma olhada no que aconteceria se a gente tentasse imprimir o conteúdo do endereço zero. Na verdade é endereço nenhum, né? Porcentagem de desreferenciando Y ou descreferenciando X, já que os dois t como endereço zero. Vamos ver o que que aconteceria. o nosso programa nem chegaria a realizar a impressão e simplesmente pararia a sua execução. Bom, então se eu tentar ali acessar o valor do espaço zero, que não existe, o meu programa vai parar de executar. E
se eu tentar acessar um espaço que vem depois do que eu posso ou que vem antes do que eu posso, será que o meu programa também vai parar de executar? Vou mostrar isso aqui para vocês, mas antes eu vou salvar esse nosso arquivo. Ponteiros inicializam nulo. Bom, a gente já sabe que a gente pode tá acessando esses nossos espaços de memória, realizando algumas operações com o nosso ponteiro. Antes da gente fazer essas operações, só mostrando aqui para vocês que eu reorganizei e reintitulei alguns dos nossos arquivos para ficar mais organizado. Então, a gente tem aqui
uma introdução a ponteiros, onde a gente vê como a gente cria um ponteiro e mais ou menos do que Se trata. A gente tem aqui a diferença entre declaração desse referenciamento, que é paraa gente utilizar aquele endereço, né, salvo no nosso ponteiro como variável e multiplicação. A gente tem as nossas operações com ponteiro, que a gente viu aqui, a adição e a subtração. O que acontece quando a gente adiciona mais um e subtrai -1? A gente viu que os nossos ponteiros eles já inicializam com valor nulo, diferentemente das nossas variáveis. E agora a gente vai
fazer um teste aí apontando para onde a gente não devia. Então vou criar aqui uma variável A, vou criar um ponteiro para A, vou colocar esse ponteiro para receber o endereço da nossa variável A e vou fazer um for 20i igual a 0. Vai ir até o valor 1000 fazendo i mais. E eu quero imprimir o índice I, o endereço salvo em PA e o conteúdo do endereço salvo em PA. E aqui a gente vai utilizar o porcentagem D, né? A gente vai acessar um espaço que a gente não sabe do que se trata, mas
nós vamos tentar ler aquele espaço ali como se fosse o inteiro. Aí a gente faz uso da operação de desse referenciamento. P é igual a PA men e eu vou fazer aqui 100. Bom, o que que quer dizer esse - 100 aqui? Se a gente tivesse fazendo -1, o nosso ponteiro, vamos supor que ele tá apontando para A. ele iria logo em seguida pro próximo, só que em vez de eu voltar pro anterior, eu vou voltar ali sem variáveis, né, no meu endereço. E vamos ver o que que vai acontecer quando a gente executar esse
nosso código. Eu consegui acessar todas essas 1000 posições, não tive nenhum problema. Conteúdo aqui é zero, né? Faltou uma quebra de linha, mas o acesso aconteceu normalmente. E se eu tentar aí fazer com que esse meu salto aí seja de 500 em 500, será que isso vai acontecer? Também também consegui. Então agora eu vou tentar fazer com que esse meu salto aí seja de 2000 em 2000. E aí eu [limpando a garganta] só consegui acessar até a variável 260. O que isso quer dizer, pessoal? Nós vamos poder, quando a gente tentar acessar variáveis aí que
a gente não deveria, né, que a gente nem sabe do que se trata, não foi a gente que criou, a gente acabou fazendo isso aí por est realizando uma operação indevida. Caso nós estejamos tentando acessar um espaço de memória que o nosso sistema operacional liberou aí pra gente, pro nosso programa, a gente não vai ter problema nenhum. A gente vai conseguir simplesmente est acessando, vendo o conteúdo de lá e não só vendo o conteúdo de lá, mas também alterando, tá? Porém, caso esse espaço que eu tente acessar não faça [limpando a garganta] parte do meu
programa, ele vai simplesmente interromper a sua execução, podendo retornar um erro ou não. Nesse caso aqui, eu tô só verificando, né? Eu só estou lendo o conteúdo da minha memória. Mas eu poderia também estar modificando, fazendo o seguinte, ó. Vou agora colocar o valor 10 nesses endereços e aí depois eu vou imprimir esse conteúdo. Observem que agora eu consegui alterar esses valores aí até esse endereço. Quando eu tentei ir um pouquinho além, o meu programa foi interrompido. Então vou deixar isso Escrito aqui pra gente. Ao acessar conteúdo do endereço nulo, o programa interrompe. Ao acessar
conteúdo que não deveríamos, o programa pode acessar endereço de memória válido. Sorte ou azar, né? Será que é uma sorte a gente conseguir acessar isso ou é um azar, já que o nosso programa não vai interromper a sua execução e a gente vai estar realizando uma operação ali no que poderia ser o espaço de uma outra variável. O programa pode interromper informando o erro ou não. E aqui eu já criei esse nosso arquivo apontando para quem a gente não devia. Dito isso, pessoal, a gente vai tentar se manter aí dentro da regra. E a regra
não é só apontar para onde a gente pode, mas sim apontar para onde a gente deve, pros locais onde a gente já criou uma variável, pros locais que fazem sentido, tá bom? Nós vamos fazer isso para tentar manter a integridade do nosso programa, para que ele não fique alterando coisas que a gente não gostaria que fossem alteradas e sem a gente saber. Bom, pessoal, com isso que a gente viu, a gente vai agora poder entender o que é um arrei, ter muito mais clareza aí sobre como que a gente passa um arrei para uma função
ou como a gente retorna um arrei para uma função. Primeiro eu vou escrever aqui, né, o que é um arrei. Na linguagem C, um arrei nada mais é do que um ponteiro para o primeiro valor de um conjunto de variáveis sem nome, variáveis do mesmo tipo e armazenadas em Sequência na memória. Vamos ver isso aí melhor. Se eu criar aqui um arre inteiros com três elementos, né? três elementos. Eu estou, na verdade, na verdade, criando um ponteiro para o primeiro elemento. E se isso daqui é um ponteiro para o primeiro elemento, eu posso então imprimir
aí o conteúdo desse meu ponteiro, certo? Então vou colocar aqui a. Vamos ver se realmente aí ele é um ponteiro, se é uma variável que contém um endereço ou não. Se a gente quisesse alterar o conteúdo, a gente pode fazer uso aí desses colchetes, correto? E bom, a gente conseguiu aqui, né, usando o A, a gente tá, na verdade, tendo acesso a um endereço. Por isso que quando a gente faz uso de um arrei, a gente não pode vir aqui e fazer assim, a igual a 10, tá bom? Mas se isso daqui é um ponteiro,
a gente poderia desreferenciar então o endereço de a mais 0, a gente vai est acessando a posição zero e colocar lá o valor 20. a gente poderia desreferenciar aí o endereço de a + 1 e aí a gente estaria acessando aí a segunda posição do nosso arreio. A gente pode fazer a mesma coisa para mais dois e aí a gente estaria acessando a terceira posição do nosso arreio. Vamos realizar a impressão desse nosso arreio aqui. Como são só três elementos, vou usar um print em vez de usar uma interação. E vamos dar uma olhada. Imprimimos
o endereço inicial, os três valores, né? Só faltou uma quebra de linha. E depois fizemos essa nossa alteração. Percebam então pessoal que na verdade os colchetes, os colchetes eles são simplesmente uma maneira automática, né, de est realizando essas operações aí, de se movimentar através de endereços. Então vou escrever isso aqui pra gente. O operador colchetes é a junção de duas operações. A primeira, a soma de um ponteiro com um inteiro e a segunda, o desreferidenciamento do resultado dessa soma. Ponteiros e a reis. Então vamos ver o que que o uso de ponteiros vai nos trazer
aí de benefício. Imagina que a gente tinha ali a nossa função main, a gente criou a nossa variável e colocou o conteúdo 1000 dentro dessa nossa variável. Depois a gente chamou a nossa função dobra, que é uma função void, e a gente enviou aí a nossa variável A como parâmetro. A gente sabe, né, que até então as nossas funções funcionam da seguinte maneira. Ela pega esse parâmetro que foi enviado, cria um novo espaço na memória e copia esse valor para esse nosso novo espaço. E nessa nossa função, a gente tá multiplicando esse conteúdo por dois.
Se a gente tá multiplicando esse conteúdo A por do, nós estamos vindo nessa variável A, que só é acessível dentro da nossa função, multiplicando por dois, obtendo o valor 2000. E depois a gente sai dessa nossa função sem retornar nada. Como a gente não retorna nada, esse nosso espaço de memória é esquecido, deixado de lado, né? A gente não tem mais acesso a ele e a gente retorna para nossa função main e realiza uma impressão. Essa impressão que a gente vai realizar vai ser dessa nossa variável aqui. E a gente não vai conseguir fazer essa
alteração, já que nós fazemos uma passagem por valor. A solução que a gente tinha para esse problema era fazer algo assim. a gente fazia com que a nossa função dobra retornasse um conteúdo do tipo inteiro e aí a gente alterava esse nosso valor. Em outras palavras, a gente fazia essa alteração aqui, retornava esse valor armazenando agora nessa minha variável e depois a gente simplesmente deixava isso aqui de lado. Porém, como nós agora sabemos armazenar endereços, a gente vai poder criar uma função que vai conseguir alterar diretamente esse espaço aqui da minha memória. E as funções
que fazem isso, nós vamos dizer que são funções que recebem passagem por referência. Vamos dar uma olhada nisso. Vou voltar aqui para um retorno vazio. Agora a gente não vai mais salvar nada. E ao invés de eu estar passando o conteúdo da minha variável A, eu vou passar o endereço da minha variável A. Se eu estou passando o endereço, eu não posso armazenar esse endereço numa variável que recebe o inteiro, mas eu posso armazenar esse endereço num ponteiro, que é um tipo de variável feita para armazenar endereços. Feito isso, eu vou poder disreferenciando essa minha
variável, ou seja, acessando aí agora o endereço que eu armazenei a minha variável a lá da minha função main, vou Multiplicá-lo por função dobra pa. E agora sim, o que que tá acontecendo na prática? A minha função agora, ela não recebe mais o conteúdo de a, mas sim um endereço. Ela armazena esse endereço. E aí a gente utilizando a operação de desreferenciamento, que é muito poderosa, nos permite simplesmente tratar como se a gente estivesse trabalhando com esta variável aqui, a gente tá fazendo essa alteração direta e depois a gente simplesmente apaga essa nossa variável sem
ter que se preocupar em retornar nada. Então, a partir de agora, a gente ganhou essa capacidade de fazer chamadas de funções com passagem por referência, onde a gente consegue mandar o endereço de uma variável e tendo esse endereço, a gente não vai ter aquele problema do escopo. Essa nossa variável vai ficar acessível e aí a nossa função vai poder alterar conteúdos de variáveis que estão na minha função main. Passagem por valor versus passagem por referência. E pessoal, isso daqui acontece só na linguagem C? Não. Em qualquer linguagem de programação, você vai se deparar com esse
termo. Passagem por valor e passagem por referência. A linguagem C, ela é excelente porque ela deixa claro essa diferença. Dito isso, a gente tem total condição de entender o que que estava realmente acontecendo quando a gente viu ali a passagem de arreis como parâmetros. Vou copiar aquele mesmo arquivo. Arrei como parâmetro. Passagem por referência. E vamos entender um pouquinho melhor. A gente tem aqui um arrei, né, contém cinco elementos. O nome desse nosso arrei é números. Números. Vamos entender aí que o primeiro elemento tem o endereço 660, que tá armazenado aqui dentro. Quando a gente
faz essa chamada de função passando esse números, a gente tá passando esse endereço, que é o endereço do primeiro elemento, tá sendo armazenado aqui. E depois a gente tá passando a quantidade, ou seja, lá na nossa função soma elementos, nós vamos ter soma elementos, duas variáveis. Uma vai armazenar esse endereço e a outra vai armazenar o tamanho do arrei, que nesse caso é cinco. Se a gente tem este endereço e a gente tem esse tamanho, a gente poderia aí ir caminhando, realizando essas somas. Mas nós vimos que os colchetes vão nos permitir realizar essa operação.
Caso a gente não tivesse fazendo uso dos colchetes, aí nós teríamos que fazer duas operações, a soma de um ponteiro com o inteiro e depois o desreferenciamento, que é acessar esse espaço da memória. Então, para facilitar, a gente faz uso dos colchetes. Agora a gente consegue entender também por que quando a gente passa strings como parâmetro, 09 strings Como parâmetro, nós também temos uma passagem por referência. a gente consegue estar enviando somente este primeiro endereço, porque nós sabemos que a gente chegou no nosso último elemento quando a gente encontrar este caracter aqui. Ou seja, quando
a gente tá se referindo à strings, a gente não precisa do tamanho, porque a gente vai ir caminhando em cada uma dessas posições e vai parar quando a gente encontrar esse caracter. Se a gente não encontrar esse caracter, a gente vai ter um problemão, porque a gente vai ficar indo aí para outros lugares, mas strings normalmente sempre vão ter esse nosso último elemento. Uma outra coisa que agora a gente vai conseguir entender muito melhor, inclusive criar uma nova solução, né, seria em relação ao retorno de um arrei 10 retorno de arrei por referência. Peguei aquele
nosso código. Percebo, a gente está chamando essa nossa função que cria um arrei. E aí a gente cria esse arrei com a palavra estático para que ele não suma, que ele não deixe de existir quando a gente encerrar a nossa função. E a gente retorna esse valor inicial. Obtendo esse valor inicial, a gente consegue percorrer o nosso arrei. O problema é que eu preciso saber aí o tamanho, né, desse meu arrei. E quando eu estou retornando, eu só posso retornar uma coisa, que é esse valor inicial. Eu não tô retornando aí o tamanho. E aí,
como é que eu vou saber quando parar? Percebam o que eu estou dizendo é que nós estamos a nossa função main, a gente chama uma outra função. Essa outra função cria esse arrei. Ela atribui valores a esse arrei e ela retorna o endereço inicial, tá? Que a gente vai armazenar aí em um lugar da nossa função. M. Qual que é o problema? a gente não sabe quando termina o nosso arreio. Vejam só que a nossa função está retornando aqui um ponteiro para um inteiro. E a gente tá armazenando esse valor e uma variável ponteiro para
um inteiro na nossa função main. Se a gente tivesse retornando uma string, a gente não teria que se preocupar com isso, porque nas strings a gente consegue ir percorrendo ela até encontrar o caracter que indica o final dessa nossa string. Nós vamos ver uma alternativa onde a gente vai conseguir aí retornar tanto o nosso arrei quanto essa quantidade. E a gente vai fazer isso usando aí tipos criados por nós mesmos, já que nada nos impede de retornar um tipo aí que vai conter um arreio e a quantidade de elementos desse arrei. Porém, antes da gente
ver aí ponteiros para registros, eu vou revisar rapidinho aqui o que a gente viu. A gente fez uma introdução, vimos ali o que que eram os ponteiros, nós vimos aí a operação de declaração, como que a gente cria essa variável capaz de armazenar endereços. Nós vimos como a gente faz uso do desreferenciamento. A gente viu que o desseferenciamento ele é bem poderoso, porque ele nos permite, né, simplesmente colocando esse asterisco, que a gente utilize o nosso ponteiro como se ele fosse aquela variável cujo endereço está armazenado no nosso ponteiro. Depois nós vimos a operação de
adição e de subtração com os nossos ponteiros, né? O que que acontece? A gente vai se movimentando ali na memória. Vimos que os ponteiros inicializam com valor nulo, diferente Das variáveis. E aí nós vimos também o que que acontece quando a gente tenta acessar um lugar que a gente não deveria, que se esse lugar de memória fizer parte do nosso programa, a gente vai conseguir acessar, não vamos ser barrados, mas se ele não fizer, o nosso sistema operacional vai simplesmente interromper a execução do nosso programa. Depois disso, nós não vimos nenhum conteúdo novo, né? A
gente voltou em conteúdos anteriores e fizemos uso dessa declaração, desse referenciamento e dessas operações para compreender melhor algumas coisas. Dentre essas coisas, nós vimos o que é um arrei, o que o operador Chaves realmente está fazendo. A gente viu que a gente poderia tá fazendo aí uso dessa operação de descerenciamento, dessa operação com ponteiros, somando inteiros aí ao nosso endereço. Nós vimos a diferença de passagem por valor e passagem por referência, que é basicamente ao invés da gente estar passando o conteúdo de uma variável que vai ser copiado quando a gente entrar na nossa função,
a gente vai passar esse endereço para uma variável da nossa função. E aí quando a gente passa esse endereço, a gente chama de passagem por referência, porque você vai conseguir modificar a variável de uma outra função, já que você recebeu o endereço, né? Antes da gente aprender ponteiros, a gente só conseguia acessar uma variável pelo nome. Agora a gente consegue acessar ela pelo endereço também com essa operação de desreferenciamento. Então essa daqui é uma aplicação importante do que nós aprendemos sobre ponteiros. Nós voltamos o nosso código lá de funções, arrei como um parâmetro e Aí
a gente entendeu que ele era, na verdade uma passagem por referência. a gente estava criando um arrei, a gente estava enviando o endereço do primeiro elemento do nosso arrei. E aí, pessoal, a gente poderia inclusive, né, trocar esse nosso cabeçalho da nossa função e escrever dessa maneira aqui. Não teria problema nenhum. a gente conseguiria, mesmo escrevendo dessa maneira, recebendo esse primeiro endereço, ainda fazer uso dos colchetes. Funcionaria normalmente. Voltamos lá no nosso código de strings como parâmetros, quando a gente quer passar uma string para uma função. E aí nós vimos que quando a gente tá
passando uma string, a gente também tá passando esse primeiro endereço. Mas a gente não precisa se preocupar em enviar o tamanho, porque nós vamos interromper a nossa execução quando a gente encontrar esse caractere especial que indica o final de uma string. Da mesma maneira que eu poderia ter no meu cabeçalho declarado de maneira explícita que a gente tá recebendo um ponteiro simplesmente um endereço, o que não afetaria a nossa execução. O nosso programa continuaria funcionando normalmente. Depois a gente voltou lá em outro código também que a gente já havia visto, que é o do retorno
de um arrei. A gente agora não está mais passando um arrei, a gente está retornando o arrei. E aqui a nossa função está criando um arrei. E aí ela retorna, né? Ela cria usando a palavra estática para que esse espaço de memória ali não seja esquecido, não seja tido como um espaço que está vazio, né? E aí a gente retorna esse primeiro endereço e conseguimos fazer uso na nossa função main sem nenhum problema. Agora, uma observaçãozinha, se a gente esquecesse de fazer uso dessa palavra estática aqui e retornasse esse endereço, a gente ainda conseguiria estar
acessando e Modificando. Porém, no nosso código, a gente cria uma variável logo em seguida, por exemplo, a variável i. E aí vai que essa variável aí foi criada em alguma dessas posições. Isso poderia acontecer se a gente não utilizasse a palavra estátic. Então quando a gente sai da nossa função, é como se no nosso desenho isso aqui tivesse deixado de ser rosa. O conteúdo fica, mas esse espaço vai ser tido aí como um espaço que a gente poderia tá vindo aí construindo uma nova variável e essa variável aí poderia est alterando esse valor. Então a
gente tem que usar a palavra static. E antes da gente ver os ponteiros para registros, né, eu quero só colocar essa imagem, porque eu quero deixar bem claro a importância dessa diferenciação entre passagem por valor e passagem por referência. Isso já foi explicado para vocês, mas com certeza vale a pena a gente dedicar aí um slidezinho, uma imagem diferente dentro do nosso curso para reforçar isso para vocês. Na passagem pro valor, então a gente copia os valores das variáveis indicadas na chamada para variáveis da nossa função. Na passagem de por referência, a gente vai copiar
o endereço das variáveis indicadas na chamada para essas variáveis aí da nossa função. Na passagem por valor, a gente alterar esses parâmetros, né? alterar esse conteúdo que a gente recebeu, essa variável que a gente recebeu, não vai modificar o conteúdo das variáveis indicadas na chamada dessa nossa função. Já na passagem por referência, essas alterações vão afetar diretamente as variáveis indicadas na chamada. Aqui a passagem pro valor aceita constantes Literais na chamada, ou seja, se a gente tá chamando uma função que espera receber o inteiro, a gente pode simplesmente colocar o valor lá dentro. E na
passagem, por referência, nós não vamos aceitar constantes literais. Na verdade, na verdade a gente aceita constantes literais, sim, desde que essa constante literal aí seja um hexadecimal, um endereço na memória. Porém, você não vai saber inserir um endereço específico da memória que você quer acessar de cabeça, passando ali uma constante, escrevendo no seu código. Então, estamos deixando aqui que a passagem de preferência não aceita constantes literais na chamada. Agora, ponteiros para registros. Ponteiros para registros eles vão ser utilizados aí sem problema nenhum. da mesma maneira que a gente faz uso aí de qualquer outro. Só
que quando a gente fala aí de ponteiros para registros, né, para que a gente não tenha que ficar utilizando aí a operação de desreferenciamento entre parênteses e depois usando o ponto, a gente pode usar esse operador traço maior. Vou mostrar isso para vocês aqui no código. Então, percebam que aqui a gente está criando o nosso tipo ponto com P maiúsculo. a gente está criando a nossa variável ponto com P minúsculo, né, que é desse tipo ponto que a gente criou anteriormente. Tem as coordenadas X e Y. Depois nós estamos criando o ponteiro P, que vai
armazenar o endereço de uma variável do tipo ponto. E nesse caso, tá Armazenando o endereço dessa variável que a gente criou aqui em cima. E aí, pessoal, a gente pode estar realizando a impressão, né, claro, fazendo uso dessa nossa variável. Ou a gente pode também fazer uso do nosso ponteiro, tá? E aí fazendo uso do nosso ponteiro e da operação de desse referenciamento, a gente faria da seguinte maneira, descerenciaríamos o nosso ponteiro, depois acessaríamos ali o atributo X, faríamos o mesmo aí pro nosso atributo Y. E aí, quando a gente tá trabalhando com registros para
que a gente não tenha que fazer dessa forma, a gente pode utilizar esse operador aqui, que é o traço maior. Aqui tá faltando só um parênteses. Vamos executar aqui para vocês verem que na linha 13 e na linha 14, né, a semântica, o funcionamento ele é o mesmo, mas a gente tem essa nova maneira aí de estar escrevendo. Criar o nosso arquivo 11 ponteiro para registros ponto C. Vamos dar uma olhada agora em arreis de ponteiros, né? A gente também pode construir um arrei, ou seja, um conjunto de variáveis em sequência na nossa memória que
vão armazenar endereços. E isso nada mais é do que um arrei de ponteiros. Vamos criar aqui então a variável X com valor 10. Vamos criar o arrei de inteiros Y com dois elementos. E aí a gente pode já até atribuir os valores, colocar 20, 30. E vou fazer o seguinte, para testar o nosso conhecimento em ponteiros, eu vou criar uma rei de ponteiros de tamanho dois e vou armazenar na posição zero o endereço da variável X e na posição 1 Y. E aí, pessoal, será que eu posso fazer isso? Será que isso daqui faz sentido?
Vou mostrar esse código para vocês no desenho e aí vocês me dizem se faz sentido ou não. Criei a variável X, né? Ela tem o conteúdo 5 e também criei esse arrei com dois elementos. Vou colocar aqui lado a lado. Nele a gente tem 20 e 30. E temos o nome Y. Depois eu criei um arrede ponteiro de nome P. Vou colocar os tipos aqui. int 2 pra gente saber que é uma rei de inteiros. E aqui pra gente saber que é um arrei de ponteiro para inteiro. E aqui dentro eu fiz o seguinte, eu
fiz a minha posição zero, receber o endereço da variável X e eu fiz a minha posição receber Y. e Y, pessoal, nada mais é do que o primeiro endereço, o endereço da posição zero do nosso arrei. Então, agora dentro desse meu arreio de ponteiros, o elemento zero aponta pra variável X e o elemento um aponta pro meu arreiros Y. Vou deixar comentado pra gente. Bom, pessoal, agora que a gente já entendeu o que tá acontecendo na nossa memória, a gente precisa aprender a estar acessando esses endereços que estão salvos no nosso arrei e modificando ou
lendo esse conteúdo. E nós vamos fazer isso agora no código, apenas lendo o conteúdo, mas a gente poderia também estar alterando os valores ali, salvos. Primeiramente vamos realizar um print f de p0. E aqui eu quero que você pare e analise e imagine o que nós vamos encontrar. E o que nós vamos obter acessando a posição zero do nosso arrede de ponteiras vai ser um endereço. A mesma coisa vai acontecer paraa nossa posição um. Então vamos Executar esse nosso código e obtivemos aqui dois endereços. Agora, o que será que acontece caso eu disreferencie esse endereço
salvo dentro do meu arreio de ponteiros? Nesse caso, a gente vai estar acessando então o conteúdo da minha variável X ou o conteúdo do primeiro elemento do arrei Y. Como que está armazenado? É um inteiro. Eu vou utilizar aqui o porcentagem I. E obtivemos o que a gente já esperava, né? O valor 10. Vou até alterar para cinco para ficar igual ao nosso desenho. E o primeiro elemento do nosso arreio y que tem o valor 20. Bom, mas e se a gente quiser acessar a segunda posição do nosso arre y, sendo que a segunda posição
do nosso arrei de ponteiros tem o endereço da primeira posição do nosso arrei y, o que que a gente pode estar fazendo para est acessando aí essa variável que está armazenando o número inteiro 30? Pra gente fazer isso, a gente vai precisar lembrar das nossas operações com ponteiros. Então eu sei que eu estou neste endereço aqui e se eu somar mais um, eu vou vir para este endereço aqui. Então eu posso pegar esse conteúdo do meu ponteiro, somar mais um a ele e aí eu vou estar neste espaço aqui de memória. Daí então eu posso
estar fazendo a minha operação de desse referenciamento para est acessando este Valor. Então vamos fazer isso. P1 + 1. Nesse caso aqui, a gente quer encontrar aquele endereço primeiro e depois desse referenciar. Então aqui a gente tem o endereço do primeiro elemento. Vamos adicionar um e a gente vai encontrar o endereço do segundo elemento. Depois a gente vai desferenciar. Aqui faltou um parênteses. Bora executar o nosso programa para ver se é isso que vai acontecer. Bom, foi exatamente isso que aconteceu. Vou deixar aqui também o P1 + 0, né? Mas aí a gente vai tá
obtendo a mesma coisa ali da linha 16. Mas para que fique mais claro aí caso você venha acessar esse código futuramente para relembrar esse conteúdo. Pessoal, existe também uma outra maneira da gente estar acessando essa posição sem estar realizando essa soma, tá? que é fazendo uso dos colchetes. Se vocês lembrarem bem, essa operação de soma, ela sempre pode ser substituída pelos colchetes. E quando a gente tá trabalhando com uma arreiros, a gente pode fazer o mesmo. Então, percebam que é isso que está acontecendo aqui. Quando a gente faz uso desses dois colchetes, basicamente nós estamos
acessando esse primeiro endereço com o conteúdo do nosso primeiro colchete e acessando a posição com o conteúdo do nosso segundo. Vamos salvar esse nosso arquivo e fazer um novo código pra gente poder fixar bem o uso dos colchetes. Muito bem. Antes da gente escrever esse nosso novo código, eu vou desenhar ele aqui e quando eu terminar eu explico para vocês. Ok? Então, a gente vai criar aí no nosso código o arrei número zero, um arrei de inteiros que vai armazenar 10 valores inteiros. Quando a gente cria, a gente vai ter ali algum lixo na nossa
memória. Depois a gente vai criar o arrei um, que também vai ter 10 espaços para armazenar inteiros, o arrei dois, que também vai ter 10 espaços para armazenar inteiros, e o arrei três, que também vai ter 10 espaços para armazenar inteiros. Ou seja, ao total a gente vai estar criando aí quatro arreis cada um com 10 espaços. Essas reticências em amarelo estão aqui para ilustrar que nós não teremos só três elementos, mas sim 10 elementos em cada um desses nossos arreis. Depois que a gente fizer isso, nós vamos criar o arreio ponteiro, tá? E esse
arreio ponteiro, ele automaticamente já é inicializado ali com os valores zero. Então, bora fazer isso no código. Intrei zero vai ter 10 elementos. Intrei 1 vai ter 10 elementos. Intrei 2 também vai ter 10 elementos. E in arrei 3, que também vai ter 10 elementos. Agora nós vamos criar o nosso arrei de ponteiros, chamado arrei ponteiro. E ele vai ter quatro espaços. Nesse momento, se a gente compilar esse nosso programa e executar, ele vai fazer a reserva dos espaços da maneira que a gente havia ilustrado. Mas agora eu vou alterar O conteúdo do meu arrei
ponteiro. Vou fazer com que a minha posição zero receba o primeiro endereço do meu arrei. Vou fazer com que a posição um receba o endereço do arrei 1. Vou fazer com que a posição 2 receba o endereço do arrei 2. Vou fazer com que a posição 3 receba o endereço do arrei 3. Então o que tá acontecendo é que a gente vai est alterando aí os valores armazenados nesse nosso arrede ponteiro. Se a gente der uma olhada aqui, isso aqui é hipotético, tá? Aqui tá tendo um salto aí de endereço do memória de dois em
dois. Vamos alterar aqui. OK. Agora, pessoal, eu quero realizar a impressão de todos esses lixos, de todos os meus arreis, porém utilizando somente o meu arrei ponteiro. E eu posso fazer isso fazendo uso de uma iteração. Mas antes da gente escrever nossa iteração, vamos escrever aqui manualmente para que fique bem claro para vocês o que tá acontecendo. Eu vou acessar o meu arrei ponteiro na posição 0 e vou imprimir esse valor. Depois eu vou acessar esse arrei ponteiro na posição 01 e vou imprimir esse valor. Em seguida, eu vou acessar na posição 02 até chegar
à posição 09, imprimindo todos os 10 elementos do meu arrei zero. Depois eu vou fazer a mesma coisa pro arrei 1, pro arrei 2 e pro arrei 3. Percebam que quando a gente tem um arrei de ponteiros que armazena um outro arrei, nós estamos falando aí de matrizes, ou seja, arreis Bidimensionais. O nosso primeiro valor vai realizar um salto maior na nossa memória, né? E o nosso segundo valor vai fazer aquele salto variável a variável. Pessoal, todas as vezes que vocês estiverem com dificuldade de imaginar aí a reis multidimensionais, lembrem-se sempre que na prática, na
nossa memória, as coisas estão lado a lado. Seja um arreio unidimensional ou um arreio bidimensional, as coisas vão estar lado a lado. A única diferença é que no arreio bidimensional a gente vai ter uma chave que vai nos permitir saltar mais do que uma posição. Nesse caso aqui, nesse meu ponteiro, uma chave que vai me permitir saltar aí 10 posições. Então vamos lá realizar essa impressão. Vou fazer um forquanto o i for menor do que 10, onde o i vai ir de 0 a 3. E vou fazer um outro for com a variável J, que
vai inicializar em zero. E J vai ir de 0 a 9. Vou colocar aqui na nossa impressão valor de IJ para ficar melhor de visualizar. E vamos imprimir o conteúdo daquela posição na memória que vai ser algum lixo ali. Então, ar ponteiro, posição I e na posição J. Vamos colocar também ali os valores, né, que eu havia dito. Muito bem, vamos executar esse nosso código. Esquecemos de colocar ali a quebra de linha, né? E percebam que agora a gente está imprimindo aí esses nossos quatro arreis somente do nosso ponteiro. É claro, pessoal, que eu poderia
fazer o mesmo utilizando a operação de desreferenciamento e de soma. Então a gente acessaria ali o primeiro endereço, depois a gente estaria aí somando esse endereço com o valor de J e depois a gente estaria aí desreferenciando. Vamos executar esse nosso código. Percebam que o comportamento é exatamente o mesmo. Espero que agora tenha ficado mais claro para vocês o uso dos colchetes. Vamos salvar esse nosso arquivo. Bom, pessoal, agora a gente vai aprender sobre ponteiros genéricos. Esse é um conteúdo que as pessoas têm dificuldade de aprender, principalmente quando elas estão tendo o primeiro contato com
ponteiros. Mas nós vamos ir bem devagar, passo a passo, e eu tenho certeza que se você se dedicar, você vai conseguir entender porque que os ponteiros genéricos existem e vai entender também como fazer uso deles. Antes de mais nada, a gente precisa entender porque que os nossos ponteiros têm um tipo. Reparem bem, aqui eu tenho uma variável do tipo inteiro. Ela ocupa 4 bytes. Então, separei aqui com essas linhas vermelhas, 1 2 3 4. Eu tenho uma variável do tipo double, que ocupa 8 bytes, tá? O tamanho que cada variável ocupa na memória, a gente
viu quando a gente aprendeu sobre variáveis. Então, trouxe aqui pra gente relembrar. Double ocupando 8 bytes, intupando 4 bytes. Já os caracteres eles ocupam apenas 1 byte, porém quando a gente cria um ponteiro, todo ponteiro possui o mesmo tamanho. Aqui eu tô dizendo que os ponteiros eles têm 4 byte. Isso vai depender, pessoal, da arquitetura que você tá utilizando, do seu sistema operacional. Mas assim, pr você ter uma visão geral, geralmente quando a gente tá falando de sistemas 32 bits, esses endereços vão ocupar 4 bytes. Se a gente tiver falando de sistemas de 64 bits,
eles vão ocupar 8 bytes. E por que que o tamanho dos nossos ponteiros sempre vai ser o mesmo? porque eles sempre vão armazenar endereços e esses endereços sempre vão ter o mesmo tamanho. E já que os nossos ponteiros sempre terão o mesmo tamanho, qual é a necessidade que nos leva a declarar o tipo deste ponteiro quando a gente cria ele? Pessoal, é necessário que você informe o tipo desse ponteiro, o que está dentro desse endereço armazenado, não para determinar aí qual vai ser o tamanho, né, da memória lucada para armazenar esse endereço, mas sim para
que você consiga estar realizando a operação de desreferenciamento. Percebam, por exemplo, nesse código aqui, onde a gente tá criando o espaço na memória A, o espaço na memória B, e a gente cria esses ponteiros que a gente precisa indicar esse tipo, porque quando a gente está desreferenciando, a gente precisa saber atéonde a gente vai ir. Um Inteiro, por exemplo, a gente vai olhar ali os 4 bytes. Num double, a gente vai olhar 8 bytes. Mas e se eu dissesse para vocês que existe uma maneira da gente armazenar esses diferentes tipos em um só ponteiro? E
esse ponteiro a gente vai chamar de ponteiro genérico ou ponteiro do tipo vazio. E isso é possível, já que a gente vai estar armazenando o endereço. Então aqui, ao invés de eu criar ponteiros do tipo inteiro, eu criei ponteiros do tipo void. Vou atualizar o nosso desenho para vocês entenderem um pouco melhor. E agora a gente tem uma ideia visual do que tá acontecendo naquele nosso código. A gente tem duas variáveis do tipo inteiro e a gente tem dois ponteiros do tipo vazio. Até aqui, 1000 maravilhas. O problema vai acontecer quando a gente tentar descerenciar
esse ponteiro. Por quê? Porque a gente tem esse primeiro endereço e ele não sabe me indicar se é do tipo inteiro, se é do tipo double, até onde é necessário está lendo. Então, caso eu tente realizar essa minha operação aqui de desferenciar o meu ponteiro A e o meu ponteiro B, eu vou obter um erro. Vou tentar executar esse nosso código e ele nem roda. Porque como é que eu vou saber aonde parar? Eu posso ter que parar somente no primeiro byte, se for um caracter. Eu posso ter que parar no quarto byte, se for
inteiro, eu posso ter que parar no oitavo byte se for um double. Então a gente pode sim fazer uso desses ponteiros vazios, mas na hora de desreferenciar a gente precisa indicar aí aonde nós vamos parar. E para fazer isso, a gente vai dar um cast. A gente vai converter um Ponteiro do tipo vazio para um ponteiro do tipo que ele deveria realmente ser. Então, dá uma olhada em como seria. A gente iria converter para um ponteiro do tipo inteiro. Reparem que a sintaxe é exatamente a mesma que a gente vem utilizando aí para converter qualquer
coisa, né? Se a gente quisesse converter para um float, seria sim. Então, para um ponteiro de float, dessa maneira. E aí, realizando essa conversão, né, do conteúdo dessa minha variável, eu posso descerferenciar esse endereço e realizar a minha operação sem nenhum tipo de problema. Então, olha só, ent. Agora esse meu código aqui vai executar corretamente. Pessoal, qual a aplicação disso? Você vai conseguir, por exemplo, criar um só ponteiro que pode apontar para diferentes tipos. Vou salvar esse nosso arquivo e mostrar isso para vocês. Vou criar um inteiro, vou criar um float, vou criar um caracter
e somente um ponteiro, esse meu ponteiro aqui vazio. E agora fazendo uso somente desse meu ponteiro, observem só o que eu vou fazer. Vou imprimir o valor de A, mas para isso acontecer eu vou fazer com que o meu ponteiro aponte para o endereço de A. E aí eu vou realizar uma conversão para o inteiro na hora de fazer o desse referenciamento. Depois eu vou fazer com que esse meu ponteiro aponte para o endereço de F e vou converter para um float e vou fazer com que esse mesmo ponteiro aponte para o endereço de C.
e vou realizar essa conversão para um Ponteiro do tipo char, só para melhorar a nossa impressão, pra gente não confundir. Prontinho, a gente tem aqui o mesmo ponteiro que sendo referenciado nos mostra o valor de um inteiro, o valor de um float e o valor de um caracter. Aqui são todos inteiros porque eu acabei utilizando aqui o porcentagem D, né? O correto seria usar o porcentagem F e o porcentagem C. Agora tudo correto, pessoal. O que que tá acontecendo? Eu tenho então um inteiro, eu tenho um float que também ocupa 4 bytes, valor 2.0 e
eu tenho um caracter que ocupa um só byte. Ponteiros, eu só criei um e eu tô alterando esses endereços. Primeiro, esse meu ponteiro tem o endereço da minha variável A. Depois, esse meu ponteiro tem o endereço da minha variável F. Depois esse meu ponteiro tem o endereço da minha variável C. E independentemente do tamanho dessas minhas variáveis, somente um ponteiro é utilizado. Como eu disse para vocês, a primeira vez que a gente se depara com essa ideia de ponteiros genéricos é um pouco complicado de entender, mas não tenham medo, desenhem e tentem compreender o que
está acontecendo caso vocês se deparem com algum código que contenha ponteiros vazios. Lembrem-se sempre que o problema do ponteiro vazio é na hora de desreferenciar. E agora, pessoal, só nos falta aprender sobre ponteiros para ponteiros. Mas antes da gente dar uma olhada nesse conteúdo, vamos passar rapidinho em tudo que a gente viu. Até o nosso arquivo cinco, nós vimos a Declaração, o desseferenciamento e operações com ponteiros. A gente viu também o comportamento dele na memória quando a gente tenta acessar lugares que a gente não deveria. Uma motivação para que a gente esteja jogando dentro da
regra. Nos arquivos seguintes, a gente viu aplicações do conteúdo que a gente havia aprendido, né, utilizando ponteiros para apontar para reis. Vimos que os arreis já faziam uso de ponteiros e uso dessas operações. Vimos a diferença de passagem por valor e passagem por referência. Quando a gente passa ali um endereço para ser acessado paraa nossa função, nós vimos que já que a reis na verdade são ponteiros ali pro primeiro endereço, a gente pode estar passando um arrei para uma função. A gente viu que a gente pode fazer o mesmo ali com uma string e vimos
também como fazer o retorno de um arrei. Eu disse para vocês que a gente voltaria nesse nosso código e aprenderia a escrever ele de uma maneira melhor. Mas logo em seguida comecei a ensinar sobre ponteiros para registros, a reis de ponteiros e ponteiros genéricos, que são ponteiros que são capazes de apontar aí para variáveis de diferentes tipos. Agora, o que nos falta é aprender ponteiros para ponteiros, mas antes disso, a gente vai refazer esse nosso código paraa gente poder estar praticando um pouquinho mais sobre ponteiros e entrar nesse assunto de ponteiros para ponteiros com um
pouquinho mais de maturidade. Então bora lá, bora refazer esse nosso problema aí de retornar um arrei por referência para vocês verem aqui o que que tá acontecendo. Eu tô chamando aqui na minha função main, essa função obter array, ela cria um array e me retorna esse arreio. Me manda aí um ponteiro pro endereço inicial, eu pego esse ponteiro Na minha função main. Porém, eu gostaria de ter também o tamanho nesse meu código aqui. Magicamente ali na minha função main, eu sei que esse arreio vai ser de cinco elementos, mas eu gostaria de ter uma maneira
de verificar isso. Então, para resolver esse problema, eu vou propor que a gente faça algo diferente. A gente cria um tipo que eu vou chamar de arrei e tamanho, colocar minúsculo mesmo. Dentro desse meu tipo, eu vou ter o ponteiro para um arrei. vou ter o tamanho que vai ser o número inteiro e o nome desse meu tipo vai ser arrei tamanho. Bom, e depois de ter criado esse nosso vou criar uma variável do tipo arrei tamanho de nome AR. O que eu fiz aqui, eu vou deixar aqui desenhado na nossa memória. Lembrando que quando
a gente cria um tipo, a gente não cria um espaço na nossa memória, a gente só cria um tipo. E aí, a partir do momento que eu crio a minha variável, aí sim vou colocar o nome dela aqui do lado pra gente. Pois bem, agora a gente tem que fazer a nossa função. Vamos dar uma olhada em qual era o nome dela. Obteri. Essa função obter arrei, ela vai retornar um arrei tamanho. terrei, não vai receber nenhum parâmetro. E aqui dentro a gente vai criar uma variável do tipo a rei tamanho. Vou chamar de ar
também. Então, tá aqui essa nossa variável, ela é acessível na função main e essa daqui é acessível na nossa função obter a rei. Pessoal, percebam que essas variáveis que a gente criou, por a gente ter um ponteiro ali, ele vai ser inicializado automaticamente com valor zero, mas o Nosso inteiro, ele é só um lixo. Então a gente ainda não criou o nosso arrei. Então vamos criar o nosso arreio aqui dentro usando a palavra estática, arrei de tamanho 10. E vamos colocar aí no nosso desenho. Colocar aqui também o nome, né, e o tipo. Agora nós
criamos esse nosso arrei. Ele é estático, mas ele só é acessível dentro da nossa função obter arrei. Para a gente poder acessar isso, a gente tem que salvar esse endereço, né? E aonde que a gente vai salvar? Vamos salvar dentro dessa nossa variável aqui. Vamos alterar ali o nosso ponteiro para que ele aponte para esse nosso arreio que a gente acabou de criar. Para fazer isso, a gente usa o ponto para acessar esse atributo. A R vai receber a rei. E a gente ainda precisa est alterando a quantidade, ou seja, o tamanho do nosso arrei
que vai ser 10. Fizemos a alteração ali. Agora basta que a gente retorne essa nossa variável return a ar. E aí a gente pode estar recebendo aqui dentro dessa nossa variável AR o que essa nossa função vai estar retornando. Pessoal, percebam que quando a gente retornar esse nosso valor, esse nosso arrei vai continuar existindo. Porém, o conteúdo dessa nossa variável vai deixar de existir, mas a gente vai est copiando o conteúdo dela para essa outra variável que a gente criou na nossa função main. Ou seja, aqui vai virar 10 e aqui a gente vai ter
esse endereço acessível a partir da nossa função main. E aí a gente vai conseguir fazer uso do nosso array. Vamos salvar o arquivo e comparar ela com o anterior. Então aqui a gente criava esse nosso arrei utilizando a palavra estático e retornava um ponteiro, esse endereço aí que ficava salvo em um ponteiro. Porém, como a gente só pode retornar uma coisa de cada vez, eu criei esse meu novo tipo e dentro desse meu novo tipo eu tenho ali um ponteiro e também tenho o tamanho. Agora eu não estou mais retornando um endereço e sim estou
copiando o conteúdo dessa variável. Agora, prestem bastante atenção. Eu já havia mostrado isso para vocês no código anterior, né? Por isso que eu coloquei aqui o arrei do e fiz as modificações do meu arrei do e depois ali realizei a impressão. Se eu vier aqui e criar uma variável arrei tamanho ar e chamar essa minha função obter arrei, nós não vamos criar um novo arrei porque apesar dessa função está sendo chamada várias vezes, quando a gente tem a palavra static, essa variável só é alocada no início do programa e todas as outras vezes que a
gente chama essa nossa função, ela vai utilizar essa mesma instância dessa variável, ou seja, aquele mesmo espaço na memória. Então, a gente só tem um arrei e a gente vai aprender a resolver isso em breve, mas já fiquem cientes deste problema. E aqui a gente poderia acessar aí normalmente essa nossa variável ar, mas percebam que não é porque a gente utilizou aí um novo tipo que a gente vai usar aquele operador de setinha porque não é um ponteiro. Então a gente usa aqui o ponto mesmo. Então tá dito isso daí vamos fazer a impressão desse
nosso arrei. A gente pode fazer então uma iteração que vai ir de até o tamanho desse nosso arreio que tá armazenado dentro desse nosso tipo arrei tamanho. a gente faz o Nosso incremento e imprime esse nosso arreio. Coloquei o índice para nossa impressão ficar melhor de visualizar. E aí, se a gente acessar o nosso ponteiro AR.ARar, a gente vai est pegando a primeira posição e a gente pode fazer uso dos colchetes. Vamos executar pra gente ver o que que tá acontecendo. E aqui eu tive um probleminha só porque eu não tô no meu arquivo main.
Então vamos voltar pro nosso arquivo main, executar. Faltou a gente colocar aqui, né, nosso porcentagem D ou porcentagem I. Os valores que a gente pegou, todos estavam iguais a zeros, mas isso daqui nem sempre vai acontecer. A gente poderia também, né, estar vindo aqui, depois de acessar esse nosso endereço, somar com o valor da nossa variável I. Então, estar desreferenciando daria na mesma. E agora, pessoal, para finalizar o nosso estudo de ponteiros, a gente vai ver aí ponteiros para ponteiros. Vou criar aqui uma variável X que vai receber o valor 10. Vou criar um ponteiro
para essa minha variável. Vai receber o endereço dessa variável. E vou criar um ponteiro para ponteiro, que vai receber o endereço do ponteiro que eu havia criado ali em cima. A gente pode fazer com que isso daqui aconteça. E na nossa memória nós teremos algo assim, um endereço do tipo inteiro, um endereço que é um ponteiro e um ponteiro para um ponteiro. O que que eu gostaria que vocês pensassem quando vocês se deparassem com ponteiros para ponteiros? Que na hora de criar uma variável que vai armazenar o endereço de um ponteiro, a gente precisa fazer
uso De dois asteriscos. E na hora de acessar, na hora de realizar a operação de desse referenciamento, a gente tem que ter sempre em mente que quando a gente faz o desse referenciamento, nós podemos simplesmente considerar que nós estamos trabalhando com a variável cujo endereço está armazenado. Então, se eu desferenciar esse meu ponteiro para ponteiro, eu posso fingir que eu estou trabalhando com este meu ponteiro aqui. E se eu desferenciar novamente, eu posso fingir que eu estou trabalhando com a minha variável. Então vamos dar uma olhada nisso. Vamos imprimir o conteúdo do meu ponteiro, que
vai ser um endereço. Vamos imprimir o conteúdo do meu ponteiro desse referenciado. E aí nós vamos obter o que vai estar dentro do ponteiro um. E vamos imprimir o conteúdo do meu ponteiro para ponteiro desreferenciado duas vezes. Então, na primeira vez que eu estou desreferenciando, eu vou imprimir este conteúdo aqui. Na segunda vez, eu vou imprimir um inteiro, que é este conteúdo aqui. E vamos executar esse nosso código. Vou adicionar uma quebra de linha para facilitar a nossa visualização. Que eu quero que vocês tenham em mente são essas duas operações. operação de declaração do ponteiro
para ponteiro, que vai exigir de você fazer uso do asterisco duas vezes, porque agora a gente não vai estar apontando para um inteiro e sim para um ponteiro que aponta para um inteiro. E também lembrar que na hora de fazer operação de dferenciamento, quando a gente usa o desse referenciamento, a gente pode fingir que a gente tá trabalhando com o endereço armazenado no nosso ponteiro. E se a gente fizer isso uma vez, a gente vai est trabalhando ali com o ponteiro. Se a gente fizer duas vezes, a gente vai estar trabalhando com a nossa variável
inteira. Fiquem tranquilos. Eu sei que, apesar da explicação ser curta, não é tão trivial tá entendendo e pensando aí sobre o uso de ponteiros para ponteiros, mas futuramente a gente vai se deparar com aplicações que envolvem isso e aí vocês vão entender um pouco melhor. Muito bem. Muito bem. Percebam que agora a gente já está em outro lugar da nossa estrada, em uma parte que muitos consideram difícil do nosso caminho, mas eu garanto que aqueles que seguirem as placas vão chegar ao seu destino final. E o que elas estão nos indicando é que chegou o
momento de praticar realizando a nossa lista sete de exercício. Então, pausa esse vídeo, vai pra descrição e eu vejo vocês o nosso próximo assunto. Muito bem, pessoal. Criar aqui a nossa pasta desse nosso próximo assunto, a locação dinâmica. E vamos começar entendendo por que a gente precisa aprender sobre a alocação dinâmica. Bom, vamos supor que você tem que criar um programa que deve cadastrar os dados de vários clientes, nome, endereço, CPF, e-mail. Uma opção seria você criar um arreio do tipo cliente e armazenar os dados de todos os clientes nesse arrei. Porém, um arrei precisa
ter um tamanho pré-definido. Se você defini um tamanho muito pequeno, você vai limitar o número de clientes que podem ser cadastrados. E se você definir um tamanho muito grande, vai desperdiçar a memória. E se você pensou em criar uma função que vai criando um cliente de acordo com a necessidade, saiba que até então isso não é possível. A gente viu isso quando a gente viu ali o nosso exemplo número 10 do arquivo de ponteiros. Se a gente Criar uma função que utiliza palavra estática, para que o conteúdo criado por essa função se mantenha existente na
nossa memória, mesmo saindo dela, caso a gente chame essa função novamente, nós vamos estar simplesmente acessando esse mesmo espaço e não criando um novo. Em situações como essa, o ideal seria a gente ter alguma forma de criar variáveis à medida que elas se tornam necessárias durante a execução do nosso programa, a partir do momento que o usuário clique ali em cadastrar um novo cliente. E é claro que existe uma solução para esse problema. Essa solução é chamada locação dinâmica. Você pode observar no seu computador, se você estiver no Windows procurando aí por Task Manager, você
vai encontrar aí todos os programas que estão em execução dentro da sua máquina. Percebam que a quantidade de memória que eles utilizam vai variando com o tempo. Então, se você abrir o seu navegador, por exemplo, a medida que você for abrindo novas abas, ele vai consumir mais memória. A mesma coisa vai acontecer pros programas que a gente vai estar criando agora. Eles vão começar ocupando uma quantidade de memória e à medida que a gente for criando novas variáveis, eles vão ocupar uma quantidade maior. Se você estiver no Linux, você pode utilizar o comando top. A
diferença é que por padrão comando top, ele vai te mostrar a quantidade de memória em porcentagem, tá? E se você estiver no Mac, você pode procurar aí por Activity Monitor ou monitor de atividade. E aí você clica na opção de memória e ele vai listar para você a quantidade de memória que cada programa está utilizando. Percebam que essa quantidade varia rapidamente. Isso acontece por causa da alocação dinâmica. Antes da gente aprender os comandos que a gente vai usar, vamos entender um pouquinho melhor o que que tá acontecendo. Toda vez que a gente escreve um código,
compila aquele nosso código e gera um executável, esse nosso executável, ele vai ser dividido em quatro partes, em quatro partes dentro da nossa memória RAM, a partir do momento que a gente executar esse nosso programa. Então, lá no comecinho, lá atrás, quando a gente estava aprendendo aí sobre a execução de um programa, a gente viu que a primeira coisa quando a gente inicia a execução é pegar esse arquivo executável e carregar algumas coisas pra nossa memória RAM. Aí a gente colocou lá que a gente iria estar carregando ali as instruções a serem executadas. A primeira
parte é onde as instruções do seu programa são armazenadas. Imagine que a gente tem ali várias linhas de código, depois a gente traduz isso para uma linguagem de máquina e gera um executável. Nessas linhas de código existe uma sequência a ser seguida. Primeiro você cria uma variável, depois você imprime uma mensagem, depois você aguarda o usuário inserir um input no terminal. Então é nessa primeira parte que essas instruções ficam armazenadas. Geralmente esse segmento ele é só de leitura para prevenir a modificação ascendental das instruções. Depois nós vamos ter um espaço de memória que vai armazenar
todas as variáveis globais e estáticas do nosso programa. Ou seja, aquelas variáveis que a gente criou fora de qualquer uma das nossas funções e aquelas variáveis que a gente criou utilizando a palavra estática. Quando a gente gera o nosso arquivo executável, quando a gente compila, a gente já consegue saber quantas variáveis estáticas e globais vão existir no nosso programa. Fica tranquilo que a gente vai dar uma olhada melhor nisso. E a gente tem também a Stack. Essa região, ela é utilizada para gerenciar chamadas de Funções, incluindo o armazenamento de variáveis locais, os parâmetros das funções
e retornos. Então essa parte aqui da nossa memória é onde as nossas variáveis que não são estáticas e globais vão ficar as variáveis que a gente cria normalmente dentro de alguma função. E à medida que a gente chama novas funções, nós vamos estar armazenando aí variáveis, né, dentro dessa região que também é conhecido como região de pilha. E o tamanho dessa pilha, pessoal, geralmente ele é determinado pelo sistema operacional, mas muitas vezes ele pode ser configurado. Inclusive no Linux, se você quiser, você pode utilizar o comando limit traço S. Daí você vai ver o tamanho
padrão da pilha de execução de um programa. Qual que é o problema? Como esse tamanho ele é limitado, se a gente ficar criando variáveis demais, a gente vai estourar esse nosso tamanho e aí o nosso programa vai interromper a sua execução. Pois bem, agora a gente vai aprender sobre a HIP. A HIP é uma região usada para que a gente possa alocar a nossa memória dinamicamente. Nós vamos ver essas quatro funções aqui que nós vamos usar para gerenciar a memória nessa região. O jeito um pouquinho melhor da gente estar visualizando a HIP é a seguinte.
Toda vez que a gente cria um programa, a gente compila, a gente executa, vai subir pra nossa memória. Isso daqui, as instruções que o nosso programa vai ter que executar, um espaço com todas as variáveis estáticas e globais e o espaço da pilha, que vai ter um tamanho fixo determinado pelo sistema operacional que não aumenta nem diminui. E aí é a nossa HIP. Qual que é a diferença da HIP? A HIP, sim, ela vai aumentando e diminuindo de acordo com a execução do nosso programa. Aqui eu Trouxe algumas informações. A gente não precisa ficar entrando
em detalhe, é só para entender a ideia geral. Quando um programa inicia o sistema operacional, ele aloca uma certa quantidade inicial de espaço para HIP. Por isso que ela não começa sem nada. Ela sempre vai ter um endereço inicial mínimo. E outra coisa, dependendo do sistema operacional que você vai estar utilizando, tá? Pode ser que ele vai impor limite aos nossos programas. Então eles não vão poder ocupar aí memória demais. Ele pode chegar num ponto onde o nosso sistema operacional vai falar: "Opa, você tá querendo usar memória demais, vamos interromper sua execução". vai depender do
seu sistema operacional. Pois bem, vamos entender tudo isso aí um pouquinho melhor. As instruções, eu acredito que é o mais simples da gente compreender. É a nossa sequência de código que vai ser executado. Isso aí é carregado paraa nossa memória, mas a gente tem que ver aí as variáveis estáticas e globais. Então, pra gente dar uma olhada nisso, trouxe aqui essa imagem, ó. Na linguagem C são estáticas as variáveis globais e as variáveis declaradas com o comando estático. Então, se a gente tiver aqui uma variável que está fora de qualquer numa das nossas funções, a
gente vai dizer que ela é uma variável global. Não necessariamente ela tem que ter a palavra const. A palavra const aqui serve simplesmente para indicar que essa variável não pode mudar de valor. E toda vez que a gente criar uma variável dentro de uma função com a palavra estática, ela também não vai fazer parte da dinâmica da pilha, vai estar localizada aqui neste lugar da memória, um lugar diferente. E apesar dessa função aqui conta chamadas, né, ser chamada várias vezes, aqui a gente tem uma função que retorna o inteiro e a gente tem uma variável
estática para contar quantas vezes essa função foi chamada. Então, apesar dessa função Poder ser chamada várias vezes, essa variável só é alocada no início do programa e todas as chamadas utilizam a mesma instância dessa variável. Então, assim que o nosso programa começa a executar, ele já é carregado na memória com as nossas instruções e com essas duas variáveis aqui, essa global e essa estática nesse local da nossa memória. Já as variáveis de dinâmica da pilha, elas são alocadas na memória quando a função na qual elas pertencem é chamada e elas são liberadas quando essa função
se encerra. Como uma função pode ser chamada várias vezes, a gente viu isso em funções secursivas, imagine que a gente tem um espaço de memória e a gente tá chamando essa nossa função e tá gastando esse nosso espaço, chamando, chamando, chamando, chamando, mas a gente não estoura esse espaço. Daí depois a gente começa a dar um retorno, coisa e tal, vai retornando e aí a gente chega numa parte do nosso programa que a gente começa a chamar uma outra função, uma função diferente. Essa outra função chama uma outra função e a gente retorna e por
aí vai. Apesar do tamanho da pilha ser fixo, ele vai sendo reutilizado. Aquele mesmo local da memória é utilizado para diferentes chamadas de funções. E na linguagem C são dinâmicas da pilha as variáveis não estáticas e globais, ou seja, qualquer variável declarada dentro de uma função que não tem a palavra estática. Então aqui a gente tem um exemplo. Dentro dessa nossa função aqui, fun, a gente tem duas variáveis A e B. E se a gente ficar chamando essa função aí várias vezes, novas variáveis vão ser criadas dentro dessa nossa pilha. E quando a gente retornar,
aquele espaço ali vai continuar reservado na nossa memória. Como eu disse, a pilha ela é fixa. Porém, caso a gente chame uma nova função, a gente vai tratar como se aquele espaço ali estivesse disponível para que essa outra função utilizasse ele. E se você está preocupado se você vai ou não estourar aí o tamanho da sua pilha, claro que isso pode acontecer, mas vamos fazer uma estimativazinha aqui pra gente entender quantas variáveis a gente poderia estar criando com o tamanho padrão da nossa pilha. Aqui eu estou no Linux. Esse comando só vai funcionar no Linux
ou no Mac, mas não se preocupem, só pra gente ter uma ideia geral. Eu utilizei aqui o comando limit. 8192 é a quantidade de KB que a gente tem na nossa pilha. Então, toda vez que a gente executa o nosso programa, no mínimo ali, ele vai ocupar 8 MB da nossa memória ou 8192 KB da nossa memória. Isso pra pilha. ainda vai ter ali aquele espaço reservado pras instruções e aquele espaço reservado pras variáveis estáticas e globais. E claro, aquele pedacinho da RIP, mas no mínimo aí vai ocupar 8 m por padrão. Se eu quiser
ver a quantidade de bytes, eu posso multiplicar isso daqui por 1000. Então eu sei que eu vou ter 8.192.000 bytes. Eu sei que um ponteiro ocupa 4 bytes, certo? Então vou dividir aqui por quatro pra gente ver quantos ponteiros a gente poderia ter ou quantas variáveis do tipo inteiro a gente poderia ter dentro da nossa pilha. Então seria algo aí próximo de 2.48.000, um número bem grande, tá? Agora, pessoal, e se eu tivesse um cliente que tem idade, que ocupa 4 bytes, tem altura, que ocupa 4 bytes, tem nome aqui ocupando 50 bytes, podendo ter
50 caracteres e e-mail que também pode ter 50 caracteres. Então, a gente teria ali Um total de 108 bytes para cada variável do tipo cliente. Nesse caso aqui, vamos ver quantos clientes a gente poderia ter dentro da nossa pilha sem fazer uso de alocação dinâmica. Então, a gente tem um total de 8.192.000 1000 bytes e a gente vai dividir isso aí por 108. Então, a gente poderia ter 75.000 clientes dentro da nossa pilha. É claro, pessoal, que o valor vai ser menor que isso, tá? Porque a nossa pilha, ela não serve somente para armazenar variáveis.
Então, toda vez que a gente faz uma chamada de função, vai ocupar um espaço também da nossa pilha. Mas só pra gente conseguir imaginar que a gente conseguiria ali cadastrar 75.000 clientes na nossa pilha. De 75.000 para 2 milhões, a diferença é brutal. Se a gente fizesse esse armazenamento sem ponteiras, utilizando só variáveis, a gente poderia aí criar um arrei de, por exemplo, 60.000 clientes e a gente ainda teria esse problema da nossa HIP trabalhar praticamente sempre cheia. Então, as chamadas de funções poderiam acabar via estourar e aí a execução do nosso programa seria interrompida.
A gente não precisa ficar se apegando muito a isso. Eu só quis trazer essa informação aqui para vocês, para vocês terem uma noção do tamanho da nossa pilha. E é claro, pessoal, se algum dia for necessário, e provavelmente não vai ser, tá? Quando a gente for trabalhar com muitos dados, a gente vai estar usando aí banco de dados, enfim, outras coisas. Mas caso seja necessário, a gente pode ver que aumentar o tamanho padrão da nossa pilha. Não vou fazer isso porque aí qualquer programa que a gente adicionar, ele já vai começar ocupando bem mais espaço.
Se a gente tiver utilizando o Linux e o Mac vai ser esse comando ali que eu mostrei para vocês. Muito bem. Então tudo que a gente falou até agora foi sobre o funcionamento de um programa e agora a Gente vai começar a conversar sobre a nossa RIP. Pessoal, eu disse pra vocês que a gente poderia ali ter 2 milhões de ponteiros. Por quê? Porque o funcionamento da RIP vai ser o seguinte: A gente vai ter um ponteiro na nossa pilha e aí ele vai guardar um endereço na nossa HIP que a gente vai poder estar
acessando, tudo bem? Uma outra coisa sobre a nossa RIP vão ser esses dois termos aqui, alocação e liberação. A partir de agora, o tempo de vida de uma variável, ele vai ser determinado pela sua alocação e pela sua liberação. Então, a gente vai poder reservar um espaço na nossa HIP, aumentando o tamanho dela, e a gente vai poder também liberar esse espaço na nossa hip. nossas variáveis não vão mais morrer ou deixar de existir quando a gente sair da nossa função, mas sim quando a gente liberar esse espaço de memória. E o que a gente
vai aprender para poder realizar a locação de memória vão ser só essas quatro funções aqui. Três delas são pra alocação de memória e uma delas é pra liberação. E antes da gente dar uma olhada nessas funções, eu quero falar com vocês sobre o comando size off. Pessoal, o comando size off ele serve para nos retornar o tamanho de uma variável. Então, se a gente quiser descobrir ali o tamanho da variável inteira, por exemplo, a gente pode utilizar o comando size off. Percebam que nesse nosso código aqui a gente cria a variável inteira A valendo 5.
A gente cria a variável TA, tamanho de A. E aí a gente chama esse comando size off e passa a nossa variável A. Não é necessário que você utilize parênteses, mas você pode. E dessa operação aqui, nós vamos estar obtendo o tamanho da Variável A, que é o tamanho 4. Depois a gente vai pegar e vai criar essa variável aqui inteira chamado tamanho do double e a gente vai dar aqui o size off e vamos colocar entre parênteses o tipo. E aí, nesse caso, como não é uma variável e sim um tipo aqui dentro, é
obrigatório que a gente faça uso dos parênteses. Então aqui a gente vai tá obtendo aí o tamanho da variável double. Logo em sequência a gente tá imprimindo aí esses tamanhos, tamanho da variável inteira e o tamanho da variável do tipo double. Agora vamos ver aí a nossa primeira função, a função Malock. Essa função ela vai permitir que a gente reserve um espaço na nossa HIP e nós vamos chamar essa função passando para ela um número inteiro. Eu poderia colocar aqui então malock e passar como parâmetro valor 4 e aí eu estaria reservando 4 bytes. Porém,
para que eu não tenha que ficar gravando o tamanho de cada uma dessas variáveis, a gente vai usar o comando size off. Então a gente utiliza aqui o size of int e a gente sabe que a gente tá reservando um espaço do tamanho de um inteiro. Depois disso, pessoal, essa minha função aqui malock, ela vai me retornar um endereço. Só que o endereço que essa função Malock retorna é sempre um ponteiro do tipo vazio. Então a gente precisa aqui fazer uma conversão para um ponteiro do tipo inteiro e a gente vai estar armazenando aqui nessa
minha variável X. E agora a gente pode simplesmente utilizar da maneira que a gente estava fazendo anteriormente. A gente pode desferenciar, alterar esse valor e a gente já vai estar usando esse espaço na nossa hip. Um outro comando que já está aparecendo aqui é o comando free, que é pra gente liberar esse espaço na nossa HIP. E aí a gente tem que passar o endereço que tá salvo. Então a gente usa o X, que é um ponteiro. E aí quando a gente usa o X, a gente passa esse conteúdo, que é um endereço lá hexadecimal.
Então, para garantir que você compreendeu, vamos fazer aqui uma ilustração. A gente tem o nosso código, né? E aí, inicialmente, a gente vai ter essa nossa variável X. A gente tá criando essa nossa variável aqui. Logo em seguida, a gente vai chamar o comando malock e vai passar aí o size off. Esse sizeo vai retornar o valor 4. Então, poderia estar inserindo aqui o valor 4 também, não teria nenhum problema. Feito isso, que a função Malock vai alocar 4 bytes de espaço. Aqui a gente tem 4 bytes. Eu fiz essa divisão para ela acontecer de
quatro em quatro, né? Mas se eu tivesse feito de um em um byte, eu teria que ter aqui então quatro traços. Beleza? Então quando for alocado esse nosso espaço, essa nossa função malock, ela vai retornar um ponteiro do tipo void. E aí nós vamos converter esse ponteiro para um ponteiro do tipo int. Repara aqui. E feito isso, a gente vai estar armazenando dentro da variável que a gente criou na nossa função main, alterando então esse valor que inicialmente era zero. Feito isso, a gente tem um endereço fácil da gente trabalhar com ponteiro, como a gente
sempre fez. Nesse caso aqui, houve um dreferenciamento e a gente vai est colocando o valor cinco lá dentro. Logo em seguida, a gente tá imprimindo esse valor, utilizando a operação de desse referenciamento e depois estamos fazendo a liberação desse espaço. A função free está recebendo um endereço e esse endereço é o endereço que tá armazenado na minha variável X. Vou colocar aqui esse endereço. Feito isso, esse nosso espaço de memória vai ser liberado. Mas o conteúdo da nossa variável, se o nosso Código continuasse a existir, ele ainda estaria aqui. Um detalhezinho, né? função main tá
simplesmente chamando essa nossa função fun aí. Então quando a gente retornasse aí paraa nossa função main, essa nossa variável aqui também deixaria de existir. Essa função malo pode ser usada paraa gente poder alocar aí também um arrei. Onde a gente pergunta pro usuário quantos números ele deseja editar, ele nos informa quantos números ele deseja digitar e a gente armazena nessa variável quantidade. Aí a gente cria esse nosso ponteiro chamado arrei e aqui dentro a gente chama a nossa função maloque. Daí pra gente não ter que colocar aqui o valor 4, né, que é a quantidade
de bytes que um float tem, a gente usa o comando size off float. Aqui dentro nós vamos receber o valor 4 e multiplica pela quantidade que ele deseja armazenar. Então aqui nós vamos estar reservando na nossa memória, caso o usuário tenha ditado 5 x 4 bytes, 20 bytes. Esses 20 bytes vão estar na nossa hip, eles retornam como um ponteiro do tipo vazio. Então a gente tem que converter para um ponteiro do tipo float. Por isso a gente coloca aqui esse cast, essa conversão de tipo convertido. A gente vai ter aqui no nosso arreio um
endereço lá da nossa memória hip. Se a gente tem um endereço lá da nossa memória RIP de um array, como a gente perguntou pro usuário a quantidade, a gente pode fazer uma iteração aí que vai ir de zero até essa quantidade e ir preenchendo esses locais da nossa memória. Nesse caso aqui, a gente está utilizando o arrei colchet Z, Que já realiza a operação de desse referenciamento. E aí, por esse motivo, a gente precisa fazer uso do nosso operador de endereço. E a gente poderia também ter escrito dessa maneira aqui, onde a gente estaria somando
o valor de ao endereço desse nosso arreio. E aí nós não precisaríamos estar fazendo uso do operador de endereçamento. Vamos passar aqui rapidinho, então, numa ilustração pra gente ver o que que tá acontecendo na memória. Vamos supor que o usuário tenha digitado três. Então, ao invés da gente alocar somente um espaço, a gente vai alocar três espaços de inteiros da 12 bytes. E aí a gente vai retornar aí um ponteiro do tipo void com esse endereço inicial. Mas ao lado da nossa função Malo a gente já tá fazendo uma conversão para uma ponteira do tipo
float. E aí a gente pode armazenar isso nessa nossa variável. Tá aí, pessoal. O programa tá pedindo aí pro usuário ir preenchendo esse vetor. Repara que diferentemente de quando a gente cria o nosso ponteiro, quando a gente aloca esse nosso espaço na memória utilizando a função Malock, a gente vai ter lá um lixo e não zero. Então o usuário vai vir aqui, vai preencher e aí vamos supor que o usuário preencheu ali com um, um e um. E à medida que ele vai preenchendo, a gente vai calculando a média. que estaria numa variável aí que
é dinâmica da nossa pilha. Então ia ter algo aqui mais ou menos assim, calculando essa média que ia dar 1,5. Então vamos agora pra nossa próxima função. E se você entendeu a função meloc que recebe esse nome aí que é uma abreviação para memory allocation. Então toda vez que você vê aqui essa palavra é de allocation, ok? Você não vai ter problemas de entender essa função co que recebe esse nome aí por causa de continuous allocation, tá? Porque basicamente a diferença entre essas duas funções é que quando a gente faz uso da função mel, a
gente vai reservar esse nosso espaço na nossa HIP, mas ele não vai ser inicializado, então ele vai ter um lixo lá dentro. Não confundam. Quando a gente cria um ponteiro, todo ponteiro que a gente cria, ele vai ter o valor zero. Mas quando a gente reserva um espaço na nossa hip, se a gente fizer isso utilizando a função mel, esse espaço não vai ser inicializado, então ele vai ter algum lixo lá. Então, até agora, se você voltar aí no vídeo, você vai ver que eu tinha colocado lixo dentro desse nosso espaço reservado. Se a gente
fizer uso da função aí sim nós vamos reservar esse espaço e colocar o valor zero dentro dos locais que a gente reservou na memória. Uma outra diferença que tem da função Melock pra função Kello é que essa função Kello recebe dois parâmetros. Reparem na vírgula aqui dentro. Na função MELOC, a gente envia ali uma quantidade de bytes. Então, por exemplo, se o usuário quer armazenar três variáveis do tipo inteiro, a gente vai lá e manda para ele ou do tipo float, né, que também ocupa 4 bytes, a gente manda para ele ali 12. E a
gente sabe que a gente vai tá reservando aí 12 bytes. Na função Kello, a gente manda uma quantidade que vai ser três e manda ali um tamanho que vai ser quatro. Então essa multiplicação ela é feita dentro da função. Na função mel a gente tinha que fazer essa multiplicação fora dela. Na função kock essa multiplicação é feita lá dentro. Então a gente envia dois parâmetros. Ela também retorna um ponteiro vazio e a gente também precisa est realizando essa conversão. Então vamos passar de uma maneira mais detalhada nesse programa pra gente ver tudo direitinho. Então esse
nosso programa ele tá criando duas variáveis. Essas variáveis são dinâmicas da pilha, tá aqui em verde, que é a variável arrei, que é um ponteiro, né? E a variável média. essa variável média, vamos ver se ele tá inicializando, já tá inicializando ali com o valor zero. Então vamos colocar aqui zero. E na verdade, na verdade não é essa variável arreia, é a variável quantidade. Então vamos colocar aqui a variável quantidade também pra gente fazer bem direitinho quantidade que é o inteiro. Eu vou deixar esse arreio aqui, apesar da gente não ter chegado nessa linha, né?
Mas daqui a pouquinho a gente vai estar criando. Então a gente cria essas duas variáveis aqui na linha um e linha dois do nosso programa. E aí a gente imprime ali pro usuário quantos números ele deseja editar e espera que ele informe quantos números ele deseja editar. Depois a gente armazena isso na nossa variável quantidade. Então vamos colocar aqui que o nosso usuário digitou três. Depois disso a gente cria essa nossa variável arrei que armazena o endereço. Então a gente chama ela de ponteiro, né? Tá aqui. E aí a gente faz a chamada da função,
a precedência que é da direita pra esquerda. Então a gente vai começar aqui pelo size of float que é quatro. E aí depois a gente vai chamar a nossa função mandando aí a quantidade que é três e o tamanho que é quatro. Quando a gente fizer isso, essa nossa função Clock vai reservar três espaços na nossa memória e já vai inicializar eles com valor zero. Depois ela vai retornar esse endereço aqui e esse endereço vai ser retornado aí como um void, né? E a gente vai est fazendo a conversão para um float. Para quê? Para
Quando a gente desreferencie, a gente saiba que a gente está acessando um float. Então vamos colocar aqui no nosso arreio esse endereço e depois disso nós vamos fazer uma iteração aí pedindo para que o usuário insira um valor. E aí a gente vai inserir esse valor. Vamos colocar aqui o valor, sei lá, cinco, por exemplo. E aí a gente vai pegar e vai adicionar esse valor na nossa variável média, fazendo essa soma aí. Aí se ele digitar um outro valor que vai ser três, a gente vai somar aqui, vai passar a ser oito. Depois ele
inserindo o novo valor, a gente colocaria ali cinco novamente e vamos passar a ter 13 nessa nossa variável. E aí para finalizar o nosso programa vai estar realizando uma impressão dividindo 13 por 3 e encontrando a média. Pessoal, além das diferenças que eu listei para vocês aqui, eu quero dizer que a função Melock, ela tem um desempenho maior. Quando a gente vem aqui, né, na verdade a gente tem 4 bytes, cada byte tem 8 bits. Pra gente estar zerando, inicializando esse valor, a gente tem que passar em cada um desses bits aí e ir zerando
ele. E às vezes não é necessário, como por exemplo, nesse caso aqui, se o usuário já vai preencher, para que que a gente vai ter que gastar recurso zerando esse valor? Evidentemente que nos nossos programas isso não vai ser algo que vai afetar, mas né, eu preciso mencionar porque vai que você tá trabalhando com sistema embarcado, enfim, como a gente tá falando da linguagem C. Aí nesse caso, vale a pena você se atentar se realmente é necessário ou não fazer uso da função? Então, das quatro funções que a gente iria ver sobre a locação de
memória, a gente já viu duas, a Malock e a Calock, que são funções pra gente estar reservando esse espaço na nossa memória RIP, sendo que uma delas reserva esse espaço e já inicializa ele, já coloca o valor zero lá dentro. A gente acabou já esbarrando aí na função de liberação. A gente já viu mais ou menos como funciona. E agora a gente precisa ver a função para realizar a realocação de memória. O que que isso quer dizer? Imagino que eu reservei um espaço na minha hipí diminuir esse espaço, esse espaço do meu arrê, por exemplo.
Você não vai precisar deletar down free e alocar novamente, porque agora nós vamos aprender a utilizar a função de realocação de memória. Pois bem, essa função, pessoal, ela precisa de um endereço da nossa HIP e do novo tamanho desse nosso espaço de memória. Aqui a gente tem que enviar esse tamanho e esse tamanho é em número de bytes. Então vamos dar uma olhada nesse nosso código aqui. Então primeiro a gente tá usando a função clock para poder alocar cinco espaços de flo floats que ocupam 4 bytes. Então a gente vai ter aí 20 bytes. Beleza?
A gente já tá convertendo pra gente poder desreferenciar aí numa boa. Estamos armazenando nesse nosso ponteiro que a gente chamou de arrei. Depois disso, a gente tá alterando esse valor. Então nesse caso aqui seria mais interessante utilizar uma lock, mas tudo bem. E a gente tá imprimindo esse valor que a gente acabou de alterar. Logo em seguida, a gente tá reduzindo, a gente tá chamando a função de realocação de memória. A gente tá passando esse nosso endereço que a gente recebeu quando a gente usou essa função coloca aqui. E a gente tá reduzindo o tamanho
porque antes o tamanho era C e agora o tamanho Vai ser 3. Então em vez de 20 bytes, a gente vai ter aí 12 bytes. 3 x 4 dá 12. E aí essa nossa função reallocation vai nos retornar um ponteiro, um endereço da nossa hip. a gente atualiza ali dentro da nossa variável array e depois realiza essa impressão. Então vamos dar uma olhada aqui numa ilustração pra gente fixar bem. Então, só de bater o olho aqui dentro da minha pilha e da minha HIP, eu sei que a gente já tá mais ou menos aqui no
nosso código. Por quê? Porque a gente já armazenou cinco espaços na nossa HIP, né? um array de cinco floats. A gente já tem o endereço para tá acessando esse array aqui. E agora a gente vai chamar aí a nossa função de realocação. Então a gente passa para ela esse endereço. A gente passa para ela o novo tamanho em bytes que vai ser 12. Antes o tamanho era 20 porque isso aqui ocupa quatro. Isso daqui quatro. Isso daqui quatro. Esse daqui quatro. Esse daqui 4. Dá 20. E aí nós vamos realizar essa redução. Realizada essa redução,
essa nossa função de realocação, ela vai retornar este endereço aqui. E aí a gente vai armazenar ele novamente na nossa variável. Então a gente vai arzenar o mesmo endereço na nossa variável. Aí você pode então estar se perguntando por que que eles não fizeram essa função de realocação de memória para ter um retorno do tipo void, porque daí eu nem precisava colocar essa minha variável aqui, esse igual. Eu podia só chamar essa função, ela não ia ter retorno nenhum. Pessoal, acontece que quando a gente tá reduzindo o nosso tamanho, o nosso endereço vai ser sempre
o mesmo. Mas se a gente quiser aumentar o nosso tamanho, a gente pode não conseguir fazer isso no mesmo endereço. Então, imagina que tenha ali alocado uma outra coisa. E aí você pediu, utilizou a função de realocação de memória para aumentar o tamanho para cinco, de três para cinco. Se já houver algo alocado ali na frente, essa função internamente ela vai pegar, vai movimentar isso aqui para um outro lugar, vai aumentar o tamanho e ela vai copiar esses elementos também, né, para que você possa simplesmente considerar aí que o tamanho do seu arreio aumentou. Então
essa explicação toda é só para você entender porque que é importante que você esteja atualizando ali, receba esse retorno da função de realocação, que é o endereço e atualize. E se você tá preocupado aí, se quando você estiver aumentando, né, o tamanho do seu arreio utilizando a função de relocação, você vai precisar fazer aquele cast que a gente fez aqui, por exemplo, quando a gente usou a função callot, você não precisa, ele já é feito internamente, não vai ter nenhum problema, não é necessário. Mesmo que essa movimentação ocorra, né, você pode simplesmente considerar aí que
foi aumentado o valor. Você não precisa nem saber do funcionamento interno dessa função. Cuidado ao pesquisar sobre isso, porque você pode encontrar aí a necessidade de fazer esse cast, mas isso aí seria pra linguagem C mais, pra linguagem C não é necessário. Muito bem, pessoal. Agora já trouxe para cá os nossos arquivos, tudo que a gente viu e a gente vai conversar um pouquinho sobre o comando free. Agora a gente não vai ficar tanto analisando o código, mas eu quero trocar uma ideia com vocês sobre a importância de você estar liberando esses espaços que você
aloca na HIP. Quando eu aprendi sobre esse assunto, eu aprendi que se eu não liberasse esse meu espaço e eu terminasse o meu programa, eu ia ter um lixo ali na minha memória e esse lixo só sumiria quando eu reiniciasse o meu computador. Então, imagina que você vem aqui nesse nosso código, executa esse código três, executa esse código dois, a gente não tá utilizando o free. Executa esse código aqui também, a gente não tá utilizando o free. você teria aí teoricamente aí perdido um espaço na sua memória e você teria que reiniciar o seu computador.
Acontece que isso não é verdade. Os nossos sistemas operacionais mais modernos, mesmo que você não utilize o free, não precisa se preocupar, tá? Esse espaço vai voltar ao final da sua execução aí pro seu sistema operacional. Você não vai precisar estar reiniciando o seu computador. Porém, por que que eu aprendi dessa maneira? Às vezes a gente vai estar trabalhando com um sistema operacional mais simples, ou às vezes a gente pode estar trabalhando até mesmo na construção de um sistema operacional. E aí, se você cometer esse erro de não liberar essa memória, realmente ela vai estar
perdida para todo sempre até que você reinicie o seu computador. E vão existir programas que eles vão ficar rodando o tempo inteiro. Então imagina se toda hora você acaba alocando uma nova variável, não dá um free. Com o passar do tempo, um programa, por exemplo, que seria ali um relógio, ele vai consumir toda a memória do seu computador. Então é importante que você se preocupe em estar realizando essa liberação de tudo que você alocar na sua HIP. Existem programas que vão ajudar a Gente a analisar, verificar se houve algum tipo de vazamento de memória ou
não. Futuramente eu vou mostrar isso para vocês, mas agora eu só queria trocar essa ideia para vocês entenderem a importância de estar realizando essa liberação de memória. Dito isso, eu quero conversar com vocês sobre dois problemas. Já vou criar aqui os arquivos para vocês saberem o nome desses problemas. Colocar aqui cuidado em maiúsculo, problema da variável perdida. E vou criar aqui também o arquivo número seis. Cuidado, problema do ponteiro solto. Então vamos lá. Vou colocar um código aqui. Esse nosso código vai começar na nossa função main. A nossa função main vai chamar a nossa função
fun. E aí vamos imaginar que depois que a gente retornar dela aqui, programa continua, vida segue. Aí essa nossa função FAM, ela tá alocando um espaço de memória que ela tá salvando aqui nessa variável X o endereço que foi alocado. Depois disso, ela altera o valor lá dentro da nossa IP e aí ela imprime esse valor. Só que quando ela retorna, pessoal, isso daqui tá numa variável que é dinâmica da pilha. Quando ela retorna, a gente nunca mais, nunca mais vai conseguir acessar este endereço que foi alocado, porque ao sair dessa função, aquela variável não
vai mais existir. Então, a partir daí, se você executou esse código, você vai ter ali alocado um espaço de memória que você nunca mais vai conseguir liberar, porque para você liberar, a gente teria aqui que dar um free e colocar esse endereço e a gente não vai saber esse endereço. Então o correto é você vir aqui e está liberando esse espaço antes De sair dessa função. Então vou escrever essa explicação aqui para vocês. Uma variável foi alocada na HIP e X aponta para ela, mas como X é dinâmica da pilha, ela deixa de existir quando
a função fun encerra. Contudo, a variável locada na RIP, que armazena o valor 5, não foi liberada. a gente esqueceu de liberar e ela vai continuar existindo após o final da função. Essa variável ela tá perdida na RIP porque não há mais um ponteiro, a gente não sabe mais qual que é esse endereço da RIP que a gente precisa estar liberando. E a gente não consegue também recuperar essa variável. Se a gente chamar essa função novamente, ela vai alocar um novo espaço e esse novo espaço vai ter um novo endereço. Então esse é um problema
aí que a gente deve se atentar a ele. E aí vou deixar uma dica aqui para vocês. Dica: sempre que alocar memória em uma função, verifique [roncando] se está retornando o ponteiro. Não estiver, verifique [risadas] se a liberação está sendo feita. Então, percebam que nesse caso aqui a gente tem um espaço na nossa memória e a gente não consegue apontar para ele. A gente pode ter também o caso contrário, onde a gente vai ter um espaço na nossa memória que a gente está apontando para ele, mas ele já foi liberado. Vou escrever um exemplo aqui
para vocês. Então, dá uma olhada. Creio aqui dois ponteiros, o ponteiro Y, o ponteiro X e o ponteiro X tá recebendo um endereço lá da minha rede. E aí eu peguei, pessoal, e fiz com que o meu ponteiro Y apontasse exatamente para onde o meu ponteiro X está apontando. Então agora a gente tem dois ponteiros, tanto Y quanto X, que tem o mesmo endereço. Aí eu acessei aqui através do X, do endereço X, alterei o meu valor lá da minha HIP, passou a C5, imprimi e aí eu liberei essa minha variável X, liberei esse endereço,
esse espaço que tava lá na minha HIP, só que eu ainda tenho esse endereço salvo na minha variável Y. Aí eu vou lá e acesso e ainda altero um espaço lá da minha RIP que tá liberado e faço essa alteração. Pessoal, isso dá um problema danado. Então assim, você tá tentando acessar um lugar da HIP, você tem esse endereço ali, mas ele não tá reservado. E aí pode ser que no seu programa, em outro momento, na hora que você alocar uma outra variável, ela vai reservar um pedaço daquele espaço ou aquele espaço ali e você
ainda vai est alterando ele porque você salvou nessa outra variável aqui. E esse problema aqui, ele pode acontecer até se você não criar uma outra variável. Então vou até realizar uma impressão aqui pr vocês verem qual que é o conteúdo da variável X e o conteúdo da variável Y. E vou mostrar para vocês como isso poderia acontecer mesmo só com uma variável. Pessoal, por que que não necessariamente você precisa dessas duas variáveis? Vou melhorar essas impressões aqui para vocês consultarem no futuro, porque quando a gente dá esse free, a gente ainda tem o nosso ponteiro
e dentro desse nosso ponteiro, a gente ainda tem esse endereço salvo. Então, percebam que essa impressão que eu tô realizando aqui, ela é desreferenciando o valor de X, porque Mesmo depois de eu realizar a minha liberação, nós ainda vamos conseguir acessar esse endereço da nossa hip. Então, esse daí é o problema do ponteiro solto. E para você evitar ele, eu vou te dar duas dicas. A primeira é tentar realizar as liberações no final do seu programa ou da sua função. E a segunda dica é, caso você esteja realizando uma liberação no meio do seu código,
faça com que aquele ponteiro receba o valor nulo. Fazendo isso, eu vou ter um problema aqui na minha linha 19 e também vou ter um problema na minha linha 23. Mas isso é bom porque antes de eu compilar o meu código, eu já identifico que eu tô fazendo uso de um espaço de memória que eu não deveria. Vamos deixar essa linha comentada. Pois bem, pessoal, então agora só falta a gente ver aí a reis multidimensionais, como que a gente pode estar utilizando a nossa HIP para armazenar reis multidimensionais. A gente vai ver aí três maneiras
da gente estar fazendo isso. Mas para garantir que não haja nenhuma confusão, a gente vai voltar ali no nosso conteúdo de ponteiros e nesse arquivo aqui de arrei de ponteiros. Vamos dar uma olhada no que que tá acontecendo aqui, dar aquela revisada maneira e depois a gente olha como que a gente faz utilizando a nossa HIP. Então vamos lá. Aqui a gente tá criando quatro arreis de tamanho 10. Esses arreis estão aqui. Nós estamos criando um arreio de ponteiros de tamanho quatro. Esse arreio está aqui. E nós estamos fazendo com que cada uma dessas posições
aponte pra primeira posição deste outro arreiros. Então, percebam que fazendo desta maneira aqui, a gente tá criando ali quatro arreis cada um de tamanho dessa e a gente tá colocando um ponteiro pro início de cada um desses arreis. Nós podemos fazer uso dos colchetes. Por quê? Porque quando a gente faz o uso desses cchetes, na verdade a operação que tá sendo realizada é essa daqui. A gente pega o valor de Xi, este endereço, né, que tá nessa posição desse nosso arrei ponteiro, por exemplo, esse daqui. E aí a gente soma ao valor de J nessa
nossa interação. Então, como o nosso valor de i vai começar sendo zero, a gente vai pegar esse endereço aqui e a ele a gente vai somar zero. Então, a gente vai acessar essa posição aqui e vai desreferenciar ela. Logo em seguida, o valor de J vai ser incrementado e aí a gente vai continuar pegando essa posição aqui, mas agora somar mais um e depois desse referenciar. Então a gente vai acessar esse local aqui da memória. Depois o J vai sofrer um novo incremento. Então a gente vai continuar pegando essa posição aqui, mas agora a gente
vai somar mais dois e vai acessar esse lugar da nossa memória. Quando isso sofrer um incremento, a gente vai est pegando este local aqui e aí a gente vai somar mais zero, vai acessar ele aqui. Aí o J vai sofrer um novo incremento. A gente vai continuar pegando o endereço que está em X1. Essa aqui é a posição X1. Então a gente vai vir para cá, vai somar mais um e vai estar acessando esse local da nossa memória. Portanto, uso dos coochetes, quando a gente tem uma estrutura, um armazenamento ali na nossa memória, como este
daqui, né, funciona muito bem. Agora, pessoal, vamos imaginar o seguinte caso. Vamos imaginar Que, na verdade, eu tivesse criado um arrei só de 40 elementos, eu estaria ocupando o mesmo espaço. Lembrando que aqui a gente tá trabalhando com a nossa pilha, né? Não fizemos a locação dinâmica, mas eu teria ali esses 40 espaços e eu criasse somente o ponteiro para este primeiro elemento. Será que eu poderia estar fazendo uso dos colchetes dessa maneira? Vamos dar uma olhada. X0 vai ser esse valor aqui de X, né? Então a gente vai vir para cá. Mais 0 a
gente vai est nessa posição aqui. X01 a gente vai encontrar essa posição aqui. X02 a gente vai encontrar essa posição aqui. E por aí vai. Então, a gente vai percorrer aí 10 elementos e tá tudo certo. Agora, quando a gente for tentar acessar aí a nossa posição X1, a gente vai ter um [limpando a garganta] problemão. Por quê? Porque ela nem existe. O nosso arrei ponteiro aqui, ele só tem um endereço. Então, se eu tentar acessar X1, infelizmente a nossa linguagem C não vai nos barrar. a gente vai simplesmente acessar a posição aqui do lado.
E neste caso aqui é este endereço, porém poderia ter ali uma outra variável entre essas duas posições. Então a gente vai ter um problemão aí quando a gente tentar acessar a posição x1. Do jeito que o meu desenho tá, eu acabaria acessando este local aqui e aí depois somaria zero. Então continuaria neste local, ia tá fazendo desse referenciamento. No x1 1, eu ia acessar este local aqui mais um. Então, percebam que não vai estar funcionando. O que eu gostaria mesmo que acontecesse é que eu pegasse ali a partir da minha posição dessa. O que que
Eu quero que vocês tirem de informação de tudo isso que eu trouxe para vocês? Todas as vezes que a gente tiver trabalhando com arre multidimensionais, seja na nossa pilha de memória ou na nossa hiip, esta primeira coluna aqui vai ter que ter mais de um elemento. Percebam que o nosso problema não é porque, diferentemente da nossa solução anterior, a gente alocou de maneira separada, mas sim porque a gente não tem esses quatro elementos aqui. Então vamos tentar adaptar esse nosso código fazendo o seguinte, colocando esses quatro endereços aqui. E aí a partir de agora eu
vou atribuir a posição 0 10 20 e 30 ao meu arreio de ponteiros. Aí tudo bem, eu conseguiria estar fazendo uso dos meus colchetes. Acontece, pessoal, que existe também aí uma outra solução para que você consiga estar acessando como se você tivesse trabalhando com arrei multidimensional. Eu particularmente não gosto dessa solução. Eu acredito que o uso dos coochetes vai aumentar bastante a legibilidade do seu código e vai reduzir a chance de você estar cometendo erros. Mas eu vou te mostrar essa outra solução que a gente pode estar utilizando. Então para garantir aí que eu estou
sendo claro, toda vez que a gente quer trabalhar com um arrei multidimensional, neste caso aqui, um arrei bidimensional, uma matriz, a gente quer acessar uma Posição a partir de dois endereços. Um deles vai ser a nossa linha e o outro vai ser a nossa coluna. Então eu quero, por exemplo, colocar lá dois, três e acessar o espaço de memória correto. Existe uma maneira de você fazer isso mesmo que você armazene o seu arreio deste jeito aqui. A gente coloca ali, por exemplo, esses 40 espaços e faz só um ponteiro pro primeiro endereço. Qual que é
a maneira da gente estar fazendo isso? A gente vai agora não mais fazer uso dessas duas colunas, mas somente de uma, porque a gente já viu o problema que dá quando a gente faz x1, né, que ele vai vir aqui pro lado, x0, tá beleza? A gente tá aqui, tá acessando esse endereço aqui, tá, tá numa boa, mas quando a gente faz o x1 vai ter um problema. Então, a gente vai fazer uso aí de somente uma coluna. Só que dentro dessa nossa coluna, a gente vai ter ali a linha vezes o número total de
colunas mais a coluna. Então vou tá dando como entrada ali no lugar de i seria ali o zero e no lugar de j seria ali, por exemplo, o zero. E isso daqui vai funcionar. Por quê? Percebam que se eu quiser acessar a posição 0, eu vou fazer aqui 0 x 10 vai dar 0 + 0. Isso vai ser zero. E este colochete aqui fazem operação de descerenciamento. Então vou tá acessando este endereço aqui e aí vou est descerenciando, pegando essa posição. Se eu informar aí a linha zero e a coluna um, eu vou somar mais
um a este endereço aqui. Então vou cair neste endereço aqui e aí vou estar desreferenciando e vai estar funcionando. Se eu quiser acessar a posição X1 0, eu vou estar fazendo aqui a operação 1 x 10, que é 10 + 0. E aí eu vou estar acessando essa posição 10 aqui. E esses colchetes já vão fazer essa operação de desse referenciamento. Se eu quiser acessar a posição linha dois, coluna zero, eu também vou conseguir, porque percebam que eu vou fazer 2 x 10, que é igual a 20 + 0. Então, vou estar acessando a posição
20. Então, basicamente o que eu estou fazendo é tendo ali no meu programa um arrei unidimensional que tá ocupando esses 40 espaços, mas fazendo essa multiplicação e essa soma, eu consigo acessar baseado na linha e na coluna, chegando na posição correta. Beleza, Caio? Mas por que que você nos explicou isso daí? Por, pessoal, a gente pode fazer da mesma coisa quando a gente tiver trabalhando com a nossa RIP. Se você tiver trabalhando com a sua HIP e aí você quer armazenar uma matriz ou um arrei bidha três linhas e duas colunas, vou até escrever aqui,
três linhas e duas colunas, você pode simplesmente estar multiplicando 3 x 2, que a gente vai obter o valor 6, e armazenando um arreio unidimensional de seis espaços. Só que se você fizer isso e salvar este endereço em somente um ponteiro, você vai ter o endereço somente do primeiro elemento desse arrei. Você não vai conseguir fazer uso dos colchetes duplos, mas você vai conseguir acessar a posição correta se você utilizar essa operação que eu mostrei para vocês, onde o i a linha e o j é a coluna. Então, nesse meu caso aqui, se eu quiser
estar acessando aí, por exemplo, né, a posição Dois um, eu vou conseguir [limpando a garganta] fazendo essa operação que vai ser 2 xo coluna, que é 2 + 1. 2 x 2 dá 4 + 1 dá 5. E aí eu vou estar acessando o local correto. Essa daqui seria 0 1 0 1 2 1. E fica tranquilo que daqui a pouco a gente vai dar uma olhada melhor nesse código. Por enquanto eu quero que você entenda o que que tá acontecendo aqui, que é basicamente a mesma coisa que está acontecendo neste nosso exemplo anterior. Pessoal,
é claro que utilizando a RIP, eu também posso criar uma solução onde eu vou ter um ponteiro para cada uma dessas posições e aí sim eu vou conseguir tá fazendo o uso desses meus colchetes. Porém, percebam que agora vai haver uma diferença. Eu não estou utilizando somente a minha pilha, mas sim a minha hip. Então eu preciso ter um ponteiro para esse meu arreio de ponteiros. E aí esse arreio de ponteiros do número de linhas vai apontar aí para cada uma delas. Percebam que é semelhante a esse caso que a gente viu, mas existe essa
diferença de termos um ponteiro apontando para esse arreio de ponteiros, porque aqui nós só estamos fazendo uso da pilha e aqui nós estamos fazendo uso da hip. Lá nós tínhamos um arrei bidensional de tamanho 4 por 10. E aqui nós temos um arreio bidimensional de tamanho 3 por 2. Então vamos dar uma olhada em como a gente pode estar implementando isso. Primeiro nós vamos criar um ponteiro para ponteiro P. Por que ponteiro para ponteiro? Porque eu vou estar salvando o endereço de um Arreio de ponteiros. Então, se eu estou salvando o endereço de uma rei
de ponteiros, eu preciso ter um ponteiro para ponteiro. Criado esse nosso espaço, nós vamos então alocar um espaço na nossa HIP do tamanho do número de linhas, que é três. Depois nós vamos pegar esse endereço e armazenar nesse nosso ponteiro, fazendo esse nosso cast para esse tipo. Feito isso, nós vamos iterar sobre esse arreio que a gente acabou de criar. Vamos fazer um fora aí que vai ir até o número de linhas, que é três. Então a gente vai passar em cada uma dessas posições. E à medida que a gente for passando em cada uma
dessas posições, nós vamos estar alocando um espaço equivalente ao tamanho das nossas colunas, que é dois. E como a função Malo retorna um endereço, nós já vamos pegar esse endereço e armazenar dentro do nosso arreio de ponteiros. Finalizada essa nossa iteração, nosso arreio de ponteiros vai estar apontando para cada uma das nossas linhas. Essa parte aqui do nosso código, ela existe só para est alterando esses valores que são lixo. Então, nós não precisamos nos preocupar com ela agora. E depois nós estamos realizando a impressão de cada um desses valores, fazendo uso dos colchetes. Bom, vamos
dar uma olhada no porque é possível a gente estar fazendo uso desses colchetes. E basicamente essa operação P J, ela pode ser escrita da seguinte maneira. A gente vai ter ali o desse referenciamento de PI + J. E pi, na Verdade, é o desse referenciamento de P + I. Então, percebam que caso eu tente acessar aí qualquer posição, vamos pegar uma hipotética aqui como a posição P 1. Nós vamos pegar o endereço que está dentro de P, vamos somar um a esse endereço, que é o valor de I. E se a gente vai somar um
a esse nosso endereço, nós vamos chegar neste endereço aqui, 8804. Feito isso, nós vamos desreferenciar. Reparem na nossa operação de d referenciamento. Então, a gente vai estar acessando agora este conteúdo aqui, 8820. E nós vamos somar um a este conteúdo. E somando um a este conteúdo, nós vamos chegar nesta posição aqui. Então, percebam que não há nenhum tipo de problema da gente estar fazendo o uso dos dois colchetes, caso a gente tenha utilizado essa implementação de arreis bidensionais na nossa HIP. Pessoal, se você entendeu essa solução, você vai entender as outras, que são simplesmente variações.
Eu poderia, caso eu quisesse, ao invés de estar alocando três arreis diferentes, como a gente tem aqui, 1 2 3, alocar um arreio único de tamanho seis e depois pegar esses endereços e colocar aqui dentro. Seria uma solução viável e que também me permitiria fazer uso dos colchetes. E para não confundir vocês, eu vou trazer somente essas duas variações que vão nos permitir fazer uso dos colchetes, esta daqui e esta daqui. Neste caso, a nossa solução vai se parecer com a anterior, porém nós vamos criar dois ponteiros na nossa pilha. O primeiro deles vai ser
o ponteiro A e ele vai apontar para esse espaço total do meu arreio bidimensional. O segundo vai ser o ponteiro M, que vai ser um ponteiro para ponteiros. Por quê? Porque ele vai apontar para um arreio de ponteiros. OK? Alocado esses dois espaços que vão ser acessados por esses dois ponteiros, basta agora eu terar de acordo com a quantidade de linhas, assim como a gente havia feito no exemplo anterior. E para cada uma dessas posições, eu vou colocar o endereço correspondente. Como que eu vou fazer isso? Eu vou multiplicar a minha variável i pelo número
de colunas, que é 2, e vou somar a a, ou seja, ao endereço inicial do meu arrei. Vamos ver porque isso vai funcionar. Quando i for zero, nós vamos ter 0 x 2, que vai dar 0 + a, vai ser o endereço de a. Então, a gente vai retornar este endereço aqui. Quando a gente tiver o i = a 1, nós vamos fazer 1 x 2, que é 2. Nós vamos formar ao endereço inicial 2. E aí nós vamos ter este endereço aqui. E a gente vai retornar ele para essa posição do nosso arreio de
ponteiros. Quando i for Igual a 2, nós vamos fazer 2 x 2, que é 4, e vamos somar a esse endereço. Então a gente vai estar nesta posição aqui e vamos retornar para cá. Feito isso, a gente não vai mais utilizar essa variável A. Vamos utilizar somente essa nossa variável M e vamos estar conseguindo fazer uso dos colchetes. Eu sei que a primeira vez que a gente se depara com esse conteúdo é um pouco assustador, mas mantenham a calma. Eu vou salvar esses nossos códigos para que vocês possam consultar. E lembrem-se, vocês não vão ter
que criar essas soluções com o cronômetro ligado. Vocês vão ter materiais disponíveis para consultar. Em caso de dúvida, você vai ter este código disponível para consultar e com o tempo isso tudo vai se tornando mais fácil de ser compreendido. Pessoal, essa lista de exercício será a mais desafiadora que já enfrentamos até agora, mas também será a mais enriquecedora. Ela vai exigir dedicação e esforço de cada um de vocês, mas vai oferecer uma excelente oportunidade de aprendizado e desenvolvimento. Aproveitem ao máximo essa chance para praticar e se aprimorar e respeitem o tempo individual de vocês. E
se surgirem dúvidas, não hesitem entrar no nosso Discord acessando o link aqui na descrição. Desejo uma boa sorte a todos vocês e nos vemos no nosso próximo assunto. Muito bem, pessoal. Então vamos começar o nosso próximo assunto. Persistência em arquivos. Esse assunto você pode encontrar ele com o nome de persistência em alguns livros. Você pode Encontrar ele com o nome de arquivos e você também pode encontrar ele em inglês com o nome files, que nada mais é do que arquivos em inglês. Antes da gente entrar de cabeça nesse nosso tópico, vamos dar uma olhada na
nossa estrada até aqui. O que que nós conseguimos fazer? o que que nós não conseguimos fazer, que a gente vai aprender agora. E vamos tentar clarear algumas coisas para que a gente não confunda a locação dinâmica com persistência de arquivos. Contudo que a gente já aprendeu até agora, a gente seria capaz de estar criando um programa como esse aqui que eu vou estar executando, onde a gente pode estar cadastrando um novo produto, nome do produto, chiclete, o valor do nosso produto, R$,99. Vamos cadastrar um novo produto, House. O valor do nosso produto, R$,99. E a
gente poderia continuar cadastrando novos produtos à medida que essa necessidade fosse existindo. Inclusive, a gente poderia também remover esses produtos. Acontece que a gente saia desse nosso programa e a gente entre nesse nosso programa novamente, nós vamos precisar estar cadastrando todos os nossos produtos novamente. Pessoal, essa opção aqui seria viável para um supermercado? Bom, se você deixasse a máquina ligada 24 horas o Tempo inteiro, sim, mas e se faltasse energia? Ah, a gente tem um backup de energia, mas e se o backup falhasse? Então você perderia todos os seus produtos cadastrados. Da mesma maneira você
perderia ali todas as suas vendas cadastradas. Por que que isso acontece? Tudo que nós vimos até agora foi utilizando a nossa memória RAM. Ou seja, todos os desenhos que nós fizemos até o momento envolvem a nossa memória RAM. E isso nos traz uma limitação. A limitação é a persistência desses arquivos. Uma vez que o nosso programa deixa de executar, as variáveis que nós salvamos também deixarão de existir. Mas e a locação dinâmica, será que ela tem alguma relação com isso? Não, pessoal, ela não tem. Se a gente não utilizar a locação dinâmica, o nosso programa
vai ocupar aqueles 8 MB iniciais. E se a gente for cadastrando novos produtos, eventualmente nós vamos estourar a capacidade da nossa pilha. Se nós utilizarmos a locação dinâmica, então o nosso programa, quando a gente for verificar lá no nosso gerenciador de tarefa, dependendo do sistema operacional que a gente esteja utilizando, ele vai aumentando ali o seu consumo de RAM à medida que isso for necessário. Mas ainda assim nós estamos trabalhando dentro da nossa memória RAM. Bom, então já dá para perceber que a gente vai aprender então a salvar esses nossos arquivos, fazer com que eles
persistam, mas vai muito além disso. Nós vamos aprender a ler esses arquivos. Vários programas eles precisam estar lendo esses arquivos. O navegador que vocês estão utilizando para consumir Esse vídeo, ele está lendo arquivos que você recebe dentro da sua máquina, tá? Um banco de dados, por exemplo, ele também precisa salvar esses arquivos, porque quando você fecha e ele abre, esses arquivos persistem. O banco de dados também é um programa, ele também foi programado. Então esse é um assunto extremamente importante e nós vamos dar a devida atenção a esse tópico. E para começar, vamos dar uma
analisada nessa imagem que eu trouxe para vocês. Aqui nós temos um HD. Ele recebe essa sigla HD que vem do inglês hard disk, disco duro, né? Que tá associado aí a persistência desses nossos arquivos. À direita a gente tem ali um SSD que recebe essa sigla por causa de uma abreviação, Solid State Disc, que é disco de estado sólido, né? Então esses arquivos vão persistir dentro do SSD. A gente tem ali um SSD M2, também recebe essa mesma sigla, mas a sua conexão é diferente. E aqui embaixo nós temos um cartão de memória, um microSD,
micro Solid disc, um pequeno disco sólido. E na nossa esquerda, a gente tem uma fita que era utilizada para armazenar aí músicas e tocar num carro. A gente poderia salvar programas ali dentro também. Existem vários outros dispositivos que já foram utilizados para salvar esses nossos arquivos. Inclusive, existe um deles que era uma folha de papel que recebia alguns furos. Então, mesmo que o seu programa fechasse, tinha suas folhas de papel, depois você mandava o seu programa e ler essas folhas de papel. Era um papel mais grosso, claro, mas cumpri essa mesma função. Pois bem, então
esses dispositivos de arquivos, eles podem armazenar algumas informações. Mas quais informações? Nós vamos dividir essas informações em dois grupos, arquivos de texto e arquivos binários. E qual é a diferença de um arquivo de texto para um arquivo binário? Percebam que nós temos aqui um número 1 2 3 4 5 6 7 8 que poderia ser lido como 12.345 678. Nós poderíamos salvar esse nosso número em um programa na nossa linguagem C utilizando caracteres. Quando a gente dá uma olhada na nossa tabela AS, vamos dar uma olhada aqui, além das letras A maiúsculo, A minúsculo, nós
também podemos salvar os nossos números como caracteres. Então nós poderemos estar armazenando como caracteres esses números. E se a gente voltar na nossa tabelinha, vamos voltar aqui na nossa tabelinha, nós vamos perceber que cada caracter ocupa 1 byte. Um byte contém 8 bits. Então nós temos aqui 8 bits que vão representar cada um desses caracteres. Então o dígito ele seria representado aí por 00 1 8 bits. Caracter número 2 00 1 001. Um outro byte. O caracter número 3 por um outro byte. O caracter número 4 por um outro byte, o caracter número cinco por
um outro byte, caracter número seis por um outro byte. Cada byte contém 8 bits, caracter número 7 por um outro byte e o caracter número 8 por um outro byte. Então, para armazenar esse número, nós gastaríamos aí 8 bytes, caso a gente estivesse armazenando ele como caracteres da linguagem C, utilizando a nossa tabela ASC 2, né, que é o que é utilizado ali por baixo dos panos, que é o que determina que cada caracter vai ocupar um byte. Da mesma maneira, nós poderíamos estar armazenando este número como um inteiro. E o inteiro ele ocupa 4
bytes. Esse inteiro que ocupa 4 bytes vai nos gerar uma economia. Ao invés da gente gastar 8 bytes, nós vamos gastar agora 4 bytes e nós vamos poder armazenar números que estiverem neste intervalo aqui. Então existem essas duas formas da gente estar armazenando números. seria como uma string ou seria como um inteiro. E existem vantagens e desvantagens quando a gente escolhe armazenar arquivos de texto e quando a gente escolhe armazenar arquivos binários. Antes da gente conversar sobre essas vantagens e desvantagens, eu quero mostrar para Vocês alguns arquivos de texto e alguns arquivos binários. Dentre os
arquivos de texto, nós temos várias extensões possíveis. Uma delas é o txt, nós temos o JSON, nós temos o ponto C, são os arquivos que a gente vem escrevendo na nossa linguagem C, nós temos o HTML, nós temos o CSV e vários outros. Já como exemplos de arquivos binários, nós temos os arquivos pone. Nós transformamos um arquivo de texto pc em um arquivo ponamos o nosso programa. Então, nós saímos de um arquivo de texto, onde as coisas são armazenadas como caracteres. Caracteres esses que podem ser inclusive o espaço, o ponto e vírgula e criamos um
arquivo pone. Dentre os arquivos binários, nós temos também imagens, JPG, PNG, GIF, MP3, áudio, MP4, arquivo de vídeo, PDF é um exemplo de arquivo binário, o ZIP também é um exemplo de arquivo binário. A ISO também é um exemplo de arquivo binário. Nós podemos abrir no nosso computador o bloco de notas. O bloco de nota, ele consegue ler arquivos de texto. Mas será que ele consegue ler arquivos binários? Vamos fazer o teste. Vou pegar O meu arquivo main.c e vou abrir aqui no bloco de notas. Tranquilamente, eu consigo estar lendo esses arquivos. Eu consigo verificar
as quebras de linhas. Barra N, ele já corrige aqui, já interpreta pra gente, quebra essa linha. Mas e se eu tentar ler um arquivo ponto e dentro do bloco de notas? O que acontece? Bom, abriu, mas a gente vê aí vários caracteres super estranhos. Inclusive, chega um momento onde a gente consegue ver palavras, né? Vamos continuar explorando isso daqui. Se eu tentar abrir um arquivo PNG, vamos ver o que que acontece. Também nós vemos aí vários caracteres super estranhos. Agora eu vou ir um pouquinho mais longe. Vamos tentar abrir uma planilha do Excel. Excel que
é um editor de planilhas aí extremamente popular. Então, percebam que aqui eu tenho uma planilha onde a gente tem ali o curso, uma quantidade de módulos que esse curso contém e quantidade de horas gravadas desse curso. Tem vários cursos aqui, isso é só um exemplo. Eu posso salvar como um txt, também posso salvar como um XLSX. Vou salvar aqui como CSV. E agora eu vou tentar abrir esse meu arquivo no bloco de notas. Percebam que foi possível e as Minhas colunas estão separadas por vírgula. Pois bem, para abrir arquivos de texto, a gente já percebeu
que o bloco de notas consegue ler eles com facilidade, diferentes arquivos de texto. Mas pra gente abrir arquivos binários, nós temos ali vários caracteres super estranhos e nós precisamos ainda explicar um pouco melhor o que que são esses caracteres e porque eles aparecem. Pessoal, o seguinte, quando nós salvamos um arquivo binário, nós vamos estar ali copiando um intervalo da nossa memória RAM e jogando para um arquivo que vai estar no nosso HD, no nosso SSD em algum dispositivo de persistência de arquivos. Acontece que o que nós copiamos da nossa memória RAM não vai ter uma
ordem específica. Por exemplo, nesse caso aqui na nossa memória RAM, nós estamos ocupando aqui 4 bytes. Então, seria ali o equivalente de um inteiro, mas vamos fingir que na verdade nós temos um long double, então a gente estaria ocupando aí 16 bytes, quatro vezes mais do que esse espaço. E logo em seguida na nossa memória RAM, a gente tem um caracter. Depois a gente tem um long double e a gente tem um outro caracter. Como nós aprendemos a utilizar o struct, a gente sabe que aqui poderia ter uma estrutura, né, que carrega ali um long
double e um caracter. Essa estrutura poderia salvar um valor ponto flutuante, número real com vírgula e logo em seguida aí a qualidade, a importância desse valor, sei lá, numa empresa de exploração de Petróleo, não sei. Acontece que quando nós armazenarmos esse nosso arquivo no nosso HD, utilizando a persistência, o nosso bloco de notas não vai saber disso. Ele tenta ler esse nosso arquivo binário como se ele fosse um arquivo de texto. Então ele vai ler essa posição aqui, vai identificar qual caracter que é e vai mostrar. Vai ler essa, vai identificar qual caracter que é
e vai mostrar. Vai ler essa, vai identificar qual caracter que é e vai mostrar. Vai ler essa, vai identificar qual caracterí vai mostrar. E por aí vai. De vez em quando ele vai encontrar um caracter de quebra de linha, ele vai realizar uma quebra de linha pra gente. Então vamos inclusive dar uma olhada aqui no nosso bloco de notas, porque quando a gente tenta abrir o PNG, percebam que não há nenhum padrão. A quebra de linha acontece aqui, acontece aqui, acontece aqui, aqui, depois acontece antes. Então, à medida que ele vai encontrando esses caracteres e
o que tá lá dentro é totalmente diferente. Pra gente poder ler esse arquivo binário, a gente teria que saber se a gente tá lidando aí com long double, com o caracter, precisaremos entender o que que realmente tá sendo armazenado ali pra gente saber de qual intervalo até qual intervalo a gente vai estar analisando. Se ficou um pouco confuso, não se preocupe. é uma visão geral. Nós vamos entender isso muito melhor daqui a pouquinho, mas o que eu espero de você é que você entenda que existem arquivos de texto, arquivos binário. E agora eu vou falar
um pouquinho sobre quando utilizar cada um desses tipos. Pessoal, arquivos de texto eles carregam uma vantagem, né? A primeira vantagem que a gente pode perceber é que eu consegui abrir um arquivo CSV dentro do Excel e eu também consegui abrir esse arquivo no meu bloco De notas. Eu consegui abrir o meu arquivo ponto C dentro do Visual Studio Code e eu também consegui abrir no bloco de notas. Qualquer que seja o editor, caso você esteja no Mac ou caso você esteja no Linux, você também vai conseguir estar abrindo esses arquivos. Essa é a principal vantagem.
Então, vão existir vários programas que nós vamos criar, onde nós queremos simplesmente trabalhar ali uma informação e deixar ela dentro de um arquivo que um outro programa também vai abrir. Esse outro programa ele não vai precisar saber como está organizado esse nosso arquivo, porém como a gente viu, os arquivos de texto eles vão carregar consigo uma grande desvantagem quando a gente estiver armazenando vários números, uma quantidade grande de dados numéricos. Nós vamos gastar mais espaço de memória do que utilizando arquivos binários. Então, arquivos binários são muito úteis em bancos de dados, imagens que nós queremos
que ela ocupe ali o menor tamanho possível, mas eles carregam consigo essa desvantagem que não podem ser abertos por qualquer leitor de texto. Então, a imagem, o arquivo pjpg, ele só consegue ser lido por programas específicos. Pois bem, agora a gente vai começar aí a se aproximar mais da nossa prática. E quando a gente fala de manipulação de arquivos, a gente vai sempre aí tá tratando de uma dessas três operações. Ou nós vamos estar abrindo um arquivo, ou nós vamos estar fechando um arquivo, ou nós vamos estar manipulando esse Arquivo. Nós vamos entender o que
que é abrir um arquivo. Nós vamos entender por nós precisamos fechar um arquivo e depois nós vamos ver como manipular arquivos tanto do tipo binário como arquivos textuais. E a primeira coisa, claro, vai ser como abrir um arquivo. Pessoal, quando a gente abre um arquivo, já vamos dar uma analisada aqui na assinatura da função de abertura de um arquivo. Isso daqui é a assinatura de uma função. Então, nós temos parâmetros que vão ser enviados, um ponteiro para um caracter, que vai ser uma string. A gente aponta pro primeiro local da memória dessa nossa string. Ele
vai ir procurando até achar o caracter que indica o fim da string. Então, nós vamos ter aqui dois parâmetros a serem enviados. O nome da nossa função é f open. F open vem de file. Open. Open em inglês é abrir. File é arquivo, abertura de arquivo. E essa função vai nos retornar uma variável de um tipo diferente. Que tipo é esse? Esse tipo é o file. Esse é o nosso slide anterior tava falando exatamente sobre esse tipo. Quando um arquivo é aberto, uma cópia desse arquivo é alocado em um espaço de memória RAM. E para
ter acesso a esse espaço de memória, a linguagem C usa um ponteiro do tipo file. Nós vamos entender porque que optaram por criar essa struct, esse novo tipo. Bom, dos parâmetros enviados, Um deles indica o caminho, o diretório, aonde esse arquivo se encontra dentro do nosso sistema operacional. O segundo indica o modo de abertura que nós vamos ver no slide seguinte. O retorno é um ponteiro do tipo file que aponta para o primeiro endereço do arquivo na memória RAM ou nu, caso a gente não encontre esse arquivo. Vamos ver então o que são esses modos
de abertura. O primeiro modo é o modo apenas leitura. Read ele recebe a sigla R de read, que é leitura em inglês para textos. e RB read binary, que é leitura de binário em inglês. O segundo módulo é o W. Recebe essa sigla porque vem do inglês W de WR right. E a gente tem a opção também de utilizar o WB, write binary, escreva binário, pra gente poder estar escrevendo em arquivo binário. A outra opção é a letra A. A de after. Então nós temos aqui after para poder escrever somente ao final do nosso arquivo
e AB after binary para escrever somente ao final do nosso arquivo do tipo binário. Depois nós vamos ter combinações, mas vamos focar nesses três por enquanto. Vamos ver o que acontece quando nós estamos abrindo um arquivo de texto. Quando nós abrimos um arquivo de texto, por exemplo, tabela.csv, CSV é um arquivo de texto. Nós vamos carregar todo o conteúdo desse nosso arquivo dentro da nossa memória RAM. Vamos dar Uma olhada no nosso bloco de notas. Nós temos aqui curso, quantidade de módulos, vírgula, horas. Aí a gente chega no nosso caracter barra n. Então nós estamos
salvando, carregando na nossa memória RAM todo esse conteúdo. C, a letra C, a letra U, a letra R, a letra S, a letra O, a vírgula. Por aí vai até a gente chegar no nosso barra N ainda continuaria porque não teria terminado o nosso arquivo. E lá no final desse nosso arquivo a gente tem esse caracter aí que indica o final da nossa string. Seria uma grande string na nossa memória. Agora, se nós estivéssemos abrindo um arquivo binário, usar como exemplo aqui o zip, nós estaríamos carregando ali bit por bit dentro da nossa memória, da
mesma maneira que fizemos pro arquivo de texto. Porém, a gente precisaria ter uma estratégia para estar lendo esse espaço da nossa memória. Precisaríamos saber, por exemplo, se esse nosso arquivo armazena primeiro um long double seguido de um caracter. Então eu caminharia ali 16 bytes, depois eu caminharia 1 byte para ler o caracter e por aí vai. Mas o que é importante é que você perceba que o conteúdo do nosso arquivo ele é copiado pra nossa memória RAM. E para fazer isso, nós utilizaríamos o modo de leitura. A gente quer somente ler o nosso arquivo. Caso
a gente quisesse escrever no nosso arquivo de texto, aí já seria o contrário. A gente precisaria tirar um conteúdo, por exemplo, de uma variável que a gente criou. Se a gente tivesse uma string, escreveríamos dentro desse nosso arquivo tabela.csv. Para fazer isso, nós precisaríamos abrir este nosso arquivo com a permissão de escrita. Caso a gente quisesse escrever em um arquivo binário, também precisaríamos abrir. Lembrando que a gente tá pensando aqui somente em como abrir esse arquivo e quais as permissões existem. Então, se a gente quisesse escrever um arquivo binário, nós precisaríamos abrir com essa permissão
e nós copiaríamos o conteúdo do nosso endereço de memória para esse nosso arquivo. Pois bem, então a gente entendeu o modo de leitura, a gente entendeu o modo de escrita e aqui o modo de escrita ele é exclusivo. Então, caso a gente abra o nosso arquivo somente com modo de escrita, nós não vamos conseguir ler esse nosso arquivo. não é necessário carregar ele pra nossa memória RAM. Pois bem, esse outro termo, o A, ele serve quando a nossa escrita vai ser sempre ao final do nosso arquivo. Então, pode acontecer que você abra lá um arquivo
binário, você quer sobrescrever as informações que já estão lá. Então, você vai ter que utilizar o W de escrita WR. Caso você queira escrever somente ao final desse arquivo, você vai estar sempre adicionando novas informações. Eu só quero esse arquivo para salvar ali o valor de uma venda, um float, valor de uma venda. E eu vou salvando o valor de todas as vendas dentro desse meu arquivo e vou salvando ao final dele. Então você vai utilizar o a que vai limitar a sua escrita ao final desse arquivo. Pois bem, existem também os modos combinados. Então
a gente pode utilizar o R+. Ele vai permitir que a gente faça a leitura e a escrita, porém o arquivo deve Existir. Se o arquivo não existir, vai nos tornar um valor nulo. A gente vai ter um problema tentando abrir com essa permissão. Caso a gente utilize o W+, a escrita vem primeiro do que a leitura. O arquivo caso não exista será criado e caso ele exista, quando a gente escrever nele, nós vamos sobrecrever esse conteúdo. Caso a gente utilize o UA+, os novos dados serão sempre adicionados ao final do arquivo, mantendo o conteúdo anterior,
e nós também seremos capazes de ler esse conteúdo que está dentro deste arquivo. Existem também as opções para arquivos binários, então a gente sempre vai colocar o B ao final. E esses são os modos de abertura de um arquivo. Então nós vamos enviar o modo de abertura, o caminho deste nosso arquivo e vamos estar recebendo um ponteiro para uma variável do tipo file. Pessoal, eu mostrei para vocês que isso é copiado paraa nossa memória RAM. E se você já está ligeiro, se você pegou bem a ideia de alocação dinâmica, você sabe que quando nós falamos
de fechar algo, na verdade a gente tá falando de dar um free, de liberar. Então, se nós abrimos e precisamos fechar, assim como nós vimos nesse nosso slide que eu disse que basicamente nós iríamos abrir, fechar arquivos e manipular arquivos, é porque nós estamos fazendo uso de alocação dinâmica. Então, na prática, todo esse conteúdo é copiado para um novo espaço da nossa memória RAM. Então vai aumentar a quantidade de memória RAM que o nosso programa tá utilizando quando a gente abrir um arquivo e depois nós precisamos Liberar esse espaço. Então a assinatura da função de
fechamento, ela pede para que a gente envie um ponteiro para uma variável do tipo file. O nome dessa função é f close de file close, fechando o arquivo. E ela retorna um inteiro. Ela vai retornar zero caso esse nosso arquivo seja fechado corretamente ou ela vai retornar um inteiro positivo caso tenha ocorrido algum erro. Vamos dar uma olhada em um exemplo. Nós vamos aqui tentar abrir o arquivo 01.txt txt no modo de leitura e vamos fazer uma verificação. Se o retorno dessa nossa função for equivalente a nulo, é porque nós tivemos um problema na abertura
desse nosso arquivo. Olhando na nossa tabela sobre o modo de abertura, a gente percebe que ele é apenas para leitura e o arquivo deve existir. Caso o arquivo não exista, a gente vai obter ali um retorno nulo. Vamos cair dentro dessa nossa condicional e vamos imprimir o erro na abertura do arquivo. Aqui na tela a gente tem a função exict da biblioteca stdib. Essa função ela serve para forçar o encerramento do programa. A gente não fez uso dessa função anteriormente, mas ela simplesmente serve para interromper o nosso programa. Logo em seguida, nós fechamos o nosso
arquivo. Então, esse nosso código abre o nosso arquivo, verifica se teve algum problema para abrir esse nosso arquivo e depois ele fecha o nosso arquivo. Nós vimos que a função f close retorna um inteiro, mas nós não estamos fazendo nada com esse inteiro. Nós estamos ali ignorando, não temos uma variável para receber esse valor, mas se já abrimos corretamente, Provavelmente nós vamos conseguir fechar corretamente também este nosso arquivo. Então, antes da gente prosseguir, a gente entendeu o que que nós não conseguimos fazer. A locação dinâmica não tem nada a ver com persistência. Nós entendemos que
existem arquivos de texto e arquivos binários. a diferença entre eles. Falamos um pouquinho sobre quando utilizar cada um desses tipos. Vimos que nosso aprendizado nesse tópico vai envolver abrir, fechar e manipular o arquivo. E já aprendemos a abrir e fechar o nosso arquivo. Nós aprendemos que para abrir esse nosso arquivo, nós precisamos indicar o caminho desse nosso arquivo. Vou falar um pouquinho melhor sobre isso. E também precisamos indicar o modo. Vimos as funções. Função de abertura, f open, função de fechamento, f closes. Então vamos falar um pouco mais sobre o caminho do nosso arquivo. Então
aqui a gente está no VS Code. A gente está dentro de uma pasta chamada Froncaio. Dentro dessa pasta a gente tem outras pastas e a gente também tem arquivos aqui. Existem duas maneiras para que a gente informe o caminho do nosso arquivo. A primeira é o caminho do arquivo relativo. Esse relativo é porque ele é relativo ao diretório do executável. Prestem bastante atenção. O executável é o nosso código já compilado. Então, se a gente compilar esse arquivo main.cta Froncai, a gente vai gerar um executável main.xe aqui dentro. E aí a gente pode utilizar o caminho
relativo ao diretório Desse nosso executável. Então, nesse caso, se eu quiser, se eu tivesse aqui dentro, né, um arquivo ponxt, eu poderia estar lendo esse arquivo ptxt, mas eu não vou ter. Vou deletar ele aqui, porque na verdade vou ter uma pasta arquivos e dentro dessa pasta arquivos eu vou ter o meu arquivo ptxt. Então eu posso colocar aqui arquivos, que é uma pasta barra arquivo.txt. Claro, pessoal, que eu poderia também utilizar o caminho absoluto. Esse caminho absoluto, né, seria o caminho desse meu arquivo dentro do meu sistema operacional, desde a sua primeira pasta, ou
melhor, do seu primeiro disco, né, no Windows, tem esses dois pontos. Então, estaria aí algo parecido com C2./home/caio bardocumentos arquivo.txt. Até aumentar o tamanho aqui para ficar mais claro para vocês. Esse conhecimento sobre caminhos de arquivos ele já vai ser suficiente para nós, mas tem outras coisas que poderiam ser feitas. Então, por exemplo, se eu quisesse acessar essa pasta documentos utilizando o caminho relativo, eu poderia voltar, né, dois pontos aqui, eu voltaria pra pasta documentos e aí eu pegaria aqui a pasta arquivos que estaria dentro dessa pasta documentos. Mas a gente não vai se preocupar
com isso agora. Por quê? Porque esse é um conteúdo que a gente vai aprender quando a gente for estudar sobre sistemas operacionais. Então, por enquanto, basta a gente saber aí que existe o caminho relativo, ele vai ser a partir do diretório do executável e existe o nosso caminho absoluto. Pois bem, então nós já vimos aí como abrir e fechar arquivos. Nós já aprendemos um pouco mais sobre o caminho de um arquivo. Então, vamos aprender a ler um caracter dentro de um arquivo. Vou copiar esse nosso código para esse nosso arquivo main.c C e vamos analisar
o que tá sendo feito aqui. Nós estamos abrindo esse nosso arquivo no modo de leitura. Nós estamos verificando se o retorno dessa tentativa de abrir esse nosso arquivo da função file open é nulo. Se ele for nulo, a gente vai informar que a gente teve um erro a abrir o nosso arquivo. Por exemplo, se eu tivesse colocado aqui arquivo, ao invés de arquivos, ele me indicaria ali que houve um erro. E nós vamos ler um caracter. Para ler um caracter, nós vamos utilizar essa função fget c de caracter, né? Então, a gente tá lendo um
caracter de um arquivo. Como nós estamos lendo um caracter, nós estamos lendo 1 byte, 8 bits. E essa função fget vai nos retornar um valor inteiro, mas nós vamos armazenar esse valor inteiro dentro de um chá, porque na tabela ask, né, valores inteiros são caracteres. Aí a gente vai imprimir esse caracter lido e a gente vai fechar esse nosso arquivo. Qual o arquivo que a gente tá lendo? O arquivo. TXT dentro da pasta arquivos. E aqui dentro a gente tem escrito frontal. Então a gente vai ler o primeiro caracter. Vamos executar esse nosso código. E
nós obtivemos o caracter F. Muito bem. Se eu tentar ler um segundo caracter, a gente vai encontrar a letra R. Vamos executar pra gente ver. Então, percebam que quando a gente lê um caracter, é como se a gente caminhasse ali na nossa memória RAM, porque a gente carrega esse conteúdo do nosso arquivo pra nossa memória RAM. E aí quando a gente lê, é como se a gente andasse, né? O nosso ponteiro vai sair daqui do início e ele vai vir para cá, como se esse fosse o início da nossa string que tá ali na nossa
memória ram. se tudo vai ficar se tudo vai ficar mais claro aos poucos vai ficando mais claro. Vamos ver então como a gente estaria lendo vários caracteres. A gente estaria abrindo o nosso arquivo, verificando se houve um erro, criando uma variável do tipo char e a gente vai utilizando a função fgetc enviando esse nosso arquivo, enquanto nós não encontrarmos o caracter que indica o fim do nosso arquivo. Esse EOF é de end of file. Então, basicamente a gente tá lendo caracter por caracter agora utilizando uma repetição. E a gente vai continuar nesse loop até que
a gente chegue no final do nosso arquivo. Vamos executar pra gente ver. Muito bem, lemos ali todos os caracteres até chegar o final do nosso arquivo. Lembrando que a nossa função fgetc eventualmente ela vai encontrar um caracter ali que vai indicar, né, o final do nosso arquivo. E aí a gente vai comparar esse conteúdo com esse nosso ofile. Então, enquanto for diferente, enquanto a gente não estiver no final do nosso arquivo, nós vamos continuar lendo esse nosso arquivo. Então, a gente aprendeu a ler aí caracteres e a gente aprendeu a ler strings, né? A reis
de caracteres, basicamente lendo todos os caracteres ali dentro de um loop. Agora vamos ver como a gente escreve em um arquivo, como a gente altera esse nosso arquivo. Percebam que agora nós vamos estar abrindo o nosso arquivo com a permissão de escrita. A gente possa escrever. A gente não quer nem ler esse arquivo aqui, não importa pra gente nesse momento. Aí nós vamos criar aqui três variáveis, uma do tipo inteiro, outra do tipo double e uma string. Para que a gente possa escrever nesse nosso arquivo, a gente vai utilizar a função fp print f. Nós
vamos passar ali o nosso arquivo, lembrando que a gente obtém esse nosso arquivo da função fop, é do tipo file. Como segundo parâmetro da nossa função, nós vamos formatar ali o conteúdo dessas nossas variáveis. Vou indicar que aqui vai ter um inteiro, aqui nós vamos ter aqui nós vamos ter um double e aqui nós vamos ter uma string. Aí os próximos parâmetros são as variáveis. E aí a gente vai fechar esse nosso arquivo. Vamos executar esse nosso Código. Ele não vai imprimir nada. Nós não imprimimos nada na tela a não ser o erro, né? Caso
a gente não tivesse conseguido abrir esse nosso arquivo. Mas observem que agora nós salvamos ali, escrevemos o nosso arquivo, pegamos variáveis e transformamos em um texto, né? Escrevemos no arquivo de texto. Aqui, pessoal, eu pulei dois conteúdos porque a gente poderia utilizar só essa função f print f, né? pra gente escrever ali caracteres. Utilizando essa nossa formatação, a gente passa o nosso arquivo. Se a gente quiser escrever só um caracter, a gente coloca aqui uma string que tenha somente um caracter. E aí a gente consegue estar escrevendo. Mas existe um outro jeito, né? A gente
também tem a função, né? F caracter. Então vamos dar uma olhada nesse código aqui rapidamente. Aqui a gente tá abrindo o nosso arquivo com a permissão de escrita. Verificando, a gente cria aqui uma uma string que aceita até 100 caracteres. A gente pede pro nosso usuário digitar a função gets, né? Ela aceita espaços, mas para no primeiro enter. Então o usuário pode digitar ali uma frase. Na hora que ele apertar enter, a gente vai então escrevendo ali caracter por caracter dentro do nosso arquivo. Caso a gente quisesse, a gente poderia escrever nesse nosso arquivo utilizando
a função f print f. Desta maneira aqui. Existe também a função fs, né? File push to string, onde a gente tem ali uma frase, uma string, assim como a gente tinha no exemplo de cima. Mas em vez da gente ter que ficar iterando, escrevendo caracter por caracter, a gente passa a string como primeiro parâmetro, o arquivo como segundo e a gente consegue estar escrevendo lá dentro. Qual que é a principal diferença? Se eu tiver utilizando ali o fpt c, né, de caracter ou fp s de string ou tiver utilizando a função f pr print f,
a função f pr print f ela converte, né, o conteúdo ali de uma variável numérica, né, um valor numérico para um caracter. Então a gente vai est convertendo ali, por exemplo, esse número, né, double pra string. uma string que vai ter ali 1 p 65 e por aí vai. Mas a função f print f ela só vai converter quando for necessário. Se a gente já tiver utilizando uma string, né? Não, não tem por tá realizando essa conversão, OK? Então nós vimos aí como que a gente lê um caracter, como que a gente lê vários caracteres,
como que a gente escreve um caracter, né? como que a gente pode escrever uma string e como que a gente pode passar também ali variáveis, coisas numéricas para uma string escrevendo o nosso arquivo do tipo texto. Até agora a gente só viu a leitura e a escrita em arquivos do tipo texto. O que falta pra gente ver ali sobre arquivos do tipo texto é como que a gente pega, né, dentro desse nosso arquivot esses valores e transforma em uma variável no nosso programa, string para variáveis. Então nós vamos abrir o nosso arquivo com a permissão
de leitura. Nós vamos criar aqui o in, o double e dois Chars. E nós vamos utilizar a função f scan f. A função f scan f, ela recebe os seguintes parâmetros. Primeiro, ela recebe o ponteiro pro nosso arquivo. Depois ela recebe o formato dos dados que serão lidos. Então, a gente vai ler ali uma string e um inteiro. E a gente vai armazenar na variável texto, que é de string, e a gente vai armazenar na variável X, que é de inteiro. Depois nós vamos armazenar ali um string e um log double. E nós vamos armazenar
depois uma string e outra string. E vamos estar fechando esse nosso arquivo. Bem, para que esse programa funcione, então a gente vai ter que ter algo mais ou menos desse formato aqui, ó. vai ser uma string inteiro, um valor. Depois nós vamos ter uma string e um long double. Então nós vamos ter aqui, ó, long double e depois nós teremos duas strings. E aí nós vamos estar transformando o conteúdo de texto que nós temos aqui em um conteúdo na nossa memória RAM, né, dentro dessas nossas variáveis já separado, né, onde a palavra inteira vai ser
armazenada numa string, o valor número dois vai ser armazenado numa variável do tipo inteiro, a palavra longa double vai ir para uma string e assim vai. Vamos executar esse nosso código aqui e funcionou tranquilamente que a gente tá imprimindo essas nossas variáveis. Então, pessoal, sobre a manipulação de arquivos, a gente viu como que a gente lê um caracter, a gente viu como que a gente lê vários caracteres, uma string, a gente viu como que a gente escreve um caracter dentro de um arquivo, a gente viu como que a Gente escreve uma string dentro de um
arquivo. E a gente viu também como a gente converte variáveis numéricas para uma string e logo em seguida escreve dentro do arquivo. A função f print f já faz essa conversão escrita para nós. E por último, nós vimos como nós pegamos uma string que tá dentro de um arquivo e salvamos dentro de uma variável de um tipo determinado por nós. Tudo isso trabalhando com arquivos de texto, não com arquivos binários. Então, antes da gente ver aí como que a gente vai fazer para trabalhar com arquivos binários, trocar uma ideia sobre o que que são arquivos
binários, deixar bem claro a diferença deles para arquivos de texto. Vamos ver como que a gente faz para mover dentro do arquivo. Então vamos dar uma olhada nesse nosso código aqui. E aí a gente vai estar aqui abrindo o arquivo com a permissão de escrita e leitura. Nós vamos salvar nesse nosso arquivo a dois pontos espaço e o valor número dois quebra de linha. B do pontos espaço valor número 5 sem quebra de linha. E ao fazer isso, o ponteiro do nosso arquivo, ele vai estar lá no final. Então a gente tá escrevendo no nosso
arquivo, a gente vai estar no final dele. Inclusive, se a gente repetisse essa linha, a gente escreveria isso de novo ao final do nosso arquivo. A primeira coisa que a gente precisa fazer para que a gente depois de ter criado esse arquivo e escrever nele, possa estar lendo ele desde o começo, é voltar para o início. Como que a gente faz isso? A gente vai fazer isso utilizando a função fse seek é procurar em inglês f de file. Então Procura dentro do arquivo. Nessa função, nós vamos passar o ponteiro para o arquivo, o arquivo que
a gente abriu. Nós vamos passar a quantidade de bytes que a gente quer se mover. No caso, a gente não quer mover nenhum. E a partir de onde nós queremos nos mover. E aqui nós vamos passar o início do nosso arquivo. O que é isso? Essa é uma variável global que faz parte da biblioteca STDIB. E tem três variáveis que a gente precisa conhecer. A variável si set, que indica o início do nosso arquivo, SC current, que é a posição atual do nosso arquivo, e SC end, que seria o final do nosso arquivo. Essa nossa
função fic, ela pode receber aqui valores negativos também. Então, por exemplo, se a gente estiver no final do nosso arquivo e a gente quer voltar um caracter, a gente vai passaria ali o valor -1. Depois de resetar a posição do nosso arquivo para o início, nós vamos então estar utilizando a função fcan f, que nós utilizamos quando vimos ali strings para variáveis. E agora já trazendo aqui uma explicação diferente para vocês. Na função f scan f, tanto o barra t quanto barra n, quanto o espaço, eles são simplesmente separadores. Então a gente não precisa escrever
aqui o barra n, tá? E a gente vai conseguir estar armazenando esses valores do nosso arquivo dentro dessas nossas variáveis, pois a gente vai estar realizando a impressão da string, que vai ser a dois pontos. e do valor inteiro que vai ser dois. Vamos executar esse nosso código Aqui e percebam que ele está funcionando como esperado. Bom, e como agora a gente tá chegando aí no final do nosso curso, né, eu quero mostrar para vocês também algumas coisas um pouco diferentes, né? Talvez essa necessidade surja e aí você já vai ter conhecimento da existência disso.
A gente pode criar, né, um arquivo temporário. Então aqui a gente está criando uma variável do tipo file, que não tem nada. a gente tá armazenando, né, um string dentro dessa variável texto. Estamos criando uma variável char sem nada e a variável stop sharp file. Esse método ele cria um arquivo temporário na memória. Esse arquivo temporário ele é criado com modo ele de leitura escrita e é automaticamente excluído quando o ponteiro do arquivo ele é fechado. Quando a gente dá um f close, automaticamente esse arquivo é excluído aí da nossa memória. Então a gente tá
chamando esse método aqui. Por quê? Porque eu só quero demonstrar para vocês algo. Eu não preciso realmente criar esse arquivo. Então eu crio esse arquivo temporário e vocês vão entender porque que criar um arquivo eh tem um custo, né? Por enquanto a gente tá aprendendo só a utilizar, mas quando a gente vai estudar esses temas operacionais, a gente vê que não é uma tarefa tão simples. Ela pode ser um pouco lenta, ela pode ser um pouco demorada, às vezes a gente quer evitar. Mas então, basicamente aqui a gente tá criando um arquivo temporário. Se o
nosso arquivo temporário for criado com sucesso, a gente não vai imprimir um erro, nem vai sair do nosso programa. E aí a gente vai escrever a nossa string texto dentro desse nosso arquivo. Depois que a gente escrever, a gente vai tá lá no final do nosso arquivo, a gente vai precisar voltar do início. Então a gente vai voltar do início, assim como a gente fez no último programa. A partir do Início, a gente vai caminhar nesse nosso arquivo, né, até o próximo caracter, depois do ponto de exclamação. Então, a gente tem um loop, nós vamos
caminhar nesse loop até a gente encontrar, né, o caracter que seja igual ao caracter parada, que a gente colocou o ponto de exclamação. Quando a gente encontrar o caracter parada, a gente vai caminhar mais um caracter e vamos sair desse nosso loop. Saindo desse nosso loop, nós vamos imprimir o restante deste nosso arquivo. Para imprimir o restante desse nosso arquivo, vou mostrar para vocês aqui uma função que eu também não tinha mostrado, que é a putar. Até então a gente tinha usado só o print. A gente pode usar essa putar para imprimir um caracter no
nosso terminal. Então a gente vai est imprimindo ali esses caracteres restantes. Por que que a gente lê depois de encontrar o ponto de exclamação? Porque a gente vai chegar aqui, se a gente saísse do nosso loop, a gente está aqui, a gente imprimir esse espaço vazio. Então, a gente tá lendo esse espaço vazio. Depois a gente vai ler o T e vai imprimir, vai ler o H, vai imprimir, vai ler o I, vai imprimir. Mas basicamente aí bem parecido com o código anterior. A única diferença é que agora tô utilizando aqui um arquivo temporário e
também tô utilizando uma função pra impressão que vocês não estão acostumados. Estou trazendo isso para que você fique tranquilo quando você se deparar com alguma coisa, alguma função que você não conhece. Talvez seja algo muito simples que faça algo equivalente de uma função da qual você domina. Então vamos rodar esse nosso arquivo. Aconteceu tudo bem, né? Só tivemos um problema aqui com os nossos acentos. This is a tutorial using f6. Esse é um tutorial utilizando FSIC. A gente não imprimiu o hellow hoje, a gente caminhou até essa parte do nosso arquivo e realizou essa impressão.
Muito bem. Então, pessoal, então o que a gente precisa aprender agora é escrever em um arquivo binário e a gente precisa também aprender a ler de um arquivo binário e a se movimentar dentro de um arquivo binário. Mas antes da gente aprender quais funções a gente vai utilizar para est fazendo isso, eu quero conversar um pouco melhor com vocês sobre a diferença entre arquivos de textos e arquivos binários. Pessoal, na verdade, na verdade não existe essa diferenciação entre arquivos de textos e arquivos binários dentro do nosso computador. A nossa memória RAM, ela contém simplesmente bits
que podem assumir o valor zero ou um. Porém, nós dizemos que temos um arquivo de texto, pois nós sabemos que nós vamos ter que caminhar na nossa memória RAM 8 bits, ou seja, 1 byte a cada vez. Não importa o tamanho do nosso conteúdo, nós vamos sempre caminhar de 8 bits em 8 bits, ou seja, de 1 byte em 1 byte. Isso vai acontecer quando a gente carregar um arquivo de texto na nossa memória. A gente sabe que a gente vai estar caminhando nele dessa forma. E é claro, pessoal, que isso vai afetar nossas funções,
inclusive de movimentação. Então, quando a gente coloca lá para movimentar uma posição num arquivo de texto, a gente sabe que a gente vai estar movimentando para cá. Quando a gente coloca para aumentar, movimentar duas posições, a gente sabe que a gente vai estar movimentando para cá. Agora, e se a gente estiver falando ali sobre um arquivo binário, já no Nosso arquivo binário, não existe nenhuma garantia de que a gente deva caminhar dessa forma. Então a gente pode ter ali uma rede struct, né, que a gente aprendeu, onde vai ter um caracter, logo em seguida vai
ter um long double, logo em seguida vai ter um long intractido por mais dois longes. Então imagine só o que aconteceria caso eu tentasse, né, ler esse meu arquivo binário como um arquivo de texto. Isso é possível? Sim, é possível, tá? Eu posso pegar um arquivo binário e abrir ele como arquivo de texto. Não vai dar erro nenhum. Só que o que que vai acontecer? Eu vou tentar ler esse arquivo partindo da premissa de que a cada 8 pits eu vou ter um caracter. Então vou ler essa posição aqui, vou encontrar um barra n. Aí
eu vou ler essa posição aqui, o que que eu vou encontrar? Não sei o que que esses bits vão me informar, né? Da mesma maneira eu não sei o que aconteceria quando eu lesse esse terceiro, né? bit. Se eu tivesse interpretando esse arquivo binário como um arquivo de texto. Por isso aparece aqueles caracteres aleatórios, tá? Algumas questões que geralmente surgem quando vocês se deparam com esse conteúdo. Ah, então vou salvar o meu arquivo como um binário e isso vai servir como se fosse uma criptografia, né? Basta eu salvar o meu arquivo binário. Eu tenho um
programa que vai ter vários dados, vários valores numéricos. Eu salvo aquilo ali como um binário e ninguém vai conseguir ler esses dados. Pessoal, isso não é uma criptografia, tá? Por quê? Porque imagine só que eu posso caminhar bit a Bit. Eu faço um programa que caminha bit a bit e eu sei que eu tô procurando valores float, né? Tô abrindo aquele arquivo ali, eu quero acessar aqueles valores, valores float. Então eu pego aquele arquivo e tento ler como um float. Aí, se não der eu pego o próximo, leio como float, pego o próximo e leio
como float, até eu encontrar arquivos que fazem sentido, tá, pessoal? Então, não é uma forma de criptografia, mas sim vai dificultar a leitura. Se você não souber os tipos e a ordem de que as variáveis foram armazenadas nesse arquivo, você vai ter essa dificuldade. Mas é muito tranquilo de quebrar, vamos dizer, computacionalmente. Não é nada como quebrar uma criptografia. Pode demorar um tempo, mas você vai conseguir tá quebrando aquele arquivo binário. Então não é uma criptografia, tá? Mas sim vai dificultar a leitura caso você não saiba. Então você vai falar assim: "Poxa, mas se vai
dificultar a leitura, por que que eu não armazeno tudo em texto?" Percebam, pessoal, que armazenar em texto tem algumas desvantagens. Se eu tiver armazenando um número, esse número aqui ele vai ocupar vários caracteres. Por exemplo, o número inteiro, se eu colocasse o número aqui 2025, né? ele ocuparia 1 2 3 4 caracteres e 2025 utilizando um inteiro ocuparia somente 1 byte. Então a gente sai de 4 bytes para salvar 2025 arquivo de texto para 1 byte para salvar 2025 em um arquivo binário. Para deixar isso aí mais claro, vamos comparar aqui quando a gente deve
estar utilizando cada um desses tipos. Bom, quanto à legibilidade, um arquivo de texto ele é muito melhor. Por quê? Porque você vai ter ali diversos programas que estão preparados para ler um arquivo de texto. Já um arquivo binário, ele não vai ser legível diretamente. Você vai ter que saber o que está dentro daquele arquivo. Você vai ter que saber como ler aquele arquivo. Porém, apesar do arquivo binário ter uma legibilidade dificultada, ele tem uma maior eficiência. ele é mais eficiente porque ele armazena no formato binário. Já o arquivo de texto, ele é menos eficiente porque
ele armazena dados como texto. Então, se a gente tiver trabalhando com dados numéricos, o arquivo binário ele vai ser muito mais eficiente, ou seja, ele vai ocupar um espaço menor da memória. E não só isso, ele também vai permitir que a gente não precise ficar convertendo de string para dados numéricos. Agora, se a gente for salvar um arquivo de texto cru, aí a eficiência vai ser a mesma. Mas percebam, se a gente utilizar uma structe, mesmo salvando o texto, um arquivo binário, porque ele já vai estar organizado ali dentro. Quanto ao tamanho, já comentei aqui,
né? O arquivo de texto ocupa mais espaço, o arquivo binário ocupa menor espaço. Quanto à portabilidade, o arquivo de texto, ele tem alta portabilidade. Você vai conseguir ler ele em diferentes dispositivos, diferentes sistemas operacionais. sempre vai ter um programa ali capaz de ler arquivos de texto. Já o arquivo binário, ele vai ter uma baixa compatibilidade. Então você vai precisar instalar softwares específicos que estão preparados para ler aquele tipo de arquivo. Então você não consegue ler um arquivo de imagem, por exemplo, né, que tem ali valores numéricos que informam, Né, as cores de um RGB, enfim.
Eh, você vai precisar de um programa que tá preparado para ler um arquivo de imagem. exemplos de usos, né? A gente utiliza arquivos de texto para arquivos poncv, né? Arquivos.Csv são arquivos de texto, são com separate vales, são valores separados por vírgula. A gente utiliza, né, em relatório simples, a gente utiliza em logs. Logs é pra gente estar salvando, né, todas as chamadas que foram executadas ali dentro de um programa, pra gente poder manter ali uma informação salva sobre o que que o usuário fez naquele programa. Como exemplo de uso de arquivos binários, a gente
tem bancos de dados, imagens e executáveis. Então, agora vamos ver quais funções a gente utiliza para est escrevendo em um arquivo do tipo binário. Então aqui a gente tá abrindo um arquivo do tipo binário, utilizando aqui a nossa flag WB escrita em binário. Nós estamos criando três variáveis, né? no caso, duas variáveis e um array de chars, ou seja, uma string. E aí nós vamos utilizar a função fright. Os parâmetros que essa função vai receber vão ser os seguintes. Primeiro, ela vai receber o endereço da variável que a gente quer escrever dentro do nosso arquivo.
Então, a gente tá passando aqui o endereço da variável A nessa primeira chamada dessa função. Depois a gente vai receber o tamanho do dado que a gente vai estar escrevendo, o tamanho de um inteiro, a quantidade de dados, como se trata de uma variável, a quantidade é um e o ponteiro para o arquivo. E aí, beleza, nós vamos estar escrevendo a nossa variável A dentro do nosso arquivo. Claro, né, pessoal, o ponteiro vai estar se movimentando ali pro final Do nosso arquivo. Depois a gente vai fazer a mesma coisa com a variável B e depois
vamos enviar ali uma string para esse nosso arquivo binário. A gente pode armazenar strings dentro de um arquivo binário sem problemas. Aí, nesse caso, a gente não precisa nem utilizar o nosso operador de endereçamento, porque toda string, ela, na verdade, é o endereço do primeiro caracter. A gente manda o tamanho, que é seis. E tranquilo, vamos estar escrevendo aí dentro desse nosso arquivo. Vamos agora testar esse código. E percebam que eu coloquei aqui o nome arquivo.txt, apesar de ser um binário, tá? Então vamos dar uma olhada. Olha só, a gente tá tentando ler esse arquivo
como um txt, mas lá dentro, né? Mas lá dentro o que nós temos são duas variáveis seguidas de uma string. A string a gente até consegue ler, mas as variáveis armazenadas aqui elas não fazem sentido como um texto. Você deve estar se perguntando: "Ah, então por que que tem o ponto txt?" O ponto txt ele serve para indicar pro programa que tá abrindo aquele arquivo como aquele arquivo deve ser lido. Mas eu posso tentar ler um binário como um txt. E a gente fez isso quando a gente pegou aqueles arquivos e abriu no bloco de
notas. Vamos dar uma olhada em como a gente faz para est escrevendo um arrei. A mesma coisa, né? A gente escreveu ali um arrei de caracteres, uma string, mas aqui agora a gente vai est escrevendo aí um arreio do tipo double. Então a gente passa o tamanho, né, a quantidade de elementos que tem dentro desse nosso arreio, o tipo do dado e o endereço desse nosso arrei. Essa eu não vou nem fazer com Vocês. Fica o convite para que vocês acessem o nosso repositório. Lá onde ficam as listas de exercícios, vocês também tm acesso a
todos esses códigos. Agora vamos ler arquivos binários. Como que seria isso? A gente vai pegar um arquivo que está dentro de um disco sólido e a gente vai carregar ele na nossa memória RAM, ou seja, puxar esse conteúdo para algumas variáveis. Para fazer isso, a gente vai abrir esse nosso arquivo com a permissão de leitura. Nesse caso aqui, tá abrindo com a permissão de escrita. Vamos alterar. Bom, e aqui a gente leu o nosso arquivo tranquilamente, tá, pessoal? Eu só troquei aqui pro leitura de binário, né? Porque caso a gente estivesse utilizando a sigla W
+ B, a gente iria estar criando esse arquivo caso ele não existisse. Eu não queria que isso acontecesse, tá? Então a gente ia criar o arquivo e aí depois a gente ia tentar ler um arquivo que não teria nada lá dentro. Então só para evitar isso, eu coloquei aqui o RB. Bom, agora sobre como que a gente vai fazer para se movimentar dentro desse nosso arquivo. Para se mover dentro desse nosso arquivo, a gente vai utilizar a mesma função FSIC. Mas agora a gente precisa prestar atenção, né, em como a gente vai estar andando nesse
nosso arquivo. Utilizando a função fcemos, né, utilizar aqui, por exemplo, size of int. E a gente vai se movimentar ali a quantidade de bytes que tem dentro de um inteiro, que no caso é um, né? E se a gente quiser se movimentar ali pra posição três de uma ar inteiros, então a gente faz três vezes size of int. Então a gente vai ter que tá sempre pensando nisso. Se a gente tiver utilizando uma structía, nossa structia se movimentar utilizando zero e um, né? Porque a gente tá sempre se movimentando 1 byte. e 1 byte é
exatamente o tamanho de um caracter. Então agora a gente pode ter variáveis ali que vão ocupar um tamanho um pouco maior, mas a função que a gente vai estar utilizando vai ser a mesma. Inclusive isso vai ser uma questão da lista de exercício para que vocês possam estar praticando. Agora, para finalizar esse tópico, eu quero conversar com vocês um pouquinho sobre a motivação por trás desse nosso estudo de persistência em arquivos. Lá no começo, eu sei que eu dei a entender, né, que nós estávamos aprendendo a salvar os nossos arquivos para que a gente pudesse
ter um programa cujos dados seriam mantidos. Isso não é mentira. Porém, eventualmente vocês vão aprender a utilizar bancos de dados, que vai ser uma maneira melhor, mais eficiente, mais segura, né, de você manter ali os dados do seu programa. Ainda assim, esse assunto ele é extremamente importante. Por quê? Porque quando a gente precisa tá enviando informações na internet, inclusive esse vídeo que chegou até você, né, ele chegou em algum tipo de formato, pode ser um ponto MP4 e aí o seu navegador vai est executando esse vídeo. Pode ser que você esteja consumindo um PDF, o
PDF das aulas de exercício. Pode ser que você esteja utilizando um programa para abrir esse PDF. Esse programa vai ter vindo para você em um arquivo executável. Então, todos esses arquivos são arquivos que persistem na nossa memória e a gente precisa criar esses arquivos para mandar informação pela internet. Uma outra coisa também que envolve esse assunto, geralmente quando a gente tá falando aí de hacking, nós estamos falando de conseguir acesso a algum arquivo que a Gente não deveria ter, invadir esse arquivo, né, retirar informações desse arquivo. Então, caso o seu computador esteja sendo hackeado, provavelmente
vai ter alguém acessando arquivos que não deveria estar acessando dentro da sua máquina. E esse assunto eu tentei trazer ele de uma forma que a gente aprendesse ali a utilizar, mas vão ficar algumas lacunas. Essas lacunas envolvem um conhecimento de sistema operacional. Por quê? Porque na verdade, né, o nosso programa ele não vai diretamente no nosso HD e salva esses nossos arquivos. Ele pede pro sistema operacional. O sistema operacional verifica as suas permissões de usuário, vê se você pode escrever naquele lugar ali da memória e aí o sistema operacional escreve para você. Cada sistema operacional
vai fazer de um jeito, mas de modo geral, né, o iOS, o Android e o Linux vão ser bem parecidos à chamadas que a gente vai fazer e o Windows vai ser um pouco diferente. Então, estou dizendo tudo isso pr que vocês entendam que mesmo que você venha utilizar bancos de dados, né, pr salvar os arquivos, é importante, esse conteúdo, ele é muito importante em diversas outras áreas e é importante que você saiba classificar esses arquivos como arquivos de texto ou arquivos binários e manipular esses nossos arquivos. E aprendendo a fazer isso na linguagem C,
vai ser bem tranquilo fazer o mesmo em qualquer outra linguagem. Bom, uma outra coisa também antes da gente finalizar, quero conversar com vocês sobre o que nós não estudamos. Dentro dessa nossa aula, pessoal, a gente aprendeu sobre gerenciamento de arquivos, a gente aprendeu sobre os tipos de leitura, mas a gente não aprendeu sobre as permissões, né? Então, A gente não sabe como que a gente faria para ler um arquivo, para tentar conseguir a permissão de ler um arquivo que tá bloqueado. A gente não sabe como bloquear certas permissões de um arquivo para que outras pessoas
não consigam estar escrevendo, só lendo aquele arquivo ou que não consigam ler aquele arquivo, somente escrever nele. E a gente também não sabe nada sobre gerenciamento de diretórios. Então, a gente sabe ali acessar o nosso arquivo utilizando o caminho relativo ou utilizando o caminho global, mas a gente não sabe como mudar o diretório da nossa aplicação, né? Como é que a gente faz para mudar aquele nosso programa que tá sendo executado naquele local, a gente quer trocar ele para um outro lugar? A gente não sabe nada disso. Então, a gente aprendeu ali sobre gerenciamento de
arquivos, mas a gente não aprendeu sobre gerenciamento de permissão de arquivos e a gente também não aprendeu sobre gerenciamento de diretórios. A gente não aprendeu a criar diretórios. nosso programa em C poderia tá fazendo isso, tá? Mas esse conteúdo aí vai ficar para quando a gente for estudar sistemas operacionais, chamadas de sistemas, onde a gente vai pedir pro nosso sistema operacional criar uma pasta pra gente. E claro, vão existir os métodos ali, dá para fazer tudo isso tranquilamente na linguagem C. Bom, pessoal, agora vocês podem estar indo fazer a lista número nove, a nossa lista
sobre persistência. Lembrando que essa é a única maneira de realmente aprender praticando. Então, não deixa a gente fazer essa nossa lista. Muito bem. Então, antes da gente entrar aí nos últimos dois tópicos do nosso curso, eu quero relembrar vocês de algo que eu disse lá no comecinho. Aqui eu não vou entrar em detalh sobre todos os tópicos que serão abordados, porque para aqueles que estão aprendendo a sua primeira linguagem de programação, Muitos deles podem não fazer sentido, mas fiquem tranquilos, pois todos eles serão abordados com extremo detalhe. E acontece que agora a gente já tem
condições de entender muito bem todos os tópicos que nós estudamos e todos os tópicos que ainda estudaremos. E só para relembrar, no nosso tópico um, referente à nossa primeira lista de exercício, nós vimos do que se tratava um compilador, o que que esse compilador fazia, né? Como que a gente gerava um programa, um arquivo executável. Nós aprendemos sobre variáveis, as seis propriedades de uma variável: nome, endereço, valor, que é o conteúdo que tá armazenado, o tipo dessa variável, o escopo, ou seja, de onde ela pode ser acessada, e o tempo de vida. Nós vimos também
todos os tipos primitivos de variáveis, aprendemos sobre entrada e saída, como que a gente recebe informação que o usuário digita e aprendemos também como que a gente mostra essa informação através do terminal. Nós vimos ainda nesse nosso primeiro tópico os nossos operadores, os operadores relacionais, onde a gente compara valores. Nós vimos os nossos operadores aritméticos, onde nós realizamos operações matemáticas, operações aritméticas e nós vimos também os nossos operadores lógicos e ou e o de negação. No nosso próximo tópico, a gente falou sobre as estruturas de controle, as estruturas de controles que se dividiam em estruturas
condicionais e estruturas iterativas. De estrutura condicional, nós aprendemos os comandos ifels e o comando switch. E de estruturas iterativas, nós aprendemos os comandos while, do while e for. Depois a gente estudou sobre arre. Nós aprendemos como inicializar os arreis, né? Quando a gente cria um arrei, ele já tem um valor Lá dentro. A gente aprendeu sobre a rei de duas dimensões, a rei de três dimensões, a rei de N dimensões. Nós aprendemos sobre strings. Aprendemos como utilizar a função fgets para receber informação como uma string. Aprendemos a utilizar as funções a toy, a to integer,
a tof, a to float, a tall, array to long double, que são funções que a gente utiliza para converter uma rei de caracteres, uma string para um tipo primitivo. A gente aprendeu também a utilizar a função sprint f para converter uma variável, conteúdo de uma variável de tipo primitivo para uma string. A gente também falou sobre cas e coersão. Cash é quando a gente muda o tipo de um dado e coersão é quando isso acontece sem que a gente mude esse tipo. Por exemplo, quando a gente pega e armazena um valor do tipo float numa
variável do tipo int, a gente não converteu explicitamente. Então não é um cast, é uma coção que acontece. O compilador vai lá e simplesmente trunca aquele valor e ele passa a ser um inteiro, mesmo que você não tenha convertido de maneira explícita usando os parênteses. Aprendemos também sobre registros, né? Fiquei desenhando bastante com vocês. Nós vimos que registro, na verdade, são novos tipos que a gente pode criar utilizando os tipos que a gente conheceu anteriormente. Aprendemos também sobre funções. Funções que na verdade são códigos que a gente chama, né? passa alguma informação ou não passa
nenhuma e a gente recebe alguma informação ou não recebe nenhuma. Vimos também como funcionavam as funções recursivas, uma função que chama a si mesmo, né? E Eventualmente ela para e começa a retornar. Aprendemos sobre ponteiros. Esse o nosso estudo sobre ponteiros ajudou bastante a gente entender ali o nosso tópico número três, a reis e strings. Mas sobre ponteiros, basicamente eles são uma variável que podem armazenar um endereço de outras variáveis. E aí a gente tem as nossas operações de desreferenciamento, que é quando a gente, ao invés de acessar um endereço que tá salvo nessa nossa
variável, a gente acessa a variável cujo endereço está salva dentro do nosso ponteiro, que também é uma variável, né? Então, a gente viu isso daí com bastante detalhe, mas para evitar a confusão, tenham sempre em mente que o ponteiro é uma variável que armazia no endereço. Aprendemos também sobre a alocação dinâmica. Depois que a gente aprendeu ponteiros, a gente já tinha condição de aprender sobre a locação dinâmica. O programa ele começa com um tamanho fixo de memória. E aí a gente não pode simplesmente ter um programa ali que vai ter muitas variáveis. Para fazer isso,
a gente utiliza a locação dinâmica, que faz com que esse nosso programa vá aumentando o seu consumo de memória RAM no decorrer do seu uso. E aí, para isso, a gente utilizava lá as funções de alocação Malock, né, e as funções de liberação de memória e os ponteiros pra gente poder acessar esses espaços de memória. E por último, nós estudamos sobre persistência de arquivo. persistência de arquivo que trouxe um conjunto de funções que nós aprendemos tanto para abrir um arquivo quanto para Escrever dentro desse arquivo, seja uma string, né, um texto ou um valor que
a gente vai formatar como uma string ou também como escrever valores em arquivos binários. Nós vimos também como caminhar em um arquivo. Nós vimos também como fechar um arquivo. Mas aqui o mais importante foi a ideia de quando a gente abre um arquivo, na verdade a gente tá copiando ele ali pra memória RAM e a gente vai manipulando e andando, né, com um ponteiro dentro dessa cópia desse nosso arquivo. E agora, pessoal, para finalizar o nosso curso, a gente teria que estar estudando e vamos estudar, né, sobre tipos abstratos de dados e modularização. E o
nosso último tópico seria sobre o make file. Bom, tipo abstrato de dado e modularização. Acontece que até esse momento todos os programas que a gente fez, eles estavam dentro de um só arquivo. Claro, a gente utilizou bibliotecas, que são arquivos também que estão no nosso computador, mas a gente não aprendeu a criar nossas próprias bibliotecas. E geralmente um programa ele vai ter diversos arquivos. Um exemplo que eu trouxe aí para vocês seriam os arquivos main.c, C. O arquivo produto.C e produto.Hh. Hh. A gente separa, né, entre ponto C e ponto H, colocando no ponto H
as structs, né, os tipos que a gente cria, o tipo produto e também as assinaturas de funções. E no ponto C a gente coloca as implementações, porque aí a pessoa pode olhar, ela pode olhar no ponto H e já vai ver quais são os tipos de dados que envolvem o produto e vão ver ali também as assinaturas, ou seja, as funções que Ela pode estar utilizando, mas sem ter acesso à sua implementação. A implementação ficaria no ponto C. Então um programa um pouco maior, ele teria amin pc, produto.c produto. Cliente.c cliente. Ali tá errado. Venda.cenda.
arquivo pc e arquivo pontoh. Hh. Mas a gente não sabe como que a gente faz para utilizar esses arquivos, como que a gente faz para compilar quando a gente tem tantos arquivos, como é que a gente separa isso devidamente? E é muito importante que a gente esteja estudando sobre isso. Só que existe uma outra coisa também que é extremamente importante, que é a gente aprender a aprender através de texto. É claro, pessoal, que o ideal seria que você conseguisse aprender através de textos em inglês. A gente precisa começar dando pequenos passos. E o nosso primeiro
passo agora vai ser aprender através de texto. E se você já chegou até aqui no curso, eu quero dizer para você que tem uma plataforma que tá em desenvolvimento chamada Froncaio. Você pode acessar ela no endereço froncaio.com. E ao finalizar essa aula, eu já vou estar publicando conteúdo sobre tipo abstratos de dados e modularização. E vou tentar manter a linguagem o mais acessível possível, mais parecida com a nossa aula, para que você não tenha dificuldades ao aprender através de textos. E quanto ao nosso último tópico, Make File, acontece que a partir do momento que a
gente tiver vários arquivos, né, a gente poderia, né, utilizar o nosso comando GCC para compilar, onde a gente colocaria todos os nomes desses nossos arquivos. Mas eventualmente isso fica muito trabalhoso e você passa a ter vários arquivos. Então imagine que toda vez que você tivesse que compilar, você tivesse que editar o nome de todos esses arquivos. Isso seria um baita de um problema. E por esse motivo existe um programa, o programa chamado Make. Esse programa Make ele abre um arquivo chamado Make File. Você cria esse arquivo make file, lá dentro ele acessa algumas informações e
aí ele automaticamente compila. Então, o processo de compilação passa a ser feito com a utilização deste programa. O Make é um programa e ele acessa informações que estão salvas em um arquivo de texto. Esse arquivo de texto é chamado Make File e aí ele compila tudo pra gente. E o conteúdo sobre Make File também vai estar no nosso site. Muito bem. Então, chegamos oficialmente ao final do nosso curso completo sobre a linguagem C. Eu gostaria de agradecer a sua audiência e de te convidar, caso seja do seu interesse, a ir lá no nosso site, na
nossa página de doação e enviar uma mensagem onde você pode contribuir com a nossa plataforma de ensino totalmente gratuita. Quero também falar com você sobre os próximos passos. Geralmente os estudantes, né, eles aprendem uma linguagem e aí eles não sabem o que que eles devem fazer logo a seguir. Então eu quero bater esse papo, dar uma orientação para vocês. E para que você não perca esse conteúdo, eu te convido a deixar um like nesse vídeo, se inscrever, se possível, deixar um comentário também. Vai estar saindo aqui no canal, se já não tiver saído, um vídeo
exatamente sobre isso. O que estudar depois de aprender uma linguagem De programação? Lá a gente vai conversar sobre algoritmos e estruturas de dados. Meu nome é Caio do canal Froncaio e nos vemos no próximo vídeo.