Mudanças entre as edições de "PRG29002 - Programação I - Eng.Telecom 2017-1"
(Desfeita a edição 133323 de Cleber.amaral (Discussão)) |
|||
(73 revisões intermediárias pelo mesmo usuário não estão sendo mostradas) | |||
Linha 10: | Linha 10: | ||
*Monitoria: [[Programa_de_monitoria_dos_cursos_superiores_de_Telecomunica%C3%A7%C3%B5es]]<br> | *Monitoria: [[Programa_de_monitoria_dos_cursos_superiores_de_Telecomunica%C3%A7%C3%B5es]]<br> | ||
− | = | + | =Algoritmos utilizando fluxograma= |
==Introdução aos algoritmos utilizando fluxograma== | ==Introdução aos algoritmos utilizando fluxograma== | ||
Linha 124: | Linha 124: | ||
==Pseudo-código utilizando Portugol - repetições== | ==Pseudo-código utilizando Portugol - repetições== | ||
− | Exercícios - | + | {{collapse top|Exercícios - Pseudocodigo (série 1)}} |
+ | |||
+ | Exercícios - Pseudocodigo (série 1): | ||
#Implemente um algoritmo que solicita ao usuario entrar com um numero negativo qualquer. O algoritmo deve mostrar todos os números impares e pares de 0 até este negativo indicado. Utilize a estrutura para, verifique qual o passo adequado para resolver esta questão. (id:1.07) | #Implemente um algoritmo que solicita ao usuario entrar com um numero negativo qualquer. O algoritmo deve mostrar todos os números impares e pares de 0 até este negativo indicado. Utilize a estrutura para, verifique qual o passo adequado para resolver esta questão. (id:1.07) | ||
#Desenvolva uma algoritmo para marcar o placar de um jogo de futebol, deve solicitar ao usuário digitar A ou B, ao digitar A é somado um gol a equipe A e o mesmo para a B. Se digitado F deve encerrar e mostrar o placar final. (id:1.11) | #Desenvolva uma algoritmo para marcar o placar de um jogo de futebol, deve solicitar ao usuário digitar A ou B, ao digitar A é somado um gol a equipe A e o mesmo para a B. Se digitado F deve encerrar e mostrar o placar final. (id:1.11) | ||
Linha 139: | Linha 141: | ||
XXXXXXXXXX | XXXXXXXXXX | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | {{collapse top|Exercícios - Pseudocodigo (série 2)}} | ||
Exercícios - Pseudocpodigo (série 2): | Exercícios - Pseudocpodigo (série 2): | ||
#Rode e teste o exemplo do "Quadro 42 - Exemplo de uso de vetores em Portugol" do TCC de Antônio Medeiros, compare com a implementação de teste de números primos feito na lista anterior. Com o uso de vetores o que mudou? | #Rode e teste o exemplo do "Quadro 42 - Exemplo de uso de vetores em Portugol" do TCC de Antônio Medeiros, compare com a implementação de teste de números primos feito na lista anterior. Com o uso de vetores o que mudou? | ||
Linha 151: | Linha 155: | ||
Exercícios complementares: | Exercícios complementares: | ||
*Sessão 12.5 (página 294) de Araújo, Everton Coimbra de. Algoritmos: fundamento e prática; 3ª ed. [S.l]:Visual Books, 2007. 414p. ISBN 9788575022092. | *Sessão 12.5 (página 294) de Araújo, Everton Coimbra de. Algoritmos: fundamento e prática; 3ª ed. [S.l]:Visual Books, 2007. 414p. ISBN 9788575022092. | ||
+ | {{collapse bottom}} | ||
==Pseudo-código utilizando Portugol - sub-rotinas e registros== | ==Pseudo-código utilizando Portugol - sub-rotinas e registros== | ||
− | Exercícios - | + | {{collapse top|Exercícios - Pseudocodigo (série 3)}} |
+ | Exercícios - Pseudocodigo (série 3): | ||
#Execute o código do TCC do Antonio Medeiros, "Quadro 49 - Exemplo de passagem de parâmetros por referência em Portugol" | #Execute o código do TCC do Antonio Medeiros, "Quadro 49 - Exemplo de passagem de parâmetros por referência em Portugol" | ||
#Execute o código do TCC do Antonio Medeiros, "Quadro 50 - Exemplo de uso de sub-rotina que retorna valor em Portugol" | #Execute o código do TCC do Antonio Medeiros, "Quadro 50 - Exemplo de uso de sub-rotina que retorna valor em Portugol" | ||
Linha 186: | Linha 192: | ||
fim_sub_rotina tangente | fim_sub_rotina tangente | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | =Programação em C (ANSI)= | ||
− | == | + | ==Programação em C (ANSI) - Introdução ao C== |
{{collapse top|Introdução ao C e funções de saída e entrada de dados}} | {{collapse top|Introdução ao C e funções de saída e entrada de dados}} | ||
Linha 297: | Linha 305: | ||
printf ("O valor de x é %d\n",x); | printf ("O valor de x é %d\n",x); | ||
}</syntaxhighlight> | }</syntaxhighlight> | ||
+ | |||
+ | Observe que o C permite também que uma variável seja inicializada com determinado valor no momento de sua criação. Ex.:<syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | main() | ||
+ | { | ||
+ | int x = 5; /* declaração de uma variável inteira atribuindo o valor 5*/ | ||
+ | printf ("O valor de x é %d\n",x); | ||
+ | }</syntaxhighlight> | ||
+ | |||
#Compile e execute | #Compile e execute | ||
Linha 383: | Linha 400: | ||
{{collapse bottom}} | {{collapse bottom}} | ||
− | == | + | ==Programação em C (ANSI) - Controle de fluxo em C== |
+ | ===Programação em C (ANSI) - Condicionais=== | ||
{{collapse top|Condicionais em C}} | {{collapse top|Condicionais em C}} | ||
Linha 404: | Linha 422: | ||
**Exemplos:<syntaxhighlight lang=c> | **Exemplos:<syntaxhighlight lang=c> | ||
if (i > j) | if (i > j) | ||
− | max = i; | + | max = i; |
else | else | ||
− | max = j;</syntaxhighlight><syntaxhighlight lang=c> | + | max = j;</syntaxhighlight><syntaxhighlight lang=c> |
if (i > j) | if (i > j) | ||
− | if (i > k) max = i; else max = k; | + | if (i > k) max = i; else max = k; |
else | else | ||
− | if (j > k) max = j; eles max = k; | + | if (j > k) max = j; eles max = k; |
</syntaxhighlight> | </syntaxhighlight> | ||
*If em cascata | *If em cascata | ||
Linha 416: | Linha 434: | ||
**Exemplo: <syntaxhighlight lang=c> | **Exemplo: <syntaxhighlight lang=c> | ||
if ((i >= 0) && (i < 6)) | if ((i >= 0) && (i < 6)) | ||
− | printf(“Conceito insuficiente”); | + | printf(“Conceito insuficiente”); |
else if ((i >= 6) && (i < 9)) | else if ((i >= 6) && (i < 9)) | ||
− | printf(“Conceito suficiente/proficiente”); | + | printf(“Conceito suficiente/proficiente”); |
else if ((i >= 9) && (i <= 10)) | else if ((i >= 9) && (i <= 10)) | ||
− | printf(“Conceito excelente”); | + | printf(“Conceito excelente”); |
else | else | ||
− | printf(“Conceito inválido”);</syntaxhighlight> | + | printf(“Conceito inválido”);</syntaxhighlight> |
*Vídeo | *Vídeo | ||
**A estrutura mais elementar de decisão? Saloni em Code.org {{#ev:youtube|JtL7w6ja5iI}} | **A estrutura mais elementar de decisão? Saloni em Code.org {{#ev:youtube|JtL7w6ja5iI}} | ||
**O que é uma estrutura de decisão? Bill Gates em Code.org {{#ev:youtube|m2Ux2PnJe6E}} | **O que é uma estrutura de decisão? Bill Gates em Code.org {{#ev:youtube|m2Ux2PnJe6E}} | ||
+ | {{collapse bottom}} | ||
− | *Exercícios | + | {{collapse top|Exercícios - C (série 0)}} |
+ | *Exercícios - C (série 0) | ||
*#Implementar um programa que lê um número inteiro e imprime se o número é par ou ímpar. SUGESTÃO: Usar o operador de resto. | *#Implementar um programa que lê um número inteiro e imprime se o número é par ou ímpar. SUGESTÃO: Usar o operador de resto. | ||
*#Um estudo sobre sensibilidade de pessoas a temperaturas da água identificou que a maioria das pessoas considera fria a água com temperaturas abaixo de 25 graus, morna entre 25 e 30 graus, e quente acima de 30 graus. Escreva implemente em C um algoritmo que mostre as palavras "fria", "morna" ou "quente" dependendo da temperatura da água que for informada. | *#Um estudo sobre sensibilidade de pessoas a temperaturas da água identificou que a maioria das pessoas considera fria a água com temperaturas abaixo de 25 graus, morna entre 25 e 30 graus, e quente acima de 30 graus. Escreva implemente em C um algoritmo que mostre as palavras "fria", "morna" ou "quente" dependendo da temperatura da água que for informada. | ||
Linha 440: | Linha 460: | ||
{{collapse bottom}} | {{collapse bottom}} | ||
+ | ===Programação em C (ANSI) - Repetições=== | ||
{{collapse top|Estruturas de repetição em C}} | {{collapse top|Estruturas de repetição em C}} | ||
*Vídeos | *Vídeos | ||
Linha 479: | Linha 500: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
− | *Exercícios | + | {{collapse top|Exercícios - C (série 1)}} |
+ | *Exercícios - C (série 1) | ||
*#Assistir os vídeos da sessão "Condicionais em C" e "Estruturas de repetição em C" | *#Assistir os vídeos da sessão "Condicionais em C" e "Estruturas de repetição em C" | ||
*#Dado um número inteiro positivo, calcular a soma de todos os números inteiros compreendidos entre 0 e o número dado. Fazer uma versão com while e outra com for. | *#Dado um número inteiro positivo, calcular a soma de todos os números inteiros compreendidos entre 0 e o número dado. Fazer uma versão com while e outra com for. | ||
− | |||
*#Faça um algoritmo que apresente a sequencia de Fibonacci dado um valor “n” que representa a quantidade de números em série que se deseja exibir | *#Faça um algoritmo que apresente a sequencia de Fibonacci dado um valor “n” que representa a quantidade de números em série que se deseja exibir | ||
*#Desenvolva uma algoritmo em C para marcar o placar de um jogo de futebol, deve solicitar ao usuário digitar A ou B, ao digitar A é somado um gol a equipe A e o mesmo para a B. Se digitado F deve encerrar e mostrar o placar final. | *#Desenvolva uma algoritmo em C para marcar o placar de um jogo de futebol, deve solicitar ao usuário digitar A ou B, ao digitar A é somado um gol a equipe A e o mesmo para a B. Se digitado F deve encerrar e mostrar o placar final. | ||
*#Implemente um algoritmo em C que obtém um número do usuário e utilizando laço para verifica se um número primo. Valide seu algoritmo comparando com a lista de primos Lista de números primos | *#Implemente um algoritmo em C que obtém um número do usuário e utilizando laço para verifica se um número primo. Valide seu algoritmo comparando com a lista de primos Lista de números primos | ||
− | *#Escreva um algoritmo em C que solicita ao usuário digitar 6 números para uma aposta na megasena. O algoritmo deve utilizar a estrutura do...while, gravar em variaveis distintas cada número que deve estar entre 1 e 60. Deve garantir que os 6 números são diferentes entre si e no final mostrar os números digitados | + | *#Escreva um algoritmo em C que solicita ao usuário digitar 6 números para uma aposta na megasena. O algoritmo deve utilizar a estrutura do...while, gravar em variaveis distintas cada número que deve estar entre 1 e 60. Deve garantir que os 6 números são diferentes entre si e no final mostrar os números digitados (id:1.06) |
*#Implemente em C uma calculadora que realiza operações de soma ou subtração de dois números. A calculadora deve operar em um laço infinito encerrando sua operação se o usuário digitar "q" | *#Implemente em C uma calculadora que realiza operações de soma ou subtração de dois números. A calculadora deve operar em um laço infinito encerrando sua operação se o usuário digitar "q" | ||
− | *#Usando o comando for aninhado, construa um programa que implemente a figura abaixo. A margem esquerda (margem de espaços), o caracter do desenho, o número de linhas vazadas e o tamanho horizontal da figura devem ser lidos pelo teclado. Na figura abaixo representa uma saída quando a margem esquerda é 0, o caractere do desenho é 'a', o número de linhas vazadas é 1 e o tamanho horizontal é 10<code> | + | *#Usando o comando for aninhado, construa um programa que implemente a figura abaixo. A margem esquerda (margem de espaços), o caracter do desenho, o número de linhas vazadas e o tamanho horizontal da figura devem ser lidos pelo teclado. Na figura abaixo representa uma saída quando a margem esquerda é 0, o caractere do desenho é 'a', o número de linhas vazadas é 1 e o tamanho horizontal é 10 (id.:1.08)<code> |
aaaaaaaaaa | aaaaaaaaaa | ||
a a | a a | ||
Linha 503: | Linha 525: | ||
BBBBBBBB | BBBBBBBB | ||
BBBBBBBBBBB</syntaxhighlight> | BBBBBBBBBBB</syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | == Funções == | ||
+ | {{collapse top | Funções}} | ||
+ | *O que é uma função? Khan Academy {{#ev:youtube|k2-wVKJvsqs}} | ||
+ | *O que é uma função - noção intuitiva - MeSalva! {{#ev:youtube|oHupEt5YPqw}} | ||
+ | |||
+ | *Exercícios | ||
+ | *#Faça um programa que calcula a média de 2 números float utilizando uma função “calculaMedia” que recebe estes parâmetros e retorna a resposta. Na função main deve ser declarado o comando de printf da resposta. | ||
+ | *#Implemente um programa em C que calcule a Potência dissipada em um resistor e a corrente dados valores de V e R. O mesmo programa deve ser capaz de calcular a R dados P e V e R dados V e I. Faça então um menu inicial para que o usuário possa selecionar a opção de cálculo desejada. Crie diferentes funções para opção. | ||
+ | *#Crie um loop infinito na função main. Implemente uma função que recebe o valor de salário e percentual de reajuste como um parâmetros de entrada para resolver o exercício: Uma empresa irá ajustar o salário de seus funcionários de acordo com a categoria de trabalho dos funcionários: CAT A (10% de aumento), CAT B (15% de aumento) e CAT C (20% de aumento). Faça um programa que leia o plano de trabalho e o salário atual de um funcionário e calcula e imprime o seu novo salário. Use o comando switch. | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | A função main}} | ||
+ | O programa inicia pela primeira instrução contida na função main() e também se encerra na última instrução. O retorno padrão da função main é um int que representa um código de erros reconhecidos por muitos sistemas operacionais. Se o programa terminou sua execução corretamente o retorno deverá ser 0 (zero).<syntaxhighlight lang=c> | ||
+ | int main(void) | ||
+ | { | ||
+ | //Programa | ||
+ | return 0; | ||
+ | }</syntaxhighlight> | ||
+ | *A omissão do retorno da função main (por padrão int) ou utilização de outro tipo de retorno pode ser feita no C99 porém desta forma o código não fica portável a alguns padrões de C e não estaria adequado ao retorno de um programa que convencionalmente é esperado por sistemas operacionais. | ||
+ | *Quando o programador não especifica este retorno o compilador provavelmente estará atribuindo o retorno como int. | ||
+ | *Programas que devem ficar rodando indefinidamente normalmente definem um laço infinito na função main para que esta não alcance o final do código salvo se for dado um comando de terminação, por exemplo. | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | O método exit()}} | ||
+ | Uma alternativa a terminação do programa chegando ao fim da função main é a função exit da biblioteca <stdlib.h>. Para esta função deve-se passar um argumento inteiro que tem o mesmo significado do código de retorno da função main, portanto exit(0) representa uma terminação normal, alternativamente exit(EXIT_SUCCESS). Para representar uma terminação anormal pode-se utilizar exit(EXIT_FAILURE) ou exit(1).<br> | ||
+ | Há outros códigos de erro (ver [http://tldp.org/LDP/abs/html/exitcodes.html aqui]) porém são pouco usuais, no geral o que se pretende é saber apenas se a operação do programa terminou de forma bem sucedida ou não.<br> | ||
+ | Para verificar o código de erro retornado por uma aplicação, após encerrar o programa executar:<code> | ||
+ | $ echo $?</syntaxhighlight> | ||
+ | |||
+ | {{collapse bottom}} | ||
+ | |||
+ | == Vetores, strings e matrizes em C == | ||
+ | {{collapse top|Vetores e matrizes em C}} | ||
+ | *Matrizes de qualquer dimensão são caracterizadas por terem todos os elementos pertencentes ao mesmo tipo de dado; | ||
+ | *Vetores são matrizes unidimensionais; | ||
+ | *O indexador começa sempre em zero; | ||
+ | *É importante observar que o C não controla se o programador está ou não acessando uma posição de memória válida da estrutura, cabe ao programador criar proteções; | ||
+ | *Declaração: <syntaxhighlight lang=c> | ||
+ | tipo_da_variável nome_do_vetor [tamanho]; | ||
+ | tipo_da_variável nome_da_matriz [linhas][colunas]; </syntaxhighlight> | ||
+ | *Strings são vetores de chars; | ||
+ | *Têm o seu último elemento como um '\0' | ||
+ | Declaração: <syntaxhighlight lang=c> | ||
+ | char nome_da_string [tamanho]; </syntaxhighlight> | ||
+ | *Inicialização: <syntaxhighlight lang=c> | ||
+ | float vect [6] = { 1.3, 4.5, 2.7, 4.1, 0.0, 100.1 }; | ||
+ | int matrx [3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; | ||
+ | char str [10] = { 'J', 'o', 'a', 'o', '\0' }; | ||
+ | char str [10] = "Joao"; | ||
+ | char str_vect [3][10] = { "Joao", "Maria", "Jose" }; | ||
+ | </syntaxhighlight> | ||
+ | *Inicialização sem especificação de tamanho <syntaxhighlight lang=c> | ||
+ | char mess [] = "Linguagem C: flexibilidade e poder."; | ||
+ | int matrx [][2] = { 1,2,2,4,3,6,4,8,5,10 };</syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Gerando números pseudo-aleatórios}} | ||
+ | *Pergunta: é possivel que um computador binario utilizando apenas recursos computacionais seja puramente aleatorio? | ||
+ | |||
+ | *Existem artifícios de se criar números que variam conforme determinadas condições como sequencias variáveis associadas a data e hora atual por exemplo, a isso chamamos de números pseudo aleatórios. Para conseguir este recurso em C podemos utilizar a função rand() da stblib associada a função srand que configura uma semente de aleatoriedade a função rand, o resultado pode ser conferido no código a seguir: <syntaxhighlight lang=c> | ||
+ | #include <stdlib.h> | ||
+ | #include <time.h> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | int main(void) { | ||
+ | |||
+ | srand( (unsigned)time(NULL) ); | ||
+ | printf("Numero gerado: %d\n", rand() % 10); //Gera números que variam de 0 a 9 | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | {{collapse top | Tabela ASCII}} | ||
+ | *Os computadores armazenam todos os tipos de dados na forma numérica, incluindo letras. Para apresentar textos em tela neste caso o programa em C precisa conhecer o tipo de dado que está escrito em memória, sendo um dado do tipo caractere (ou string) ele será então tratado como uma letra. O conjunto de letras, números e símbolos imprimíveis está sintetizado na tabela ASCII. | ||
+ | **Para ver a tabela, acesse o link [http://www.ascii-code.com/ Tabela ASCII] | ||
+ | **Observe que as letras maiúsculas variam de 65 ('A') até 90 ('Z') | ||
+ | **Observe também que as letras minúsculas estão em outra faixa, variam de 97 ('a') até 122 ('z') | ||
+ | **Os números imprimíveis também tem seus representantes, variam de 48 ('0') a 57 ('9') | ||
+ | **Caracteres especiais como '$', '%', '*', '+' também estão relacionados na tabela | ||
+ | **A tabela também apresenta códigos de caracteres não imprimíveis como o 9 (TAB), 13 ('\r' presente no ENTER) | ||
+ | **Para representações de outros caracteres pode ser necessário acessar a extensão da tabela ASCII, podendo-se obter 'Ç' e caracteres acentuados | ||
+ | **A tabela que está sendo apresentada esta em acordo com o padrão ISO 8859-1 e Microsoft® Windows Latin-1, outros caracteres podem ainda ser obtidos se alterado o padrão de codificação | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Vetor de tamanho variável}} | ||
+ | O vetor de tamanho variável (variable lenght array) é um recurso do C que permite que o tamanho do vetor seja definido em tempo de execução. Na prática o C irá alocar uma quantidade de memória que não precisa estar definida antes da execução. | ||
+ | [https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Variable-Length.html variable-lenght] | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Exercícios - C (série 2)}} | ||
+ | *#Implementar um programa em C para ler 10 números reais (float) para um vetor. Usar o comando ''while'', imprimindo os valores lidos em um segundo laço while. (id.:3.02) | ||
+ | *#Modificar o exercício para computar a média dos 10 números que estão no vetor. (id.:3.03) | ||
+ | *#Modificar o exercício anterior para computar a quantidade de números do vetor que estão acima da média. (id.:3.04) | ||
+ | *#Refazer os exercícios anteriores usando o comando for. (id.:3.05) | ||
+ | *#Implemente um algoritmo em C que solicita do usuário 10 números, após a leitura mostra ao usuários os números na ordem inversa em que foram digitados. (id.:3.09) | ||
+ | *#Teste o exemplo dado na apostila da UFMG página 46 e execute a AUTO AVALIAÇÂO sugerida na página seguinte. | ||
+ | *#Considere um vetor global de ''floats'' chamado ''vetRnd'' de tamanho 100. Construa uma função que inicia este vetor com 100 números randômicos entre 1 e 6. Imprima em tela os valores. | ||
+ | *#Implementar uma função que recebe dois vetores de inteiros como parâmetro e o tamanho dos mesmos (suponha vetores de mesmo tamanho). A função deve retornar o número de elementos iguais comparados posição por posição. O esqueleto da função deve ser como: <syntaxhighlight lang=c> | ||
+ | int compara_vetores(int ve1[],int vet2[], int tamanho) | ||
+ | { | ||
+ | int num_elementos; | ||
+ | |||
+ | return num_elementos; | ||
+ | } | ||
+ | </syntaxhighlight>Exemplo: Para os vetores x[]={1,1,3,4,5} e y[]={1,2,3,3,5} temos três elementos iguais (nas posições 0, 2 e 4). (id.:3.01) | ||
+ | *#Implemente um algoritmo em C que conta a ocorrência de cada letra (incluindo números de 0 a 9) dada uma frase. Deve ignorar se a letra foi digitada em maiúscula ou minúscula (portando somar junto). Limite 100 caracteres. | ||
+ | *#Implemente um algoritmo em C que calcula a média de notas de alunos. Para isso deve solicitar a quantidade de alunos, em seguida obter o nome de cada aluno e depois recebe 5 notas por aluno (num único scanf). Usar uma matriz de float de duas dimensões global. Deve então calcular as médias e apresentar: Nomes dos alunos que ficaram em recuperação (média <6), Nome do aluno que tirou a maior média, Obs.: Criar a função calculaMedia(). | ||
+ | *#Utilize a função rand() e gerador de semente srand() para gerar 6 números aleatórios distintos que variam de 1 a 60 e imprima esta sequencia em tela | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Exercícios - C (série 3)}} | ||
+ | *#Implemente um programa em C que solicita ao usuário digitar duas palavras, o programa analisa estas palavras e diz se são ou não anagramas (palavra formada pela alteração da ordem ou transposição de letras) ex.: “America” e “Iracema”, “Roma” e “Amor”, “Semolina” e “Is no meal” são anagramas) | ||
+ | *#Implemente um algoritmo que dada uma frase, apresente 3 opções: tornar todas as letras em maiúsculas, tornar todas em minúsculas ou a primeira letra de cada palavra em maiúscula e demais minúsculas (limite 100 caracteres). Faça estas operações através de cálculos utilizando como base a tabela ASCII, utilize funções distintas (tudoMaiusculo(), tudoMinusculo(), primeiraMaiuscula()), utilizando o vetor de caracteres como uma variável global. | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | ==Tipos de dados compostos== | ||
+ | {{collapse top | Estruturas}} | ||
+ | |||
+ | ;Estruturas | ||
+ | Assim como o vetor a estrutura é um conjunto de dados, mas traz uma vantagem: a possibilidade de possuir "campos" de diferentes tipos de variáveis. Por exemplo, a struct TPessoa poderia ter os campos nome (char[40]) e idade (int). | ||
+ | |||
+ | A declaração genérica da estrutura é: | ||
+ | struct TNome_do_tipo { | ||
+ | //variável 1 | ||
+ | //variável 2 | ||
+ | //variável N | ||
+ | } nome_instancia; | ||
+ | |||
+ | *Convencionalmente damos ao tipo da estrutura um nome "TNome_do_tipo", onde "T" representa Tipo e a letra seguinte também vem em maiúscula | ||
+ | *nome_instancia representa a instancia de variável (do tipo struct) que será alocada em memória, esta declaração também pode ser um vetor "nome_instancia[10]", por exemplo | ||
+ | *Em uma declaração é necessário ao menos definir um dos parâmetros "TNome_do_tipo" ou "nome_instancia são opcionais" | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | struct TUsuario /* struct TUsuario é o nome do tipo que está sendo criado */ | ||
+ | { | ||
+ | char userID[20]; | ||
+ | char senha[20]; | ||
+ | } Usuario; /* aqui é definida uma variável do tipo struct TUsuario */ | ||
+ | |||
+ | struct TUsuario TabelaUsuario[20]; | ||
+ | |||
+ | main() | ||
+ | { | ||
+ | scanf("%s", Usuario.userID); | ||
+ | scanf("%s", Usuario.senha); | ||
+ | scanf("%s", TabelaUsuario[10].userID); | ||
+ | scanf("%s", TabelaUsuario[10].senha); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Neste exemplo, foi definido um tipo (modelo) para o registro (''struct TUsuario'') e foi criada uma variável chamada ''Usuario'' a partir deste tipo. Na sequência foi criada mais uma variável (um vetor de estruturas) chamada ''TabelaUsuario''. Note que basta usar as palavras chave ''struct Usuario'' para criar novas variáveis. O tipo completo é definido uma única vez no início. | ||
+ | |||
+ | ;Exercícios | ||
+ | |||
+ | #Criar um programa que define uma ''struct'' para armazenamento do nome e das notas bimestrais de um aluno. Atualizar a estrutura usando o scanf. | ||
+ | #Alterar o programa para que ele calcule e imprima a média de cada aluno. | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | #define NUM_MAX 3 | ||
+ | |||
+ | struct TAluno { | ||
+ | char nome[30]; | ||
+ | char matricula[11]; | ||
+ | float b1,b2,b3,b4; | ||
+ | } Turma[NUM_MAX]; | ||
+ | |||
+ | void print_aluno(struct TAluno aux) | ||
+ | { | ||
+ | printf("Nome -> %s\n", aux.nome); | ||
+ | printf("Matrícula -> %s\n", aux.matricula); | ||
+ | printf("Bimestre 1 -> %f\n", aux.b1); | ||
+ | printf("Bimestre 2 -> %f\n", aux.b2); | ||
+ | printf("Bimestre 3 -> %f\n", aux.b3); | ||
+ | printf("Bimestre 4 -> %f\n", aux.b4); | ||
+ | } | ||
+ | |||
+ | main() | ||
+ | { | ||
+ | int i; | ||
+ | |||
+ | for(i=0;i<NUM_MAX;i++) { | ||
+ | printf("Entre com o nome do aluno\n"); | ||
+ | scanf("%s", Turma[i].nome); | ||
+ | printf("Entre com a matrícula do aluno\n"); | ||
+ | scanf("%s", Turma[i].matricula); | ||
+ | printf("Entre com a nota do bimestre 1\n"); | ||
+ | scanf("%f", &Turma[i].b1); | ||
+ | printf("Entre com a nota do bimestre 2\n"); | ||
+ | scanf("%f", &Turma[i].b2); | ||
+ | printf("Entre com a nota do bimestre 3\n"); | ||
+ | scanf("%f", &Turma[i].b3); | ||
+ | printf("Entre com a nota do bimestre 4\n"); | ||
+ | scanf("%f", &Turma[i].b4); | ||
+ | } | ||
+ | for(i=0;i<NUM_MAX;i++) { | ||
+ | printf("=========== Aluno %d ============\n", i); | ||
+ | print_aluno(Turma[i]); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;Copiando Estruturas | ||
+ | |||
+ | O exemplo a seguir demonstra como se pode copiar uma variável ''struct'' para outra do mesmo tipo. | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | struct THoras{ | ||
+ | int hora; | ||
+ | int minuto; | ||
+ | int segundo; | ||
+ | }; | ||
+ | |||
+ | struct THoras Ontem = {2,10,57}; | ||
+ | |||
+ | void main() | ||
+ | { | ||
+ | struct THoras Hoje; | ||
+ | Hoje = Ontem; | ||
+ | |||
+ | printf("Hora hoje = %d, Minuto hoje = %d e Segundo hoje %d\n", Hoje.hora, Hoje.minuto, Hoje.segundo); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;Estruturas dentro de estruturas | ||
+ | |||
+ | Vamos ver um exemplo com estruturas definidas dentro de estruturas: | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | struct TEndereco{ | ||
+ | char rua[50]; | ||
+ | char numero[10]; | ||
+ | }; | ||
+ | |||
+ | struct TCidadao{ | ||
+ | char nome[50]; | ||
+ | char cpf[20]; | ||
+ | struct TEndereco endereco; | ||
+ | int num_filhos; | ||
+ | }; | ||
+ | |||
+ | void main() | ||
+ | { | ||
+ | struct TCidadao Cidadao; | ||
+ | |||
+ | printf("Entre com o nome\n"); | ||
+ | scanf ("%s",Cidadao.nome); | ||
+ | |||
+ | printf("Entre com o cpf\n"); | ||
+ | scanf ("%s",Cidadao.cpf); | ||
+ | |||
+ | printf("Entre a rua\n"); | ||
+ | scanf ("%s",Cidadao.endereco.rua); | ||
+ | |||
+ | printf("Entre a numero\n"); | ||
+ | scanf ("%s",Cidadao.endereco.numero); | ||
+ | |||
+ | printf("Entre com o número de filhos\n"); | ||
+ | scanf ("%d",&Cidadao.num_filhos); | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;Iniciando structs na definição | ||
+ | |||
+ | Como toda variável, é possível dar valores para uma variável do tipo struct definida no programa: | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | struct TEndereco { | ||
+ | char rua[50]; | ||
+ | int numero; | ||
+ | }; | ||
+ | |||
+ | struct TCidadao{ | ||
+ | char nome[50]; | ||
+ | char cpf[20]; | ||
+ | struct TEndereco endereco; | ||
+ | }; | ||
+ | |||
+ | int main(void) | ||
+ | { | ||
+ | //Inicializando com parâmetros em sequencia (ordem tem que ser respeitada) | ||
+ | struct TCidadao CidadaoMaria = {"Maria","42342342234",{"Rua AlfaBeta",145}}; | ||
+ | //Inicializando com parâmetros via campo (não é necessário respeitar qualquer ordem) | ||
+ | struct TCidadao CidadaoJose = {.cpf = "1234567890", .endereco.numero = 541,.nome = "Jose",.endereco.rua = "Rua GamaDelta"}; | ||
+ | |||
+ | printf("Rua do cidadao %s = %s\n", CidadaoMaria.nome, CidadaoMaria.endereco.rua); | ||
+ | printf("Rua do cidadao %s = %s\n", CidadaoJose.nome, CidadaoJose.endereco.rua); | ||
+ | |||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;Passando estruturas como parâmetro e retornando estruturas | ||
+ | |||
+ | Se não for usado o operador "&" , um parâmetro que é estrutura será passado por cópia. Não apresentaremos agora a passagem por endereço pois necessita do conceita de ponteiro. | ||
+ | Observe o exercício abaixo. | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | struct TEndereco{ | ||
+ | char rua[50]; | ||
+ | char numero[10]; | ||
+ | }; | ||
+ | |||
+ | struct TCidadao{ | ||
+ | char nome[50]; | ||
+ | char cpf[20]; | ||
+ | struct TEndereco endereco; | ||
+ | int num_filhos; | ||
+ | }; | ||
+ | |||
+ | void print_struct (struct TCidadao aux) | ||
+ | { | ||
+ | printf("nome=%s cpf=%s\n", aux.nome, aux.cpf); | ||
+ | printf("endereço inicial do aux %p\n", &aux); | ||
+ | } | ||
+ | |||
+ | void main() | ||
+ | { | ||
+ | struct TCidadao Cidadao; | ||
+ | |||
+ | printf("Entre com o nome\n"); | ||
+ | scanf ("%s",Cidadao.nome); | ||
+ | |||
+ | printf("Entre com o cpf\n"); | ||
+ | scanf ("%s",Cidadao.cpf); | ||
+ | |||
+ | printf("Entre a rua\n"); | ||
+ | scanf ("%s",Cidadao.endereco.rua); | ||
+ | |||
+ | printf("Entre a numero\n"); | ||
+ | scanf ("%s",Cidadao.endereco.numero); | ||
+ | |||
+ | printf("Entre com o número de filhos\n"); | ||
+ | scanf ("%d",&Cidadao.num_filhos); | ||
+ | |||
+ | print_struct(Cidadao); | ||
+ | |||
+ | printf("endereço inicial do Cidadao %p\n", &Cidadao); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Exercícios - C (série 4)}} | ||
+ | #Implemente um programa onde no laço principal o usuário é questionado se deseja calcular a área de um triângulo ou de um retângulo, em seguida obtém a base e altura da forma geométrica desejada. De acordo com a seleção invoca uma função específica (ambas recebendo os argumentos base e altura do tipo float). De acordo com a seleção do usuário, utilize structs TRetangulo ou TTriangulo para armazenar a base, altura e área calculada (retornada da função). Imprima o resultado em tela. Lembre-se que a área do retângulo é dada pela base vezes a altura e a do triângulo pela base vezes altura sobre 2. | ||
+ | #Crie uma estrutura TUsuario com os campos UserID, senha e quantidade de tentativas. Implementar um contador de acesso que permita bloquear o usuário após 3 tentativas seguidas. Note que caso o usuário acerte a senha, este contador deverá ser zerado. | ||
+ | #Implementar uma funcionalidade do administrador para desbloquear o usuário bloqueado. Inclua na estrutura uma variavel _Bool para dizer se o usuário é administrador. Se for, este usuário pode ter acesso a um menu (que você deve desenvolver) de desbloqueio de usuário ao digitar um UserID de certo usuário bloqueado. | ||
+ | #No programa de controle de senha inserir um campo na estrutura do usuário de forma a acomodar uma mensagem de boas vindas particularizada para cada usuário. A mensagem "DEFAULT" é Bom dia! Implemente na função administrar a inserção da mensagem no exercício anterior. | ||
+ | #Na solução acima criar uma função que procura usuário na tabela (já que este código é utilizado em mais do que um lugar). A função deve receber o UserID a ser procurado e deve retornar um inteiro correspondente ao índice do usuário encontrado ou -1 se não for encontrado. | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Unions}} | ||
+ | Union é um recurso do C que permite declarar um conjunto de dados que irá ocupar um mesmo espaço. É bastante empregado quando se deseja economizar espaço ou não se tem certeza sobre qual tipo de dado deve ser armazenado para determinada instancia. No exemplo a seguir é criada uma struct chamada TProduto e dentro destra estrutura há uma área de detalhamento do produto que é de uso genérico, para alguns produtos há campos específicos para preenchimento e outros não se tem ao certo os detalhes, portanto fica um campo de uso geral. <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | struct TRoupeiro{ | ||
+ | char cor[20]; | ||
+ | int volume; | ||
+ | float peso; | ||
+ | }; | ||
+ | |||
+ | struct TProduto{ | ||
+ | int id; | ||
+ | char nome[20]; | ||
+ | union { | ||
+ | struct TRoupeiro roupeiro; | ||
+ | char descricao_generica[sizeof(int)+sizeof(float)+20]; | ||
+ | }; | ||
+ | }; | ||
+ | |||
+ | int main(void) | ||
+ | { | ||
+ | struct TProduto vaso_decorativo = { | ||
+ | .id = 2,.nome = "Vaso decorativo 1", | ||
+ | .descricao_generica = "em vidro - peça única" | ||
+ | }; | ||
+ | |||
+ | struct TProduto guarda_roupas_solteiro = { | ||
+ | .id = 1,.nome = "Roupeiro 3 portas", | ||
+ | .roupeiro.cor = "CZ", .roupeiro.volume = 304,.roupeiro.peso = 50.0 | ||
+ | |||
+ | }; | ||
+ | printf("nome = %s, \ndescrição = %s, \ncor = %s, \nvolume = %d, \npeso = %f\n\n\n", | ||
+ | guarda_roupas_solteiro.nome, | ||
+ | guarda_roupas_solteiro.descricao_generica, | ||
+ | guarda_roupas_solteiro.roupeiro.cor, | ||
+ | guarda_roupas_solteiro.roupeiro.volume, | ||
+ | guarda_roupas_solteiro.roupeiro.peso | ||
+ | ); | ||
+ | printf("nome = %s, \ndescrição = %s, \ncor = %s, \nvolume = %d, \npeso = %f\n\n\n", | ||
+ | vaso_decorativo.nome, | ||
+ | vaso_decorativo.descricao_generica, | ||
+ | vaso_decorativo.roupeiro.cor, | ||
+ | vaso_decorativo.roupeiro.volume, | ||
+ | vaso_decorativo.roupeiro.peso | ||
+ | ); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | *Observe que a union é feita de duas entre uma struct e um vetor de caracteres. O autor neste código criou este vetor baseado no tamanho que ocupa a struct aproveitando todo o espaço já alocado, porém o C não exige que os dados ocupem o mesmo espaço, neste caso a alocação ocorrerá em relação a maior estrutura. | ||
+ | *Observe que ao imprimir os valores da instancia "guarda_roupas" o "descricao_generica" apesar de não ter sido formalmente preenchido, foi indiretamente preenchido quando no caso "roupeiro.cor" recebeu um valor. Como este valor foi "Cinza" o C escreveu um "\0" no final da string "roupeiro.cor" que acabou também servindo como final da string "descricao_generica", por isso neste print ambos os campos apresentam o mesmo valor. Porém observe também que os valores de "volume" e "peso" estão perfeitamente preservados. | ||
+ | *Agora foi interessante o cado da instancia "vaso_decorativo", ela foi descrita pelo campo "descricao_generica" e apenas para fins didáticos o autor imprimiu o que teria dentro de "roupeiro.cor", "roupeiro.volume" e "roupeiro.peso". Neste caso cairam valores oriundos da constante "em vidro - peça única" que não passam de sujeira neste contexto. | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | ==Ponteiros== | ||
+ | |||
+ | {{collapse top | Ponteiros}} | ||
+ | |||
+ | A memória de um computador pode ser vista como um vetor de bytes. Neste espaço vimos a utilização de variáveis diversas que podem armazenar valores que podem ser obtidos do usuários, serem resultados de ariméticas e muitas outras operações. O ponteiro nada mais é que um tipo de dado igualmente armazenado em memória, porém este dado se refere a um endereço da memória, ou seja, a um outro objeto. | ||
+ | |||
+ | Este recurso é muito útil para diversos propósitos, basta pensar na própria aplicação do conceito "endereço", imagine como seria localizar uma casa em uma cidade sem haver uma forma de endereçar e armazenar os endereços das casas. Explorando esta analogia, cada lote possui um endereço e pode ter um conteúdo de diferentes tipos como uma casa, um prédio ou um conjunto de lojas, enfim, trazendo para o C seria como os tipos int, char, vetores diversos, etc. | ||
+ | |||
+ | Assim é a memória, cada byte possui um endereço. O tamanho da memória é definido pelo tamanho do barramento de endereços usado para acessá-la. Uma variável ocupa uma área da memória. Tipicamente uma variável to tipo ''char'' se utiliza de um ''byte''. Já uma variável do tipo ''int'' pode (dependendo do sistema) usar 4 ''bytes'' contíguos. | ||
+ | |||
+ | ;Uma variável possui um endereço e um conteúdo (dados). | ||
+ | |||
+ | Uma variável ponteiro tem como conteúdo um endereço. Portanto a variável ponteiro possui um endereço e contém um endereço como conteúdo. Este recurso é largamente utilizado para passar parâmetros como referência (ao invés de copiar uma variável quando se quiser processá-la em alguma função), bem como para algoritmos diversos que buscam formas mais otimizadas de executar operações. Rode o código a seguir e compare com as respostas que foram obtidas: <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | int main(void) | ||
+ | { | ||
+ | int i = 10; | ||
+ | int *p; | ||
+ | long int li; | ||
+ | p = &i; | ||
+ | printf("Conteúdo de i: i = %d\n",i); | ||
+ | printf("Endereço de i: &i = %p\n",&i); | ||
+ | printf("Conteúdo de p: p = %p\n",p); | ||
+ | printf("Endereço de p: &p = %p\n",&p); | ||
+ | printf("Conteúdo apontado: *p = %d (conteúdo do endereço apontado por p)\n",*p); | ||
+ | printf("Tamanho do ponteiro = %li bytes\n",sizeof(p)); | ||
+ | printf("Tamanho do lont int = %li bytes\n",sizeof(li)); | ||
+ | printf("Tamanho do int = %li bytes\n",sizeof(i)); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Resposta obtida através do gcc em uma máquina Linux Ubuntu: | ||
+ | Conteúdo de i: i = 10 | ||
+ | Endereço de i: &i = 0x7ffeb25859e4 | ||
+ | Conteúdo de p: p = 0x7ffeb25859e4 | ||
+ | Endereço de p: &p = 0x7ffeb25859e8 | ||
+ | Conteúdo apontado: *p = 10 (conteúdo do endereço apontado por p) | ||
+ | Tamanho do ponteiro = 8 bytes | ||
+ | Tamanho do lont int = 8 bytes | ||
+ | Tamanho do int = 4 bytes | ||
+ | *Observações: | ||
+ | **"&i" e "p" são iguais, isso porque "p" tem como conteúdo um endereço (neste caso o endereço de "i") | ||
+ | **O conteúdo de "i" (10) é igual "*p", que exatamente está extraindo o conteúdo da variável apontada (é o próprio "i") | ||
+ | **O endereço de "p" (&p) é um valor próprio o que prova que p é também uma variável alocada na memória armazenando um valor próprio | ||
+ | **Nesta máquina o ponteiro está representado por uma variável inteira de 8 bytes (uintptr_t). | ||
+ | |||
+ | |||
+ | Resposta obtida através do codechef.com (gcc-4.9.2): | ||
+ | Conteúdo de i: i = 10 | ||
+ | Endereço de i: &i = 0xbfadc2b8 | ||
+ | Conteúdo de p: p = 0xbfadc2b8 | ||
+ | Endereço de p: &p = 0xbfadc2bc | ||
+ | Conteúdo apontado: *p = 10 (conteúdo do endereço apontado por p) | ||
+ | Tamanho do ponteiro = 4 bytes | ||
+ | Tamanho do lont int = 4 bytes | ||
+ | Tamanho do int = 4 bytes | ||
+ | *Observações: | ||
+ | **O endereço de "p" e "i" são completamente diferentes da resposta anterior. Isso ocorre em diferentes máquinas e cada vez que o programa for rodado deverá também gerar novos endereços. O endereço é atribuído pelo sistema operacional que por várias condições naquele instante disponibilizou estes endereços ai listados. | ||
+ | **Nesta máquina o ponteiro está representado por uma variável inteira de 4 bytes (uintptr_t), o tamanho de uma variável ponteiro varia conforme a plataforma. | ||
+ | |||
+ | ;Ponteiro para inteiro | ||
+ | |||
+ | Observe o programa abaixo. A variável ''p'' é um ponteiro para inteiro. Isto significa que ela pode armazenar um endereço | ||
+ | de um inteiro. | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | main() | ||
+ | { | ||
+ | int x; | ||
+ | int *p; | ||
+ | |||
+ | x=5; | ||
+ | printf("Valor de x antes = %d\n", x); | ||
+ | |||
+ | p = &x; | ||
+ | *p=10; | ||
+ | |||
+ | printf("Valor de x depois = %d\n", x); | ||
+ | printf("Valor de p = %p\n", p); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Observe que para se referenciar o conteúdo da posição de memória apontada por ''p'' deve-se usar o asterisco: ''*p'' | ||
+ | |||
+ | |||
+ | ;EXERCÍCIO 1: Considere o programa abaixo: | ||
+ | <syntaxhighlight lang=c> | ||
+ | main() | ||
+ | { | ||
+ | int x=10; | ||
+ | int y, *p; | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | Complete o código para copiar o conteúdo de x para y, sem que qualquer variável apareçam no lado esquerdo de um sinal de atribuição. Ou seja, sem envolver diretamente x e y. | ||
+ | |||
+ | ;EXERCÍCIO 2: Tente inferir qual seria o valor da variável y no final do programa abaixo: | ||
+ | <syntaxhighlight lang=c> | ||
+ | main() | ||
+ | { | ||
+ | int x,y,w,*p1,*p2; | ||
+ | x = 20; | ||
+ | w = 30; | ||
+ | p1 = &x; | ||
+ | p2 = &w; | ||
+ | y = *p1 + *p2; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;EXERCÍCIO 3: Tente inferir qual seria o valor da variável y no final do programa abaixo: | ||
+ | <syntaxhighlight lang=c> | ||
+ | main() | ||
+ | { | ||
+ | int x,y,w,*p1,*p2, *p3; | ||
+ | x = 20; | ||
+ | w = 30; | ||
+ | p1 = &x; | ||
+ | p2 = &w; | ||
+ | y = *p1 + w; | ||
+ | p3 = &y; | ||
+ | *p3 = *p3 + 10; | ||
+ | y = *p1 + *p2 + *p3; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;EXERCÍCIO 4: Qual seria o valor das variáveis '''y''' e '''x''' no final do programa abaixo: | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | void main() | ||
+ | { | ||
+ | int x,y; | ||
+ | int *p; | ||
+ | y=0; | ||
+ | p=&y; | ||
+ | x=*p; | ||
+ | x=4; | ||
+ | (*p)++; | ||
+ | x--; | ||
+ | (*p) += x; | ||
+ | printf("\ny=%d x=%d\n",y,x); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;Ponteiro para ''char'' | ||
+ | |||
+ | Os ponteiro para ''char'' são muito utilizados pois permitem apontar para ''strings''. A ideia é que ele aponte para o primeiro caracter (char) da ''string''. Veja o exemplo abaixo. | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | main() | ||
+ | { | ||
+ | char x[10]="ifsc"; | ||
+ | char *p; | ||
+ | p = &x[2]; | ||
+ | printf("x[2] = %c\n", *p); | ||
+ | p = x; | ||
+ | printf("string %s\n", p); | ||
+ | while (*p!=0) { | ||
+ | printf("Endereco %p conteúdo %c\n", p,*p); | ||
+ | p++; | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Neste foi usado o incremento de um ponteiro, o que implica em adicionar ao endereço armazenado em ''p'' uma quantidade relativa ao tamanho do tipo apontado. | ||
+ | No caso é 1 (tamanho de um ''char'' é um byte). | ||
+ | |||
+ | ;EXERCÍCIO: Sem executar o programa abaixo, determine o valor de y no final do programa: | ||
+ | <syntaxhighlight lang=c> | ||
+ | main() | ||
+ | { | ||
+ | char x[10]="ifsc"; | ||
+ | char *p, y; | ||
+ | p = x + 2; | ||
+ | y= *p; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | ;Apontando para um vetor de inteiros | ||
+ | |||
+ | |||
+ | Da mesma forma que usamos um ponteiro para ''char'' para apontar uma ''string'', podemos fazer um ponteiro para ''int'' apontar para para um elemento de um vetor de inteiros. | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | main() | ||
+ | { | ||
+ | int x[10]= {0,1,2,3,4,5,6,7,8,9}; | ||
+ | int *p; | ||
+ | int i; | ||
+ | p = x; | ||
+ | i=0; | ||
+ | while (i<10) { | ||
+ | printf(" endereco %p e conteudo %d\n", p, *p); | ||
+ | p++; | ||
+ | i++; | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | '''OBSERVE''' que p++ incrementa em 4 unidades. | ||
+ | |||
+ | ;Apontando para estruturas | ||
+ | |||
+ | Ponteiros podem apontar para qualquer "objeto" de qualquer tipo. Vamos verificar como é possível apontar para uma estrutura: | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | struct TRegistro { | ||
+ | char nome[20]; | ||
+ | int idade; | ||
+ | } Tabela[4] = { | ||
+ | {"joao",18,}, | ||
+ | {"maria",18,}, | ||
+ | {"jose",19,}, | ||
+ | {"lara",17,}, | ||
+ | }; | ||
+ | struct TRegistro *p; | ||
+ | main() | ||
+ | { | ||
+ | p = &Tabela[3]; /*p aponta para o registro 3 da tabela */ | ||
+ | printf("O nome na posição 3 é %s e idade = %d\n", p->nome,p->idade); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | '''NOTE''' que o uso de '''p->nome''' é uma alternativa ao uso de (*p).nome | ||
+ | |||
+ | No primeiro caso pode-se ler: o campo nome do objeto que é apontado por p. | ||
+ | |||
+ | ;Escrevendo e lendo de campos de uma estrutura através de ponteiro | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | struct TRegistro { | ||
+ | char nome[20]; | ||
+ | int idade; | ||
+ | } Tabela[4]; | ||
+ | struct TRegistro *p; | ||
+ | main() | ||
+ | { | ||
+ | int i; | ||
+ | p = &Tabela[0]; /*p aponta para o registro 0 da tabela */ | ||
+ | for (i=0;i<4;i++,p++) | ||
+ | { | ||
+ | printf("Digite o nome e idade da pessoa %d\n", i+1); | ||
+ | scanf("%s %d",p->nome,&(p->idade)); | ||
+ | printf("\n\n>>>> O nome e idade da pessoa %d é: %s, %d \n\n\n", i+1, p->nome, p->idade); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;Retornando uma estrutura em uma função | ||
+ | |||
+ | No exemplo a abaixo a função ''RetornarStruct()'' retorna um ponteiro para uma estrutura. | ||
+ | O cuidadado que se deve ter é que a função não deveria apontar para uma estrutura que foi criada localmente | ||
+ | na função! | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | struct TRegistro { | ||
+ | char nome[20]; | ||
+ | int idade; | ||
+ | } Tabela[4] = { | ||
+ | {"joao",18,}, | ||
+ | {"maria",18,}, | ||
+ | {"jose",19,}, | ||
+ | {"lara",17,}, | ||
+ | }; | ||
+ | struct TRegistro *p; | ||
+ | struct TRegistro * RetornarStruct(int indice) | ||
+ | { | ||
+ | return &Tabela[indice]; | ||
+ | } | ||
+ | main() | ||
+ | { | ||
+ | p = RetornarStruct(2); /*p aponta para o registro 3 da tabela */ | ||
+ | printf("O nome na posição 2 é %s e idade = %d\n", p->nome,p->idade); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;Passando uma estrutura como parâmetro | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | struct TRegistro { | ||
+ | char nome[20]; | ||
+ | int idade; | ||
+ | } Tabela[4] = { | ||
+ | {"joao",18,}, | ||
+ | {"maria",18,}, | ||
+ | {"jose",19,}, | ||
+ | {"lara",17,}, | ||
+ | }; | ||
+ | struct TRegistro *p; | ||
+ | void MudarStruct(struct TRegistro *p1, int indice) | ||
+ | { | ||
+ | Tabela[indice] = *p1; | ||
+ | } | ||
+ | main() | ||
+ | { | ||
+ | struct TRegistro aux = {"luisa",16}; | ||
+ | MudarStruct(&aux,2); | ||
+ | p = &Tabela[2]; | ||
+ | printf("O nome na posição 2 é %s e idade = %d\n", p->nome,p->idade); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | ;Os argumentos argc e argv (o ponteiro de strings argv) | ||
+ | |||
+ | A função main() pode ter parâmetros formais, mas o programador não pode escolhores quais serão eles. | ||
+ | A declaração que se pode ter para a função main() é: | ||
+ | int main (int argc, char *argv[]); | ||
+ | Exemplo: | ||
+ | Escreva um programa que faça uso dos parâmentros argv e argc. O programa deverá receber | ||
+ | da linha de comando o dia, mês e ano correntes (dd/mm/aaaa), e imprimir a data em formato apropriado. | ||
+ | Veja o exemplo, supondo que o executável se chame data: | ||
+ | $ data 04 11 2016 | ||
+ | O programa deverá imprimir: | ||
+ | $ 04 de novembro de 2016<syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | void main(int argc, char *argv[]) | ||
+ | { | ||
+ | int mes; | ||
+ | char *nomemes [] = {"janeiro","fevereiro","março","abril","maio","junho","julho","agosto","setembro","outubro","novembro","dezembro"}; | ||
+ | if(argc == 4) /* Testa se o numero de parametros fornecidos esta' (nome do programa, o dia, o mes e os dois ultimos algarismos do ano */ | ||
+ | { | ||
+ | /* argv contem strings. A string referente ao mes deve ser | ||
+ | * transformada em um numero inteiro. A funcao atoi esta sendo | ||
+ | * usada para isto: recebe a string e transforma no inteiro equivalente | ||
+ | */ | ||
+ | mes = atoi(argv[2]); | ||
+ | if (mes<1 || mes>12) /* Testa se o mes e' valido */ | ||
+ | printf("Erro!\nUso mes: mm, deve ser de 1 a 12.\n"); | ||
+ | else | ||
+ | printf("\n%s de %s de %s\n\n", argv[1], nomemes[mes-1],argv[3]); | ||
+ | } | ||
+ | else | ||
+ | printf("Erro!\nUso: dd/mm/aaaa, devem ser inteiros, ou estão faltando.\n"); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Usando ponteiros como parâmetros de entrada saída de funções}} | ||
+ | Enviar um ponteiro a uma função tem diversas aplicações, uma delas é a de evitar redundância de dados e realizar a leitura de informações "diretas da fonte". Estas informações quando são de grande volume também poderiam requisitar um grande volume de memória para copiar, então mais um motivo para se passar a referência (ponteiro). Observe como podemos usar ponteiros na passagem de parâmetros: <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | void str_cpy(char *pdest, char *pfonte) | ||
+ | { | ||
+ | while (*pfonte!=0) { | ||
+ | *pdest++ = *pfonte++; | ||
+ | } | ||
+ | *pdest = 0; | ||
+ | } | ||
+ | int str_len (char *p) | ||
+ | { | ||
+ | int i=0; | ||
+ | while (*p++!=0) | ||
+ | i++; | ||
+ | return i; | ||
+ | } | ||
+ | main() | ||
+ | { | ||
+ | char fonte[10]="ifsc"; | ||
+ | char destino[10]; | ||
+ | str_cpy(destino, fonte); | ||
+ | printf("string destino = %s\n", destino); | ||
+ | printf("tamanho de dest = %d\n", str_len(destino)); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | Este recurso também tem especial utilidade quando se deseja que a função retorne mais de um valor. Como sabemos a função só pode ter um retorno. Utilizando ponteiro, portanto, pode-se passar um endereço de referência para que a função utilize este espaço de memória para copiar um resultado que será posteriormente aproveitado pela função invocadora: <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | void alfa(int *p) | ||
+ | { | ||
+ | *p=10; | ||
+ | } | ||
+ | main() | ||
+ | { | ||
+ | int x; | ||
+ | x =5; | ||
+ | printf("Valor de x antes da chamada de alfa = %d\n", x); | ||
+ | alfa(&x); | ||
+ | printf("Valor de x depois da chamada de alfa = %d\n", x); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Exercícios - C (série 5)}} | ||
+ | |||
+ | #Implemente um programa que obtém do usuário uma string, utilizando um laço de repetição imprima esta string caractere por caractere utilizando um ponteiro de char. | ||
+ | #Implementar a função ''str_cat'' que concatena duas ''strings'' usando ponteiros. | ||
+ | #Implemente um programa que receba um vetor de inteiros e invova uma função que retorna o maior elemento e o menor elemento deste vetor. Utilize ponteiro no laço para realizar a busca. Faça também passagem de parâmetro por referência para que haja dois retornos nesta função. Imprima o resultado no final. | ||
+ | #Ordenar valores de um vetor de inteiros passando por referencia o ponteiro para esse vetor. | ||
+ | #Crie um programa que contém uma função "void strcopy(char *, char *, int)" que receba uma string de origem e outra de destino como parâmetro e um inteiro "tamanho" realizando a cópia da string origem na string destino. Imprima em tela ambas as strings para comparação. (id.:5.01) | ||
+ | #Implememtar um programa que recebe 3 parâmetros na linha de comando: dois números reais e um operador (char). Operador pode ser + ou menos. O programa deve mostrar o resultado da operação. Exemplo: calcula 3.5 + 2.6. Dica: usar a função '''atof''' para converter '''string''' em '''float'''. | ||
+ | #Implementar um programa chamado ''cmpcadeia'' que testa se duas strings passadas na linha de comando são iguais. O programa deve imprimir uma mensagem indicando se são iguais ou diferentes. Usar a função strcmp da biblioteca. Caso sejam passados mais ou menos que dois parâmetros o programa deve se encerrar mostrando uma indicão de alerta ao usuario de que os parametros estao incorretos. | ||
+ | |||
+ | ;Referências Complementares | ||
+ | *[http://pw1.netcom.com/~tjensen/ptr/ch1x.htm http://pw1.netcom.com/~tjensen/ptr/ch1x.htm] | ||
+ | *[http://eternallyconfuzzled.com/tuts/languages/jsw_tut_pointers.aspx http://eternallyconfuzzled.com/tuts/languages/jsw_tut_pointers.aspx] | ||
+ | *[http://duramecho.com/ComputerInformation/WhyCPointers.html http://duramecho.com/ComputerInformation/WhyCPointers.html] | ||
+ | *[http://boredzo.org/pointers/ http://boredzo.org/pointers/] | ||
+ | *[http://www.mtm.ufsc.br/~azeredo/cursoC/aulas/c600.html Link Aula Ponteiros UFMG] | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | ==Utilizando typedef== | ||
+ | {{collapse top | Typedef introdução}} | ||
+ | O compilador possui uma lista de tipos definidos como 'char', 'int', 'float' e outros. O C permite ainda que outros tipos sejam adicionados por código através da diretiva 'typedef'. A vantagem é principalmente tornar o programa mias inteligível, utilizando-se nomes intuitivos. | ||
+ | |||
+ | Um exemplo, em um programa que processa cálculos financeiros poderia-se criar um tipo chamado para representar valores monetários, o tipo "Real", por exemplo. O comando seria conforme segue: | ||
+ | typedef float Real; | ||
+ | |||
+ | Então, declarar variáveis como salário, comissão e outras sendo do tipo "Real" tornaria o programa mais fácil de compreender. | ||
+ | Real salario, comissao; //Seria equivalente a declarar "float salario, comissao", porem com 'Real' fica mais inteligível | ||
+ | |||
+ | Outra vantagem de uso do typedef é a portabilidade entre plataformas. Imaginando um programa que roda em uma máquina 64 bits e é portado para uma 32 bits, ou 16 bits. Neste exemplo, a tratativa de números 64 bits é bastante diferente entre plataformas. Imaginando um campo 'quantidade' que pode armazenar números inteiros que podem variar até 2 bilhões (positivo ou negativo). Em uma máquina em que o inteiro ocupa 32 bits este variação é possível em um 'int'. Porém em uma plataforma cujo o inteiro ocupa 16 bits (short) já não será possível tal variação. Uma possível solução: | ||
+ | typedef int quantidade; //Plataformas 32 bits ou superior | ||
+ | Ou: | ||
+ | typedef long quantidade; //Plataformas 16 bits | ||
+ | |||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Utilizando typedef em estruturas}} | ||
+ | ;Usando o ''typedef'' para ajudar na definição e declaração de estruturas | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | |||
+ | void main() | ||
+ | { | ||
+ | typedef struct { | ||
+ | int x; | ||
+ | int y; | ||
+ | } TTeste; | ||
+ | |||
+ | TTeste *teste; | ||
+ | |||
+ | teste = (TTeste *) malloc (sizeof(TTeste)); | ||
+ | if (teste==NULL) { | ||
+ | printf("erro de alocação"); | ||
+ | exit(1); | ||
+ | } | ||
+ | |||
+ | teste->x=10; | ||
+ | |||
+ | free(teste); | ||
+ | teste=NULL; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | ==Recursividade== | ||
+ | {{collapse top | Introdução a Recursividade}} | ||
+ | |||
+ | A recursividade ocorre quando uma função chama a si própria. O retorno da função é sempre dado a função que a chamou, então se a função chama a si própria ela mesma recebe o retorno de uma segunda chamada, a segunda chamada receberá o retorno se esta se chamar uma terceira vez e assim por diante. Esta característica é possível pois o C quando inicia a execução de uma função, salva o contexto da anterior em uma pilha, esta função anterior oportunamente receberá o retorno de uma função que chamou. | ||
+ | |||
+ | É importante que a função quando projetada para trabalhar recursivamente possui um teste seguro para que não se chame infinitamente. | ||
+ | |||
+ | Há ainda dois modos de operação, simples ou múltiplo. Na recursividade simples a função se chama apenas uma vezes dentro de uma execução. Na recursividade múltipla em um ciclo de execução a função se chama mais de uma vez (na mesma recursão). O modo simples tende a ser mais eficiente, o tempo e espaço tendem a aumentar linearmente conforme a quantidade de recursões. No modo múltiplo o tempo de execução e o espaço de memória que a função precisa aumentam exponencialmente. | ||
+ | |||
+ | Fontes: | ||
+ | *KING, K.N. C Programming: A Modern Approach; 2ª ed. [S.l]:W. W. Norton & Company, 2008. 832p. ISBN 9780393979503. | ||
+ | *[https://en.wikipedia.org/wiki/Recursion wikipedia] | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Exemplo de recursividade simples}} | ||
+ | ;Exemplo 1: Cálculo do fatorial | ||
+ | |||
+ | O fatorial de um número natural n, representado por n!, é o produto de todos os inteiros positivos menores ou iguais a n [https://pt.wikipedia.org/wiki/Fatorial fonte]. | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | <stdio.h> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int in, out = 0; | ||
+ | printf("Entre com o valor de entrada da fatorial: "); | ||
+ | scanf("%d",&in); | ||
+ | out = fact(in); | ||
+ | printf("Resultado: %d\n",out); | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | int fact(int n) | ||
+ | { | ||
+ | if (n <= 1) | ||
+ | { | ||
+ | printf("Finalmente n = 1, a função para se se chamar e retorna o resultado final.\n"); | ||
+ | return 1; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | printf("Nesta chamada n = %d, esta função se chamará recursivamente se for maior que 1.\n",n); | ||
+ | return n * fact(n-1); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | No exemplo acima, se entrarmos com o valor 3 (para realizar o fatorial de 3), a seguinte sequencia será processada: | ||
+ | fact(3) Como 3 não é menor ou igual a 1, realiza chamada de si mesma porém com n-1 que será 2 | ||
+ | fact(2) Como 2 não é menor ou igual a 1, realiza chamada de si mesma com n-1 que será 1 | ||
+ | fact(1) Finalmente 1 é menor ou igual a 1, então o retorno será 1, este retorno será entregue a chamada anterior, quando era fact(2) | ||
+ | fact(2) retornará n * 1 (retorno recebido), resultado 2 | ||
+ | fact(3) retornará n * 2 (2 foi o retorno que ela recebeu), resultando 6 | ||
+ | |||
+ | Este modo é chamado de recursividade simples pois a função fact() é chamada apenas uma vez em uma recursão. | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Exemplo de recursividade múltipla}} | ||
+ | ;Exemplo 2: Cálculo do número de Fibonacci e da sequência | ||
+ | |||
+ | Na matemática os números de Fibonacci são definidos pela equação F(n) = F(n-1) + F(n-2) onde F(0) = 0 e F(1) = 1 [https://en.wikipedia.org/wiki/Fibonacci_number fonte]. | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int in, out = 0, i; | ||
+ | printf("Entre com o sequencial que deseja saber o fibonacci: "); | ||
+ | scanf("%d",&in); | ||
+ | out = fib(in); | ||
+ | printf("Resultado: %d\n",out); | ||
+ | |||
+ | printf("A sequencia fica assim:\n"); | ||
+ | for (i=1;i<=in;i++) printf("%d ",fib(i)); | ||
+ | printf("\n"); | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | int fib(int n) | ||
+ | { | ||
+ | if (n-1 <= 0) | ||
+ | return 0; | ||
+ | else if (n-1 == 1) | ||
+ | return 1; | ||
+ | else { | ||
+ | return fib(n-1) + fib(n-2); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top|Exercícios - avançado (série 6)}} | ||
+ | #Qual a limitação de amostragem do resultado do fatorial? Por que existe limitação? | ||
+ | #Como se comporta o tempo de processamento a medida que se solicita uma quantidade maior de recursões? | ||
+ | #Observe que a função fib() se chama duas vezes em cada recursão, observe como o tempo de processamento é afetado a medida que se aumenta o número de fibonacci requisitado | ||
+ | #Qual o número máximo de fibonacci que pode ser obtido neste exemplo, por que existe esta limitação? | ||
+ | #Execute este código em um terminal e em outro rode um programa como o 'top' para observar o uso de recursos que cada programa está fazendo. Vá aumentando a sequencia para verificar como o programa se comporta. (para sair do 'top' pressione 'q'). | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | ==Acessando arquivos em C== | ||
+ | {{collapse top | Introdução}} | ||
+ | Arquivos em C são tratados como [https://en.wikipedia.org/wiki/Stream_(computing) "streams"] que literalmente significa córrego (riacho), em computação significa dados em fluxo. Utilizado para tratar quantidade desconhecida de informações (potencialmente infinita). Por esta característica arquivos são acessados através de ponteiros do tipo FILE * (um tipo de dados declarado na biblioteca <stdio.h>). | ||
+ | |||
+ | Ainda na stdio, há três streams padrão (stdin, stdout e stderror) que não precisam ser declarados e estão prontos para uso. Funcões como scanf e getchar, por exemplo, na prática estão obtendo dados do teclado (stdin) e funções como printf e putchar estão escrevendo da tela (stdout). Eventuais falhas serão também enviadas para a tela (stderr). | ||
+ | |||
+ | A quantidade de streams que um programa mantém aberto pode ser limitado pelo sistema operacional. | ||
+ | |||
+ | A biblioteca stdio.h suporta dois tipos de arquivos: [http://en.wikipedia.org/wiki/Binary_file binários] ou [http://en.wikipedia.org/wiki/Text_file texto]. Os arquivos tipo texto são compostos por caracteres humanamente compreensíveis. Já os arquivos binários são codificados. Arquivos binários podem armazenar mais informações em menor espaço e são normalmente mais fáceis de serem lidos por programas tornando-os também mais performáticos. | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Operações de abertura e fechamento de arquivos}} | ||
+ | ;Operação abrir arquivo | ||
+ | A primeira ação será a de abertura de uma arquivo. É realizado com a função fopen que deve receber como parâmetros o nome do arquivo a ser aberto e o modo de abertura que especifica se será para leitura ou escrita. A diretiva restrict que aparece em ambos os argumentos não é muito relevante para o momento, basicamente está dizendo que estes espaços de memória não podem ser compartilhados (C99). | ||
+ | <syntaxhighlight lang=c> | ||
+ | FILE *fopen(const char * restrict filename, const char * restrict mode) | ||
+ | </syntaxhighlight> | ||
+ | ;Um exemplo de chamada seria: | ||
+ | <syntaxhighlight lang=c> | ||
+ | FILE * arquivo; | ||
+ | arquivo = fopen("IFSC.txt", "r"); | ||
+ | </syntaxhighlight> | ||
+ | Onde "r" significa somente leitura | ||
+ | ;Outro exemplo de chamada seria: | ||
+ | <syntaxhighlight lang=c> | ||
+ | arquivo = fopen("c:\\temp\\temp.txt", "w"); | ||
+ | </syntaxhighlight> | ||
+ | Onde "w" significa que o arquivo está sendo aberto para escrita | ||
+ | Observe também o uso de "\\" e não apenas "\". Isto de deve pois algo como "C:\temp\temp.txt" teria então um "\t" que significa TAB. O uso de "\\" previne esta má interpretação. Uma outra alternativa é escrever "C:/temp/temp.txt", ou seja, com as barras invertidas. | ||
+ | O operação fopen deve retornar um endereço válido se for bem sucedida, se falhar retornará NULL. Razões para falhas estão listadas [http://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html aqui]. | ||
+ | |||
+ | ;Modos de acesso a arquivos (arquivos tipo texto) | ||
+ | *"r": Abre para leitura (arquivo precisa existir) | ||
+ | *"w": Abre para escrita (o arquivo não necessariamente precisa existir, o conteúdo possivelmente será sobrescrito caso não tratado, ver cursor) | ||
+ | *"a": Abre para escrita adicionando dados no final do arquivo (o arquivo não necessariamente precisa existir) | ||
+ | *"r+": Abre para leitura e escrita (arquivo precisa existir) | ||
+ | *"w+": Abre para leitura e escrita (trunca se o arquivo existir, apagando o conteúdo anterior. Permite leitura) | ||
+ | *"a+": Abre para leitura e escrita adicionando dados no final do arquivo (o arquivo não necessariamente precisa existir) | ||
+ | O caractere '+' representa o modo atualização (update mode). | ||
+ | Para arquivos binários devem ter a letra "b" associada ("rb", "wb", "ab", "r+b"/"rb+", "w+b"/"wb+", "a+b"/"ab+"). No sistema operacional Linux a especificação de abertura de arquivo como binário não trará mudança pois é utilizado apenas um caractere para representar nova linha (\n). Porém, para manter portabilidade com o Windows que utiliza dois caracteres de nova linha '\r\n' é importante utilizar o modo 'b' para arquivos binários. | ||
+ | ;Operação fechar arquivo | ||
+ | Permite o programa fechar um arquivo que não está mais sendo utilizado. Deve receber como parâmetro o ponteiro (FILE *) para o arquivo. Se fechar com sucesso retornará zero, caso ocorre erro retornará EOF (uma macro definida na stdio.h. | ||
+ | <syntaxhighlight lang=c> | ||
+ | int fclose(FILE *stream); | ||
+ | </syntaxhighlight> | ||
+ | ;Um exemplo de chamada seria: | ||
+ | <syntaxhighlight lang=c> | ||
+ | fclose(arquivo); | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Operações de escrita de arquivos}} | ||
+ | ;Operação escrever texto formatado em arquivo | ||
+ | Escreve uma saída em um stream apontado. A string apontada no segundo argumento se parece as utilizadas na função "printf". | ||
+ | <syntaxhighlight lang=c> | ||
+ | int fprintf(FILE * restrict stream, const char * restrict format, ...); | ||
+ | </syntaxhighlight> | ||
+ | ;Outras operações de escrita: | ||
+ | <syntaxhighlight lang=c> | ||
+ | int fputc(int c, FILE *stream); //Escreve um caracter | ||
+ | int fputs(const char * restrict s, FILE restrict *stream); //Escreve uma string | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;Exemplo: Abrindo e escrevendo um arquivo | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | int main(void) | ||
+ | { | ||
+ | FILE *p_arq; | ||
+ | int i; | ||
+ | |||
+ | if ((p_arq=fopen("IFSC.txt", "w")) == NULL) { | ||
+ | printf("Problemas na abertura do arquivo\n"); | ||
+ | return 0; | ||
+ | } else { | ||
+ | printf("Arquivo aberto com sucesso. Iniciando escrita no arquivo...\n"); | ||
+ | } | ||
+ | |||
+ | for (i = 0; i<10;i++) { | ||
+ | /* A funcao fprintf devolve o número de bytes gravados ou EOF se houve erro na gravação */ | ||
+ | if((fprintf(p_arq,"Linha %d\n",i))==EOF) { | ||
+ | printf("Erro ao escrever no arquivo!\n"); | ||
+ | return -1; | ||
+ | } | ||
+ | } | ||
+ | printf("Fim da escrita, observe o arquivo 'IFSC.txt' criado na mesma pasta deste executável!\n"); | ||
+ | fclose(p_arq); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | Note que se o arquivo IFSC.txt não existir, ele será criado. | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Operações de leitura de arquivos}} | ||
+ | |||
+ | ;Operação escrever texto formatado em arquivo | ||
+ | Lê informações de um stream apontado em um formato definido. | ||
+ | <syntaxhighlight lang=c> | ||
+ | int fscanf(FILE *stream, const char *format, ...); | ||
+ | </syntaxhighlight> | ||
+ | ;Outras operações de escrita: | ||
+ | <syntaxhighlight lang=c> | ||
+ | int fgetc(FILE *stream); //Lê um caracter e avança o cursor | ||
+ | char *fgets(char *str, int n, FILE *stream); //Lê um conjunto de caracteres limitado a n de tamanho, nova linha ou fim do arquivo | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;Exemplo: Abrindo e lendo um arquivo | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | FILE *p_arq; | ||
+ | int i,j; | ||
+ | char buff[100]; | ||
+ | |||
+ | if ((p_arq=fopen("IFSC.txt", "r")) == NULL) { | ||
+ | printf("Problemas na abertura do arquivo, o arquivo existe?\n"); | ||
+ | return 0; | ||
+ | } else { | ||
+ | printf("Arquivo aberto com sucesso. Inicio da leitura do arquivo...\n"); | ||
+ | } | ||
+ | |||
+ | while(1) { | ||
+ | if((fscanf(p_arq,"%s %d",buff,&j))==EOF) { | ||
+ | printf("Fim de leitura\n"); | ||
+ | break; | ||
+ | } | ||
+ | printf("%s %d\n",buff,j); | ||
+ | } | ||
+ | fclose(p_arq); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Note que o fscanf se comporta de forma similar ao scanf. A função retorna o caractere EOF (end-of-file) quando não existe mais dados a serem lidos. | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Adicionando uma linha no final de um arquivo}} | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | #include <time.h> | ||
+ | |||
+ | int main(void) | ||
+ | { | ||
+ | time_t ltime; | ||
+ | FILE *fp; | ||
+ | |||
+ | if ((fp=fopen("leituras.log", "a")) == NULL) { | ||
+ | printf("Problemas na abertura do arquivo\n"); | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | time(<ime); | ||
+ | if ((fputs(ctime(<ime), fp)) != EOF ) { | ||
+ | fclose(fp); | ||
+ | } else { | ||
+ | printf("Erro na escrita do arquivo!\n"); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | Tipicamente, quando se abre uma arquivo para leitura/escrita, o "cursor" de acesso fica na posição 0 (início do arquivo). Se o arquivo for aberto em modo append ('a'), o "cursor" é posicionado no final. A cada acesso (leitura ou escrita), este cursor é incrementado conforme o número de dados lidos ou escritos. | ||
+ | Execute este código algumas vezes e vá observando o que ocorre com o arquivo 'leituras.log'. | ||
+ | |||
+ | ;Exemplo 2 | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | #include <time.h> | ||
+ | #include <string.h> | ||
+ | int main(void) | ||
+ | { | ||
+ | time_t ltime; | ||
+ | FILE *fp; | ||
+ | struct tm *info; | ||
+ | char buffer[100],b2[100]; | ||
+ | |||
+ | if ((fp=fopen("tst.log", "a+")) == NULL) { | ||
+ | printf("Problemas na abertura do arquivo\n"); | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | time(<ime); | ||
+ | info = localtime( <ime ); | ||
+ | strftime(buffer,sizeof(buffer),"%x-%I:%M%p", info); | ||
+ | printf("Formatted date & time : |%s|\n", buffer ); | ||
+ | strcat(buffer," Joao\n"); | ||
+ | |||
+ | if ((fputs(buffer, fp)) == EOF ) { | ||
+ | printf("Erro na escrita do arquivo!\n"); | ||
+ | } | ||
+ | |||
+ | fseek(fp,0,SEEK_SET); | ||
+ | while(1) { | ||
+ | if((fscanf(fp,"%s %s",buffer,b2))==EOF) { | ||
+ | printf("Fim de leitura\n"); | ||
+ | break; | ||
+ | } | ||
+ | printf("Marcação lida: %s %s\n",buffer,b2); | ||
+ | } | ||
+ | fclose(fp); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Alterando o cursor com fseek}} | ||
+ | Observe este exemplo que escreve em um arquivo e depois lê o conteúdo escrito. Neste caso é utilizado fseek para voltar o cursor para o início do arquivo. | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | FILE *p_arq; | ||
+ | int i,j; | ||
+ | char buff[100]; | ||
+ | |||
+ | if ((p_arq=fopen("IFSC.txt", "w+")) == NULL) { | ||
+ | printf("Problemas na abertura do arquivo\n"); | ||
+ | return 0; | ||
+ | } else { | ||
+ | printf("Aberto com sucesso. Iniciando escrita no arquivo...\n"); | ||
+ | } | ||
+ | |||
+ | for (i = 0; i<10;i++) { | ||
+ | if((fprintf(p_arq,"LINHA %d\n",i))==EOF) { | ||
+ | printf("Erro ao escrever no arquivo!\n"); | ||
+ | return -1; | ||
+ | } | ||
+ | } | ||
+ | printf("Escrito! 'IFSC.txt' criado na pasta deste executável!\n"); | ||
+ | |||
+ | /*Muda a posição do cursor para o início do arquivo*/ | ||
+ | fseek(p_arq,0,SEEK_SET); | ||
+ | while(1) { | ||
+ | if((fscanf(p_arq,"%s %d",buff,&j))==EOF) { | ||
+ | printf("Fim de leitura\n"); | ||
+ | break; | ||
+ | } | ||
+ | printf("%s %d\n",buff,j); | ||
+ | } | ||
+ | fclose(p_arq); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Tratando erros de abertura de arquivo}} | ||
+ | Quando não é possível abrir um arquivo a função retorna NULL, porém para se saber o real motivo da falha é necessário consultar o "errno" que fica setado na biblioteca <errno.h>. Há diversos códigos de erro para váriadas funções, no caso da fopen, consulte [http://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html aqui] os erros possíveis. | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | #include <errno.h> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | FILE *fp; | ||
+ | |||
+ | printf("Para simular o erro certifique-se de não haver um arquivo 'testeErro.txt' na pasta\n"); | ||
+ | if ((fp = fopen("testeErro.txt","r")) == NULL) { | ||
+ | if (errno == ENOENT) | ||
+ | printf("Código de erro %d, arquivo não existe!\n",errno); | ||
+ | else | ||
+ | printf("Erro não previsto (código: %d)!\n",errno); | ||
+ | return 0; | ||
+ | } | ||
+ | fclose(fp); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Exercícios - avançado (série 7)}} | ||
+ | #Após criar um arquivo IFSC.txt com o código acima, modifique o texto deste arquivo manualmente, escreva seu nome, por exemplo, salve e feche. Execute novamente o executável acima. O que ocorreu? O que você observa que foi realizado com o modo 'w' utilizado acima? | ||
+ | #Modifique o programa exemplo para que abra o arquivo com modo "r+", Aumente e diminua a quantidade de iterações do loop para escrever mais e menos linhas. Observe o comportamento nos modos "w"/"w+" e "r+" . O que você conclui de diferença no comportamento dos modos 'r+' e 'w'/'w+'? | ||
+ | #Crie um programa que recebe como parâmetro de entrada o nome de um arquivo que deve ser criado e num segundo argumento um texto qualquer que deve ser inicializado neste arquivo. Os parâmetros de entrada são argc e argv - veja documentação aqui na wiki. | ||
+ | #Utilize as funções fputc e fputs para escrever em arquivos | ||
+ | #Implemente um programa em C que lê de um arquivo chamado "anagrama.txt" duas palavras (uma na primeira linha e a outra na segunda linha). O programa analisa estas palavras e diz se são ou não anagramas (palavra formada pela alteração da ordem ou transposição de letras) ex.: “America” e “Iracema”, “Roma” e “Amor”, “Semolina” e “Is no meal” são anagramas). Utilize o modo de acesso a arquivo "r".<br> | ||
+ | #Implemente um programa em C que dada uma frase obtida através de um scanf, torne todas as letras em maiúsculas (código 1), torne todas em minúsculas (código 2) ou a primeira letra de cada palavra em maiúscula e demais minúsculas (código 3) e escreve em tela o resultado. O limite para a frase digitada é 100 caracteres. Faça estas operações em funções distintas (tudoMaiusculo(), tudoMinusculo(), primeiraMaiuscula()), utilizando o vetor de caracteres como uma variável global. A escolha das opções dependerá de um numero inteiro lido de um arquivo chamado "config.cfg" este número deve estar escrito no início do arquivo e pode ser ser 1, 2 ou 3. O arquivo config.cfg deve ser criado manualmente e preenchido com diferentes opções para testar o algoritmo. Utilize o modo "r".<br> | ||
+ | #Implemente um algoritmo em C que conta a ocorrência de cada letra (incluindo números de 0 a 9) dada uma frase. Deve ignorar se a letra foi digitada em maiúscula ou minúscula (portando somar junto). Limite a 100 caracteres. Para cada palavra digitada deve gerar um log em uma arquivo chamado "ocorrencias.log" que contém a data/hora em que foi gerado o registro e quantas letras de cada tipo foram lidas. Utilize o modo "a" ou "a+".<br> | ||
+ | #Utilize a função rand() e gerador de semente srand() para gerar 6 números aleatórios distintos que variam de 1 a 60 e grava esta sequencia em um arquivo chamado "aposta.txt". Utilize o modo "w" ou "w+".<br> | ||
+ | #Crie uma estrutura chamada TReferencia que contém um campo "livro" de 100 caracteres e um campo "autores" de 100 caracteres. Crie um loop infinito para receber uma quantidade qualquer de livros que compõem as referencias bibliográficas de um trabalho acadêmico. O programa deve então receber duas string (que podem conter espaços em branco), uma sendo o nome do livro e a outra os autores. Recebidos estes dados devem ser instanciados na struct. Se recebido uma string contendo a palavra "sair" significa que deve-se então proceder com a finalização do programa gravando as instancias da struct em um arquivo chamado "referencias.txt". Como resultado este arquivo deve ter em cada linha o nome do livro entre aspas ("nome do livro") depois uma vírgula e o nome dos autores entre aspas também ("S. AUTOR1, S. AUTOR2").Utilize o modo "r+".<br> | ||
+ | #Utilize no exemplo acima o modo "w" ao invés de "w+". Qual sua conclusão? | ||
+ | #Teste agora com o modo append, da mesma maneira qual a diferença de "a" e "a+"? | ||
{{collapse bottom}} | {{collapse bottom}} | ||
=Referências= | =Referências= | ||
− | + | {{collapse top|Referências bibliográficas}} | |
*Araújo, Everton Coimbra de. Algoritmos: fundamento e prática; 3ª ed. [S.l]:Visual Books, 2007. 414p. ISBN 9788575022092. | *Araújo, Everton Coimbra de. Algoritmos: fundamento e prática; 3ª ed. [S.l]:Visual Books, 2007. 414p. ISBN 9788575022092. | ||
*KERNIGHAN, Brian W.; RITCHIE, Dennis M C: a linguagem de programação padrão ANSI; 1ª ed.[S.l]:Campus, 1989. 304p. ISBN 9788570015860. | *KERNIGHAN, Brian W.; RITCHIE, Dennis M C: a linguagem de programação padrão ANSI; 1ª ed.[S.l]:Campus, 1989. 304p. ISBN 9788570015860. | ||
Linha 517: | Linha 1 794: | ||
*VEIGA, Roberto G. A. Comandos do Linux: guia de consulta rápida; ed. São Paulo:Novatec, 2004. 144p. ISBN 85-7522-060-8. | *VEIGA, Roberto G. A. Comandos do Linux: guia de consulta rápida; ed. São Paulo:Novatec, 2004. 144p. ISBN 85-7522-060-8. | ||
− | + | ||
− | *[http://wiki.sj.ifsc.edu.br/images/8/82/Curso_de_linguagem_C_UFMG.pdf Apostila de C - UFMG] | + | *Adicionais |
+ | **[http://wiki.sj.ifsc.edu.br/images/8/82/Curso_de_linguagem_C_UFMG.pdf Apostila de C - UFMG] | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top|Ferramentas úteis}} | ||
+ | *[https://sourceforge.net/projects/visualg30/ VisualG3]: Uma IDE para desenvolvimento de programas em pseudocódigo (freeware), permite editar e compilar programas utilizando uma sintaxe própria de pseudocódigo muito parecida com a que trabalhamos em sala. Muito útil para verificar o funcionamento real dos algoritmos. Ver exemplos de códigos visualG3 em [http://docente.ifsc.edu.br/cleber.amaral/ensino/2016-2/PRG29002/VisualG3/ Exemplos VisualG3] | ||
+ | *[http://www.libreoffice.org/ LibreOffice]: O LibreOffice é um programa gratuito (freeware) e de código aberto (opensource). Além de editor de textos, planilhas e apresentações tem a ferramenta Draw que permite a criação de fluxogramas. | ||
+ | *[https://www.virtualbox.org/ VirtualBox]: O Oracle VirtualBox é um programa gratuito (freeware) que permite criar e instanciar máquinas virtuais. O uso de máquinas virtuais é bastante interessante quando desejamos ter diferentes sistemas operacionais em um computador bem como quando se está realizando ensaios e deseja-se isolar estes experimentos do sistema principal. | ||
+ | *[https://debian.org Debian]: O Debian, é umas das distribuições Linux mais estáveis existentes, suportando atualmente 12 arquiteturas de processador. É software livre e de código aberto e mantido por uma ampla comunidade com mais de 18000 desenvolvedores. Sua versão atual é a 8.5 (codinome Jessie, do filme Toy Story). | ||
+ | *[http://www.ubuntu.com/ Ubuntu]: O Ubuntu é uma distribuição linux (freeware e opensource) bastante estável e com uma comunidade bastante ativa que está sempre atualizando o sistema e presente nos foruns e redes sociais para dirimir dúvidas. | ||
+ | *[https://www.linuxmint.com/ LinuxMint]: O LinuxMint é uma distribuição linux (freeware e opensource) bastante estável e confortável aos usuários windows, pois traz um gerenciador de janelas configurado de uma forma mais natural para estes usuários e vem com um conjunto de programas pré-instalados que consegue atender a maior parte das demandas inicias. | ||
+ | *[http://www.fabforce.net/dbdesigner4/ dbDesigner4]: O dbDesigner é uma ferramenta gratuita para elaboração de diagramas de bancos de dados relacionais. Não trabalhamos com bancos na disciplina PRG29002, porém trabalhamos com dados, esta ferramenta é útil para organizá-los em diagramas. | ||
+ | *[http://coliru.stacked-crooked.com/ coliru]: Compilador online | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | =Projeto final= | ||
+ | O aluno deve propor ao professor um projeto de sua preferência que respeite os requisitos mínimos. Sendo aceito deverá desenvolver o projeto e apresentá-lo. | ||
+ | |||
+ | ==Requisitos mínimos== | ||
+ | *Utilizar funções (ao menos três além do main, sendo que devem receber argumentos e possuir retorno); | ||
+ | *Apresentar menu utilizando switch case e conter laço infinito; | ||
+ | *Utilizar Structs ou Unions; | ||
+ | *Utilizar bibliotecas (além da stdio.h); | ||
+ | *Utilizar diretivas de pré-compilação; | ||
+ | *Utilizar comentários; | ||
+ | *Utilizar Ponteiros; | ||
+ | *Aceitar argumento de entrada no programa; | ||
+ | *Realizar acesso a arquivo, lendo e escrevendo informações (busque requisitos que requerem persistência de dados); | ||
+ | *Gravar informações de operações em log; | ||
+ | |||
+ | ==Modelo== | ||
+ | *Trabalho individual | ||
+ | |||
+ | ==Metodologia== | ||
+ | #Apresentar a proposta de projeto ao professor | ||
+ | #Opcional: Documentar o escopo do projeto utilizando descrição narrativa (descrição simples) | ||
+ | ##Cenário | ||
+ | ##Problema | ||
+ | ##Dados de entrada e saída | ||
+ | #*O planejamento do cronograma não será cobrado porém cabe ao aluno se organizar quanto ao tempo para entrega no prazo | ||
+ | #Desenvolver o projeto | ||
+ | #Apresentar individualmente ao professor | ||
+ | #*Serão realizados testes diversos, arguido sobre o funcionamento, possibilidades de alterações, etc | ||
+ | |||
+ | ==Algumas ideias de projetos== | ||
+ | *Sugestão geral: veja em outras disciplinas que processos podem ser automatizados e proponha um projeto que realiza esta tarefa como de cálculos diversos de eletrônica, de rádio transmissão, etc. | ||
+ | *Implementar o jogo Pedra, papel ou tesoura. Neste jogo dois ou mais jogadores em diferentes computadores devem rodar um aplicativo que fará a leitura de um arquivo compartilhado. O algoritmo deve tratar as etapas do jogo (Setup do aplicativo, entrada na sala, escolha da figura e apresentação do resultado) | ||
+ | *Implementar o jogo da velha escrevendo em arquivo. Neste jogo dois jogadores em diferentes computadores devem rodar um aplicativo que fará a leitura de um arquivo compartilhado. O algoritmo deve tratar as etapas do jogo (Setup do aplicativo, entrada na sala, seleção das casas e apresentação do resultado) | ||
+ | *Implementar controle de empréstimo de objetos. Neste software o usuário poderá digitar nomes de objetos que emprestou, a pessoa a quem emprestou e automaticamente o software guarda a data. Deve haver uma opção para gerar relatório dos itens emprestados e opção para marcar a devolução (podendo manter o registro em histórico ou apagando o registro). | ||
+ | *Implementar software gerador de lista de compras. Neste software o usuário poderá digitar itens de supermercado com nome e quantidade. O software escreve num arquivo que poderá depois ser impresso. O software também pode ter função de numa segunda execução já trazer a antiga listagem digitada e permitir que o usuário apenas selecione novas quantidades ou inclua novos itens. | ||
+ | *Implementar software para realização de cálculos de eletrônica. Neste software um menu apresenta várias opções de cálculo como de potencia através de tensão e corrente, como obtenção do valor de um resistor, como solução de equivalência de paralelo de vários resistores e outras. Num arquivo texto pode ser armazenado um histórico de operações realizadas. | ||
+ | *Implementação de software para apostas na mega sena. Neste software são dadas sugestões de números para apostas de acordo com o número do sorteio da mega sena. Com este histórico armazenado é possível então entrar com um número de sorteio e digitar quais foram os números verdadeiramente sorteados na loteria federal checagem os acertos. | ||
+ | |||
+ | ==Projetos dos alunos== | ||
+ | *Amanda: Sistema de agendamento de manutenção preventiva de equipamentos de refrigeração | ||
+ | *Ameliza: Caixa eletrônico 24h | ||
+ | *André: Software de cálculos de circuitos eletrônicos | ||
+ | *Fábio: Jogo da velha com opção continuar de onde parou | ||
+ | *Felipe: Sistema de registro de atendimento de monitores | ||
+ | *Gabriel Santos: ? | ||
+ | *Guilherme: Projeto multilinguas | ||
+ | *Jennifer: Sistem de controle de barbearia | ||
+ | *Luiza: Sistema financeiro pessoal | ||
+ | *Simone: ? | ||
+ | *Stefanie: Múltiplos cruzamentos com semáforos síncronos | ||
=Plano de aula= | =Plano de aula= | ||
+ | {{collapse top|Cronograma}} | ||
{{Cronograma-top}} | {{Cronograma-top}} | ||
{{Cl|1 |9/2 | 2 | Aula inaugural, apresentação do professor e turma, apresentação da disciplina e introdução aos algoritmos | Lab Redes 2}} | {{Cl|1 |9/2 | 2 | Aula inaugural, apresentação do professor e turma, apresentação da disciplina e introdução aos algoritmos | Lab Redes 2}} | ||
Linha 552: | Linha 1 894: | ||
{{Cl|29 |23/5 | 2 | Vetor de Ponteiros e Ponteiro Para Estruturas, typedef, recursividade, apresentação do projeto | Lab Redes 1}} | {{Cl|29 |23/5 | 2 | Vetor de Ponteiros e Ponteiro Para Estruturas, typedef, recursividade, apresentação do projeto | Lab Redes 1}} | ||
{{Cl|30 |25/5 | 2 | Prática de Ponteiros e structs | Lab Redes 2}} | {{Cl|30 |25/5 | 2 | Prática de Ponteiros e structs | Lab Redes 2}} | ||
− | {{Cl|31 |30/5 | 2 | Avaliação: C até structs | Lab Redes 1}} | + | {{Cl|31 |30/5 | 2 | Avaliação: C até structs e ponteiros | Lab Redes 1}} |
{{Cl|32 |1/6 | 2 | Prática: Correção da Avaliação / possível realização de recuperação / Desenvolvimento do projeto | Lab Redes 2}} | {{Cl|32 |1/6 | 2 | Prática: Correção da Avaliação / possível realização de recuperação / Desenvolvimento do projeto | Lab Redes 2}} | ||
{{Cl|33 |6/6 | 2 | Apresentação do projeto. Prática: Desenvolvimento do Projeto | Lab Redes 1}} | {{Cl|33 |6/6 | 2 | Apresentação do projeto. Prática: Desenvolvimento do Projeto | Lab Redes 1}} | ||
{{Cl|34 |8/6 | 2 | Prática: Desenvolvimento do Projeto | Lab Redes 2}} | {{Cl|34 |8/6 | 2 | Prática: Desenvolvimento do Projeto | Lab Redes 2}} | ||
− | {{Cl|35 |13/6 | 2 | Desenvolvimento do Projeto | Lab Redes 1}} | + | {{Cl|35 |13/6 | 2 | Prática: Desenvolvimento do Projeto | Lab Redes 1}} |
− | {{Cl|36 |20/6 | 2 | Desenvolvimento do Projeto | Lab Redes 1}} | + | {{Cl|36 |20/6 | 2 | Prática: Desenvolvimento do Projeto | Lab Redes 1}} |
{{Cl|37 |22/6 | 2 | Prática: Desenvolvimento do Projeto | Lab Redes 2}} | {{Cl|37 |22/6 | 2 | Prática: Desenvolvimento do Projeto | Lab Redes 2}} | ||
− | {{Cl|38 |27/6 | 2 | | + | {{Cl|38 |27/6 | 2 | Avaliação: Apresentação do projeto | Lab Redes 1}} |
− | {{Cl|39 |29/6 | 2 | | + | {{Cl|39 |29/6 | 2 | Recuperação: Projeto | Lab Redes 2}} |
− | {{Cl|40 |4/7 | 2 | | + | {{Cl|40 |4/7 | 2 | Encerramento da disciplina | Lab Redes 1}} |
{{cronograma-botton |80}} | {{cronograma-botton |80}} | ||
+ | {{collapse bottom}} | ||
=Conceitos= | =Conceitos= | ||
Ver moodle da disciplina. | Ver moodle da disciplina. |
Edição atual tal como às 15h32min de 2 de agosto de 2017
Dados importantes
- Professor da Disciplina: Cleber Jorge Amaral
- Email: cleber.amaral@ifsc.edu.br
- Atendimento paralelo: terças e quintas das 12:00 as 13:00 na Sala Multimeios de Tele (ao lado da reprografia)
- Agenda do professor: Ver página
Dados da Disciplina
- Ementa da disciplina na wiki: Engenharia de Telecomunicações 2ª Fase
- Página no moodle: moodle
- Monitoria: Programa_de_monitoria_dos_cursos_superiores_de_Telecomunicações
Algoritmos utilizando fluxograma
Introdução aos algoritmos utilizando fluxograma
Introdução aos algoritmos utilizando fluxograma |
---|
|
Desenvolvendo algoritmos na forma de fluxogramas
Desenvolvendo algoritmos na forma de fluxogramas |
---|
Exercícios para resolver em sala de aula:
|
Pseudo-código
Pseudo-código utilizando Portugol - Introdução e condicionais
- Slides sobre pseudocódigo disponibilizados no moodle.
Ver exemplos de códigos Portugol dentro do software portugol (menu Arquivo->Abrir exemplo)
Pseudo-código utilizando Portugol - repetições
Exercícios - Pseudocodigo (série 1) |
---|
Exercícios - Pseudocodigo (série 1):
|
Exercícios - Pseudocodigo (série 2) |
---|
Exercícios - Pseudocpodigo (série 2):
Exercícios complementares:
|
Pseudo-código utilizando Portugol - sub-rotinas e registros
Exercícios - Pseudocodigo (série 3) |
---|
Exercícios - Pseudocodigo (série 3):
Parte da implementação do problema das funções trigonométricas
|
Programação em C (ANSI)
Programação em C (ANSI) - Introdução ao C
Introdução ao C e funções de saída e entrada de dados |
---|
|
Programação em C (ANSI) - Controle de fluxo em C
Programação em C (ANSI) - Condicionais
Condicionais em C |
---|
|
Exercícios - C (série 0) |
---|
|
Programação em C (ANSI) - Repetições
Estruturas de repetição em C |
---|
|
Exercícios - C (série 1) |
---|
|
Funções
Funções |
---|
|
A função main |
---|
O programa inicia pela primeira instrução contida na função main() e também se encerra na última instrução. O retorno padrão da função main é um int que representa um código de erros reconhecidos por muitos sistemas operacionais. Se o programa terminou sua execução corretamente o retorno deverá ser 0 (zero). int main(void)
{
//Programa
return 0;
}
|
O método exit() |
---|
Uma alternativa a terminação do programa chegando ao fim da função main é a função exit da biblioteca <stdlib.h>. Para esta função deve-se passar um argumento inteiro que tem o mesmo significado do código de retorno da função main, portanto exit(0) representa uma terminação normal, alternativamente exit(EXIT_SUCCESS). Para representar uma terminação anormal pode-se utilizar exit(EXIT_FAILURE) ou exit(1).
|
Vetores, strings e matrizes em C
Vetores e matrizes em C |
---|
char nome_da_string [tamanho];
|
Gerando números pseudo-aleatórios |
---|
|
Tabela ASCII |
---|
|
Vetor de tamanho variável |
---|
O vetor de tamanho variável (variable lenght array) é um recurso do C que permite que o tamanho do vetor seja definido em tempo de execução. Na prática o C irá alocar uma quantidade de memória que não precisa estar definida antes da execução. variable-lenght |
Exercícios - C (série 2) |
---|
|
Exercícios - C (série 3) |
---|
|
Tipos de dados compostos
Estruturas |
---|
Assim como o vetor a estrutura é um conjunto de dados, mas traz uma vantagem: a possibilidade de possuir "campos" de diferentes tipos de variáveis. Por exemplo, a struct TPessoa poderia ter os campos nome (char[40]) e idade (int). A declaração genérica da estrutura é: struct TNome_do_tipo { //variável 1 //variável 2 //variável N } nome_instancia;
#include <stdio.h>
struct TUsuario /* struct TUsuario é o nome do tipo que está sendo criado */
{
char userID[20];
char senha[20];
} Usuario; /* aqui é definida uma variável do tipo struct TUsuario */
struct TUsuario TabelaUsuario[20];
main()
{
scanf("%s", Usuario.userID);
scanf("%s", Usuario.senha);
scanf("%s", TabelaUsuario[10].userID);
scanf("%s", TabelaUsuario[10].senha);
}
Neste exemplo, foi definido um tipo (modelo) para o registro (struct TUsuario) e foi criada uma variável chamada Usuario a partir deste tipo. Na sequência foi criada mais uma variável (um vetor de estruturas) chamada TabelaUsuario. Note que basta usar as palavras chave struct Usuario para criar novas variáveis. O tipo completo é definido uma única vez no início.
#include <stdio.h>
#define NUM_MAX 3
struct TAluno {
char nome[30];
char matricula[11];
float b1,b2,b3,b4;
} Turma[NUM_MAX];
void print_aluno(struct TAluno aux)
{
printf("Nome -> %s\n", aux.nome);
printf("Matrícula -> %s\n", aux.matricula);
printf("Bimestre 1 -> %f\n", aux.b1);
printf("Bimestre 2 -> %f\n", aux.b2);
printf("Bimestre 3 -> %f\n", aux.b3);
printf("Bimestre 4 -> %f\n", aux.b4);
}
main()
{
int i;
for(i=0;i<NUM_MAX;i++) {
printf("Entre com o nome do aluno\n");
scanf("%s", Turma[i].nome);
printf("Entre com a matrícula do aluno\n");
scanf("%s", Turma[i].matricula);
printf("Entre com a nota do bimestre 1\n");
scanf("%f", &Turma[i].b1);
printf("Entre com a nota do bimestre 2\n");
scanf("%f", &Turma[i].b2);
printf("Entre com a nota do bimestre 3\n");
scanf("%f", &Turma[i].b3);
printf("Entre com a nota do bimestre 4\n");
scanf("%f", &Turma[i].b4);
}
for(i=0;i<NUM_MAX;i++) {
printf("=========== Aluno %d ============\n", i);
print_aluno(Turma[i]);
}
}
O exemplo a seguir demonstra como se pode copiar uma variável struct para outra do mesmo tipo. #include <stdio.h>
struct THoras{
int hora;
int minuto;
int segundo;
};
struct THoras Ontem = {2,10,57};
void main()
{
struct THoras Hoje;
Hoje = Ontem;
printf("Hora hoje = %d, Minuto hoje = %d e Segundo hoje %d\n", Hoje.hora, Hoje.minuto, Hoje.segundo);
}
Vamos ver um exemplo com estruturas definidas dentro de estruturas: #include <stdio.h>
struct TEndereco{
char rua[50];
char numero[10];
};
struct TCidadao{
char nome[50];
char cpf[20];
struct TEndereco endereco;
int num_filhos;
};
void main()
{
struct TCidadao Cidadao;
printf("Entre com o nome\n");
scanf ("%s",Cidadao.nome);
printf("Entre com o cpf\n");
scanf ("%s",Cidadao.cpf);
printf("Entre a rua\n");
scanf ("%s",Cidadao.endereco.rua);
printf("Entre a numero\n");
scanf ("%s",Cidadao.endereco.numero);
printf("Entre com o número de filhos\n");
scanf ("%d",&Cidadao.num_filhos);
}
Como toda variável, é possível dar valores para uma variável do tipo struct definida no programa: #include <stdio.h>
struct TEndereco {
char rua[50];
int numero;
};
struct TCidadao{
char nome[50];
char cpf[20];
struct TEndereco endereco;
};
int main(void)
{
//Inicializando com parâmetros em sequencia (ordem tem que ser respeitada)
struct TCidadao CidadaoMaria = {"Maria","42342342234",{"Rua AlfaBeta",145}};
//Inicializando com parâmetros via campo (não é necessário respeitar qualquer ordem)
struct TCidadao CidadaoJose = {.cpf = "1234567890", .endereco.numero = 541,.nome = "Jose",.endereco.rua = "Rua GamaDelta"};
printf("Rua do cidadao %s = %s\n", CidadaoMaria.nome, CidadaoMaria.endereco.rua);
printf("Rua do cidadao %s = %s\n", CidadaoJose.nome, CidadaoJose.endereco.rua);
}
Se não for usado o operador "&" , um parâmetro que é estrutura será passado por cópia. Não apresentaremos agora a passagem por endereço pois necessita do conceita de ponteiro. Observe o exercício abaixo. #include <stdio.h>
struct TEndereco{
char rua[50];
char numero[10];
};
struct TCidadao{
char nome[50];
char cpf[20];
struct TEndereco endereco;
int num_filhos;
};
void print_struct (struct TCidadao aux)
{
printf("nome=%s cpf=%s\n", aux.nome, aux.cpf);
printf("endereço inicial do aux %p\n", &aux);
}
void main()
{
struct TCidadao Cidadao;
printf("Entre com o nome\n");
scanf ("%s",Cidadao.nome);
printf("Entre com o cpf\n");
scanf ("%s",Cidadao.cpf);
printf("Entre a rua\n");
scanf ("%s",Cidadao.endereco.rua);
printf("Entre a numero\n");
scanf ("%s",Cidadao.endereco.numero);
printf("Entre com o número de filhos\n");
scanf ("%d",&Cidadao.num_filhos);
print_struct(Cidadao);
printf("endereço inicial do Cidadao %p\n", &Cidadao);
}
|
Exercícios - C (série 4) |
---|
|
Unions |
---|
Union é um recurso do C que permite declarar um conjunto de dados que irá ocupar um mesmo espaço. É bastante empregado quando se deseja economizar espaço ou não se tem certeza sobre qual tipo de dado deve ser armazenado para determinada instancia. No exemplo a seguir é criada uma struct chamada TProduto e dentro destra estrutura há uma área de detalhamento do produto que é de uso genérico, para alguns produtos há campos específicos para preenchimento e outros não se tem ao certo os detalhes, portanto fica um campo de uso geral.
#include <stdio.h>
struct TRoupeiro{
char cor[20];
int volume;
float peso;
};
struct TProduto{
int id;
char nome[20];
union {
struct TRoupeiro roupeiro;
char descricao_generica[sizeof(int)+sizeof(float)+20];
};
};
int main(void)
{
struct TProduto vaso_decorativo = {
.id = 2,.nome = "Vaso decorativo 1",
.descricao_generica = "em vidro - peça única"
};
struct TProduto guarda_roupas_solteiro = {
.id = 1,.nome = "Roupeiro 3 portas",
.roupeiro.cor = "CZ", .roupeiro.volume = 304,.roupeiro.peso = 50.0
};
printf("nome = %s, \ndescrição = %s, \ncor = %s, \nvolume = %d, \npeso = %f\n\n\n",
guarda_roupas_solteiro.nome,
guarda_roupas_solteiro.descricao_generica,
guarda_roupas_solteiro.roupeiro.cor,
guarda_roupas_solteiro.roupeiro.volume,
guarda_roupas_solteiro.roupeiro.peso
);
printf("nome = %s, \ndescrição = %s, \ncor = %s, \nvolume = %d, \npeso = %f\n\n\n",
vaso_decorativo.nome,
vaso_decorativo.descricao_generica,
vaso_decorativo.roupeiro.cor,
vaso_decorativo.roupeiro.volume,
vaso_decorativo.roupeiro.peso
);
}
|
Ponteiros
Ponteiros |
---|
A memória de um computador pode ser vista como um vetor de bytes. Neste espaço vimos a utilização de variáveis diversas que podem armazenar valores que podem ser obtidos do usuários, serem resultados de ariméticas e muitas outras operações. O ponteiro nada mais é que um tipo de dado igualmente armazenado em memória, porém este dado se refere a um endereço da memória, ou seja, a um outro objeto. Este recurso é muito útil para diversos propósitos, basta pensar na própria aplicação do conceito "endereço", imagine como seria localizar uma casa em uma cidade sem haver uma forma de endereçar e armazenar os endereços das casas. Explorando esta analogia, cada lote possui um endereço e pode ter um conteúdo de diferentes tipos como uma casa, um prédio ou um conjunto de lojas, enfim, trazendo para o C seria como os tipos int, char, vetores diversos, etc. Assim é a memória, cada byte possui um endereço. O tamanho da memória é definido pelo tamanho do barramento de endereços usado para acessá-la. Uma variável ocupa uma área da memória. Tipicamente uma variável to tipo char se utiliza de um byte. Já uma variável do tipo int pode (dependendo do sistema) usar 4 bytes contíguos.
#include <stdio.h>
int main(void)
{
int i = 10;
int *p;
long int li;
p = &i;
printf("Conteúdo de i: i = %d\n",i);
printf("Endereço de i: &i = %p\n",&i);
printf("Conteúdo de p: p = %p\n",p);
printf("Endereço de p: &p = %p\n",&p);
printf("Conteúdo apontado: *p = %d (conteúdo do endereço apontado por p)\n",*p);
printf("Tamanho do ponteiro = %li bytes\n",sizeof(p));
printf("Tamanho do lont int = %li bytes\n",sizeof(li));
printf("Tamanho do int = %li bytes\n",sizeof(i));
return 0;
}
Resposta obtida através do gcc em uma máquina Linux Ubuntu: Conteúdo de i: i = 10 Endereço de i: &i = 0x7ffeb25859e4 Conteúdo de p: p = 0x7ffeb25859e4 Endereço de p: &p = 0x7ffeb25859e8 Conteúdo apontado: *p = 10 (conteúdo do endereço apontado por p) Tamanho do ponteiro = 8 bytes Tamanho do lont int = 8 bytes Tamanho do int = 4 bytes
Conteúdo de i: i = 10 Endereço de i: &i = 0xbfadc2b8 Conteúdo de p: p = 0xbfadc2b8 Endereço de p: &p = 0xbfadc2bc Conteúdo apontado: *p = 10 (conteúdo do endereço apontado por p) Tamanho do ponteiro = 4 bytes Tamanho do lont int = 4 bytes Tamanho do int = 4 bytes
Observe o programa abaixo. A variável p é um ponteiro para inteiro. Isto significa que ela pode armazenar um endereço de um inteiro. #include <stdio.h>
main()
{
int x;
int *p;
x=5;
printf("Valor de x antes = %d\n", x);
p = &x;
*p=10;
printf("Valor de x depois = %d\n", x);
printf("Valor de p = %p\n", p);
}
Observe que para se referenciar o conteúdo da posição de memória apontada por p deve-se usar o asterisco: *p
main()
{
int x=10;
int y, *p;
}
Complete o código para copiar o conteúdo de x para y, sem que qualquer variável apareçam no lado esquerdo de um sinal de atribuição. Ou seja, sem envolver diretamente x e y.
main()
{
int x,y,w,*p1,*p2;
x = 20;
w = 30;
p1 = &x;
p2 = &w;
y = *p1 + *p2;
}
main()
{
int x,y,w,*p1,*p2, *p3;
x = 20;
w = 30;
p1 = &x;
p2 = &w;
y = *p1 + w;
p3 = &y;
*p3 = *p3 + 10;
y = *p1 + *p2 + *p3;
}
#include <stdio.h>
void main()
{
int x,y;
int *p;
y=0;
p=&y;
x=*p;
x=4;
(*p)++;
x--;
(*p) += x;
printf("\ny=%d x=%d\n",y,x);
}
Os ponteiro para char são muito utilizados pois permitem apontar para strings. A ideia é que ele aponte para o primeiro caracter (char) da string. Veja o exemplo abaixo. #include <stdio.h>
main()
{
char x[10]="ifsc";
char *p;
p = &x[2];
printf("x[2] = %c\n", *p);
p = x;
printf("string %s\n", p);
while (*p!=0) {
printf("Endereco %p conteúdo %c\n", p,*p);
p++;
}
}
Neste foi usado o incremento de um ponteiro, o que implica em adicionar ao endereço armazenado em p uma quantidade relativa ao tamanho do tipo apontado. No caso é 1 (tamanho de um char é um byte).
main()
{
char x[10]="ifsc";
char *p, y;
p = x + 2;
y= *p;
}
#include <stdio.h>
main()
{
int x[10]= {0,1,2,3,4,5,6,7,8,9};
int *p;
int i;
p = x;
i=0;
while (i<10) {
printf(" endereco %p e conteudo %d\n", p, *p);
p++;
i++;
}
}
OBSERVE que p++ incrementa em 4 unidades.
Ponteiros podem apontar para qualquer "objeto" de qualquer tipo. Vamos verificar como é possível apontar para uma estrutura: #include <stdio.h>
struct TRegistro {
char nome[20];
int idade;
} Tabela[4] = {
{"joao",18,},
{"maria",18,},
{"jose",19,},
{"lara",17,},
};
struct TRegistro *p;
main()
{
p = &Tabela[3]; /*p aponta para o registro 3 da tabela */
printf("O nome na posição 3 é %s e idade = %d\n", p->nome,p->idade);
}
NOTE que o uso de p->nome é uma alternativa ao uso de (*p).nome No primeiro caso pode-se ler: o campo nome do objeto que é apontado por p.
#include <stdio.h>
struct TRegistro {
char nome[20];
int idade;
} Tabela[4];
struct TRegistro *p;
main()
{
int i;
p = &Tabela[0]; /*p aponta para o registro 0 da tabela */
for (i=0;i<4;i++,p++)
{
printf("Digite o nome e idade da pessoa %d\n", i+1);
scanf("%s %d",p->nome,&(p->idade));
printf("\n\n>>>> O nome e idade da pessoa %d é: %s, %d \n\n\n", i+1, p->nome, p->idade);
}
}
No exemplo a abaixo a função RetornarStruct() retorna um ponteiro para uma estrutura. O cuidadado que se deve ter é que a função não deveria apontar para uma estrutura que foi criada localmente na função! #include <stdio.h>
struct TRegistro {
char nome[20];
int idade;
} Tabela[4] = {
{"joao",18,},
{"maria",18,},
{"jose",19,},
{"lara",17,},
};
struct TRegistro *p;
struct TRegistro * RetornarStruct(int indice)
{
return &Tabela[indice];
}
main()
{
p = RetornarStruct(2); /*p aponta para o registro 3 da tabela */
printf("O nome na posição 2 é %s e idade = %d\n", p->nome,p->idade);
}
#include <stdio.h>
struct TRegistro {
char nome[20];
int idade;
} Tabela[4] = {
{"joao",18,},
{"maria",18,},
{"jose",19,},
{"lara",17,},
};
struct TRegistro *p;
void MudarStruct(struct TRegistro *p1, int indice)
{
Tabela[indice] = *p1;
}
main()
{
struct TRegistro aux = {"luisa",16};
MudarStruct(&aux,2);
p = &Tabela[2];
printf("O nome na posição 2 é %s e idade = %d\n", p->nome,p->idade);
}
A função main() pode ter parâmetros formais, mas o programador não pode escolhores quais serão eles. A declaração que se pode ter para a função main() é: int main (int argc, char *argv[]); Exemplo: Escreva um programa que faça uso dos parâmentros argv e argc. O programa deverá receber da linha de comando o dia, mês e ano correntes (dd/mm/aaaa), e imprimir a data em formato apropriado. Veja o exemplo, supondo que o executável se chame data: $ data 04 11 2016 O programa deverá imprimir: $ 04 de novembro de 2016#include <stdio.h>
#include <stdlib.h>
void main(int argc, char *argv[])
{
int mes;
char *nomemes [] = {"janeiro","fevereiro","março","abril","maio","junho","julho","agosto","setembro","outubro","novembro","dezembro"};
if(argc == 4) /* Testa se o numero de parametros fornecidos esta' (nome do programa, o dia, o mes e os dois ultimos algarismos do ano */
{
/* argv contem strings. A string referente ao mes deve ser
* transformada em um numero inteiro. A funcao atoi esta sendo
* usada para isto: recebe a string e transforma no inteiro equivalente
*/
mes = atoi(argv[2]);
if (mes<1 || mes>12) /* Testa se o mes e' valido */
printf("Erro!\nUso mes: mm, deve ser de 1 a 12.\n");
else
printf("\n%s de %s de %s\n\n", argv[1], nomemes[mes-1],argv[3]);
}
else
printf("Erro!\nUso: dd/mm/aaaa, devem ser inteiros, ou estão faltando.\n");
}
|
Usando ponteiros como parâmetros de entrada saída de funções |
---|
Enviar um ponteiro a uma função tem diversas aplicações, uma delas é a de evitar redundância de dados e realizar a leitura de informações "diretas da fonte". Estas informações quando são de grande volume também poderiam requisitar um grande volume de memória para copiar, então mais um motivo para se passar a referência (ponteiro). Observe como podemos usar ponteiros na passagem de parâmetros: #include <stdio.h>
void str_cpy(char *pdest, char *pfonte)
{
while (*pfonte!=0) {
*pdest++ = *pfonte++;
}
*pdest = 0;
}
int str_len (char *p)
{
int i=0;
while (*p++!=0)
i++;
return i;
}
main()
{
char fonte[10]="ifsc";
char destino[10];
str_cpy(destino, fonte);
printf("string destino = %s\n", destino);
printf("tamanho de dest = %d\n", str_len(destino));
}
#include <stdio.h>
void alfa(int *p)
{
*p=10;
}
main()
{
int x;
x =5;
printf("Valor de x antes da chamada de alfa = %d\n", x);
alfa(&x);
printf("Valor de x depois da chamada de alfa = %d\n", x);
}
|
Exercícios - C (série 5) |
---|
|
Utilizando typedef
Typedef introdução |
---|
O compilador possui uma lista de tipos definidos como 'char', 'int', 'float' e outros. O C permite ainda que outros tipos sejam adicionados por código através da diretiva 'typedef'. A vantagem é principalmente tornar o programa mias inteligível, utilizando-se nomes intuitivos. Um exemplo, em um programa que processa cálculos financeiros poderia-se criar um tipo chamado para representar valores monetários, o tipo "Real", por exemplo. O comando seria conforme segue: typedef float Real; Então, declarar variáveis como salário, comissão e outras sendo do tipo "Real" tornaria o programa mais fácil de compreender. Real salario, comissao; //Seria equivalente a declarar "float salario, comissao", porem com 'Real' fica mais inteligível Outra vantagem de uso do typedef é a portabilidade entre plataformas. Imaginando um programa que roda em uma máquina 64 bits e é portado para uma 32 bits, ou 16 bits. Neste exemplo, a tratativa de números 64 bits é bastante diferente entre plataformas. Imaginando um campo 'quantidade' que pode armazenar números inteiros que podem variar até 2 bilhões (positivo ou negativo). Em uma máquina em que o inteiro ocupa 32 bits este variação é possível em um 'int'. Porém em uma plataforma cujo o inteiro ocupa 16 bits (short) já não será possível tal variação. Uma possível solução: typedef int quantidade; //Plataformas 32 bits ou superior Ou: typedef long quantidade; //Plataformas 16 bits |
Utilizando typedef em estruturas |
---|
#include <stdio.h>
#include <stdlib.h>
void main()
{
typedef struct {
int x;
int y;
} TTeste;
TTeste *teste;
teste = (TTeste *) malloc (sizeof(TTeste));
if (teste==NULL) {
printf("erro de alocação");
exit(1);
}
teste->x=10;
free(teste);
teste=NULL;
}
|
Recursividade
Introdução a Recursividade |
---|
A recursividade ocorre quando uma função chama a si própria. O retorno da função é sempre dado a função que a chamou, então se a função chama a si própria ela mesma recebe o retorno de uma segunda chamada, a segunda chamada receberá o retorno se esta se chamar uma terceira vez e assim por diante. Esta característica é possível pois o C quando inicia a execução de uma função, salva o contexto da anterior em uma pilha, esta função anterior oportunamente receberá o retorno de uma função que chamou. É importante que a função quando projetada para trabalhar recursivamente possui um teste seguro para que não se chame infinitamente. Há ainda dois modos de operação, simples ou múltiplo. Na recursividade simples a função se chama apenas uma vezes dentro de uma execução. Na recursividade múltipla em um ciclo de execução a função se chama mais de uma vez (na mesma recursão). O modo simples tende a ser mais eficiente, o tempo e espaço tendem a aumentar linearmente conforme a quantidade de recursões. No modo múltiplo o tempo de execução e o espaço de memória que a função precisa aumentam exponencialmente. Fontes:
|
Exemplo de recursividade simples |
---|
O fatorial de um número natural n, representado por n!, é o produto de todos os inteiros positivos menores ou iguais a n fonte. <stdio.h>
int main()
{
int in, out = 0;
printf("Entre com o valor de entrada da fatorial: ");
scanf("%d",&in);
out = fact(in);
printf("Resultado: %d\n",out);
return 0;
}
int fact(int n)
{
if (n <= 1)
{
printf("Finalmente n = 1, a função para se se chamar e retorna o resultado final.\n");
return 1;
}
else
{
printf("Nesta chamada n = %d, esta função se chamará recursivamente se for maior que 1.\n",n);
return n * fact(n-1);
}
}
No exemplo acima, se entrarmos com o valor 3 (para realizar o fatorial de 3), a seguinte sequencia será processada: fact(3) Como 3 não é menor ou igual a 1, realiza chamada de si mesma porém com n-1 que será 2 fact(2) Como 2 não é menor ou igual a 1, realiza chamada de si mesma com n-1 que será 1 fact(1) Finalmente 1 é menor ou igual a 1, então o retorno será 1, este retorno será entregue a chamada anterior, quando era fact(2) fact(2) retornará n * 1 (retorno recebido), resultado 2 fact(3) retornará n * 2 (2 foi o retorno que ela recebeu), resultando 6 Este modo é chamado de recursividade simples pois a função fact() é chamada apenas uma vez em uma recursão. |
Exemplo de recursividade múltipla |
---|
Na matemática os números de Fibonacci são definidos pela equação F(n) = F(n-1) + F(n-2) onde F(0) = 0 e F(1) = 1 fonte. #include <stdio.h>
int main()
{
int in, out = 0, i;
printf("Entre com o sequencial que deseja saber o fibonacci: ");
scanf("%d",&in);
out = fib(in);
printf("Resultado: %d\n",out);
printf("A sequencia fica assim:\n");
for (i=1;i<=in;i++) printf("%d ",fib(i));
printf("\n");
return 0;
}
int fib(int n)
{
if (n-1 <= 0)
return 0;
else if (n-1 == 1)
return 1;
else {
return fib(n-1) + fib(n-2);
}
}
|
Exercícios - avançado (série 6) |
---|
|
Acessando arquivos em C
Introdução |
---|
Arquivos em C são tratados como "streams" que literalmente significa córrego (riacho), em computação significa dados em fluxo. Utilizado para tratar quantidade desconhecida de informações (potencialmente infinita). Por esta característica arquivos são acessados através de ponteiros do tipo FILE * (um tipo de dados declarado na biblioteca <stdio.h>). Ainda na stdio, há três streams padrão (stdin, stdout e stderror) que não precisam ser declarados e estão prontos para uso. Funcões como scanf e getchar, por exemplo, na prática estão obtendo dados do teclado (stdin) e funções como printf e putchar estão escrevendo da tela (stdout). Eventuais falhas serão também enviadas para a tela (stderr). A quantidade de streams que um programa mantém aberto pode ser limitado pelo sistema operacional. A biblioteca stdio.h suporta dois tipos de arquivos: binários ou texto. Os arquivos tipo texto são compostos por caracteres humanamente compreensíveis. Já os arquivos binários são codificados. Arquivos binários podem armazenar mais informações em menor espaço e são normalmente mais fáceis de serem lidos por programas tornando-os também mais performáticos. |
Operações de abertura e fechamento de arquivos |
---|
A primeira ação será a de abertura de uma arquivo. É realizado com a função fopen que deve receber como parâmetros o nome do arquivo a ser aberto e o modo de abertura que especifica se será para leitura ou escrita. A diretiva restrict que aparece em ambos os argumentos não é muito relevante para o momento, basicamente está dizendo que estes espaços de memória não podem ser compartilhados (C99). FILE *fopen(const char * restrict filename, const char * restrict mode)
FILE * arquivo;
arquivo = fopen("IFSC.txt", "r");
Onde "r" significa somente leitura
arquivo = fopen("c:\\temp\\temp.txt", "w");
Onde "w" significa que o arquivo está sendo aberto para escrita Observe também o uso de "\\" e não apenas "\". Isto de deve pois algo como "C:\temp\temp.txt" teria então um "\t" que significa TAB. O uso de "\\" previne esta má interpretação. Uma outra alternativa é escrever "C:/temp/temp.txt", ou seja, com as barras invertidas. O operação fopen deve retornar um endereço válido se for bem sucedida, se falhar retornará NULL. Razões para falhas estão listadas aqui.
O caractere '+' representa o modo atualização (update mode). Para arquivos binários devem ter a letra "b" associada ("rb", "wb", "ab", "r+b"/"rb+", "w+b"/"wb+", "a+b"/"ab+"). No sistema operacional Linux a especificação de abertura de arquivo como binário não trará mudança pois é utilizado apenas um caractere para representar nova linha (\n). Porém, para manter portabilidade com o Windows que utiliza dois caracteres de nova linha '\r\n' é importante utilizar o modo 'b' para arquivos binários.
Permite o programa fechar um arquivo que não está mais sendo utilizado. Deve receber como parâmetro o ponteiro (FILE *) para o arquivo. Se fechar com sucesso retornará zero, caso ocorre erro retornará EOF (uma macro definida na stdio.h. int fclose(FILE *stream);
fclose(arquivo);
|
Operações de escrita de arquivos |
---|
Escreve uma saída em um stream apontado. A string apontada no segundo argumento se parece as utilizadas na função "printf". int fprintf(FILE * restrict stream, const char * restrict format, ...);
int fputc(int c, FILE *stream); //Escreve um caracter
int fputs(const char * restrict s, FILE restrict *stream); //Escreve uma string
#include <stdio.h>
int main(void)
{
FILE *p_arq;
int i;
if ((p_arq=fopen("IFSC.txt", "w")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return 0;
} else {
printf("Arquivo aberto com sucesso. Iniciando escrita no arquivo...\n");
}
for (i = 0; i<10;i++) {
/* A funcao fprintf devolve o número de bytes gravados ou EOF se houve erro na gravação */
if((fprintf(p_arq,"Linha %d\n",i))==EOF) {
printf("Erro ao escrever no arquivo!\n");
return -1;
}
}
printf("Fim da escrita, observe o arquivo 'IFSC.txt' criado na mesma pasta deste executável!\n");
fclose(p_arq);
return 0;
}
Note que se o arquivo IFSC.txt não existir, ele será criado. |
Operações de leitura de arquivos |
---|
Lê informações de um stream apontado em um formato definido. int fscanf(FILE *stream, const char *format, ...);
int fgetc(FILE *stream); //Lê um caracter e avança o cursor
char *fgets(char *str, int n, FILE *stream); //Lê um conjunto de caracteres limitado a n de tamanho, nova linha ou fim do arquivo
#include <stdio.h>
int main()
{
FILE *p_arq;
int i,j;
char buff[100];
if ((p_arq=fopen("IFSC.txt", "r")) == NULL) {
printf("Problemas na abertura do arquivo, o arquivo existe?\n");
return 0;
} else {
printf("Arquivo aberto com sucesso. Inicio da leitura do arquivo...\n");
}
while(1) {
if((fscanf(p_arq,"%s %d",buff,&j))==EOF) {
printf("Fim de leitura\n");
break;
}
printf("%s %d\n",buff,j);
}
fclose(p_arq);
return 0;
}
Note que o fscanf se comporta de forma similar ao scanf. A função retorna o caractere EOF (end-of-file) quando não existe mais dados a serem lidos. |
Adicionando uma linha no final de um arquivo |
---|
#include <stdio.h>
#include <time.h>
int main(void)
{
time_t ltime;
FILE *fp;
if ((fp=fopen("leituras.log", "a")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return;
}
time(<ime);
if ((fputs(ctime(<ime), fp)) != EOF ) {
fclose(fp);
} else {
printf("Erro na escrita do arquivo!\n");
}
}
Tipicamente, quando se abre uma arquivo para leitura/escrita, o "cursor" de acesso fica na posição 0 (início do arquivo). Se o arquivo for aberto em modo append ('a'), o "cursor" é posicionado no final. A cada acesso (leitura ou escrita), este cursor é incrementado conforme o número de dados lidos ou escritos. Execute este código algumas vezes e vá observando o que ocorre com o arquivo 'leituras.log'.
#include <stdio.h>
#include <time.h>
#include <string.h>
int main(void)
{
time_t ltime;
FILE *fp;
struct tm *info;
char buffer[100],b2[100];
if ((fp=fopen("tst.log", "a+")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return;
}
time(<ime);
info = localtime( <ime );
strftime(buffer,sizeof(buffer),"%x-%I:%M%p", info);
printf("Formatted date & time : |%s|\n", buffer );
strcat(buffer," Joao\n");
if ((fputs(buffer, fp)) == EOF ) {
printf("Erro na escrita do arquivo!\n");
}
fseek(fp,0,SEEK_SET);
while(1) {
if((fscanf(fp,"%s %s",buffer,b2))==EOF) {
printf("Fim de leitura\n");
break;
}
printf("Marcação lida: %s %s\n",buffer,b2);
}
fclose(fp);
return 0;
}
|
Alterando o cursor com fseek |
---|
Observe este exemplo que escreve em um arquivo e depois lê o conteúdo escrito. Neste caso é utilizado fseek para voltar o cursor para o início do arquivo. #include <stdio.h>
int main(int argc, char *argv[])
{
FILE *p_arq;
int i,j;
char buff[100];
if ((p_arq=fopen("IFSC.txt", "w+")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return 0;
} else {
printf("Aberto com sucesso. Iniciando escrita no arquivo...\n");
}
for (i = 0; i<10;i++) {
if((fprintf(p_arq,"LINHA %d\n",i))==EOF) {
printf("Erro ao escrever no arquivo!\n");
return -1;
}
}
printf("Escrito! 'IFSC.txt' criado na pasta deste executável!\n");
/*Muda a posição do cursor para o início do arquivo*/
fseek(p_arq,0,SEEK_SET);
while(1) {
if((fscanf(p_arq,"%s %d",buff,&j))==EOF) {
printf("Fim de leitura\n");
break;
}
printf("%s %d\n",buff,j);
}
fclose(p_arq);
return 0;
}
|
Tratando erros de abertura de arquivo |
---|
Quando não é possível abrir um arquivo a função retorna NULL, porém para se saber o real motivo da falha é necessário consultar o "errno" que fica setado na biblioteca <errno.h>. Há diversos códigos de erro para váriadas funções, no caso da fopen, consulte aqui os erros possíveis. #include <stdio.h>
#include <errno.h>
int main()
{
FILE *fp;
printf("Para simular o erro certifique-se de não haver um arquivo 'testeErro.txt' na pasta\n");
if ((fp = fopen("testeErro.txt","r")) == NULL) {
if (errno == ENOENT)
printf("Código de erro %d, arquivo não existe!\n",errno);
else
printf("Erro não previsto (código: %d)!\n",errno);
return 0;
}
fclose(fp);
return 0;
}
|
Exercícios - avançado (série 7) |
---|
|
Referências
Referências bibliográficas |
---|
|
Ferramentas úteis |
---|
|
Projeto final
O aluno deve propor ao professor um projeto de sua preferência que respeite os requisitos mínimos. Sendo aceito deverá desenvolver o projeto e apresentá-lo.
Requisitos mínimos
- Utilizar funções (ao menos três além do main, sendo que devem receber argumentos e possuir retorno);
- Apresentar menu utilizando switch case e conter laço infinito;
- Utilizar Structs ou Unions;
- Utilizar bibliotecas (além da stdio.h);
- Utilizar diretivas de pré-compilação;
- Utilizar comentários;
- Utilizar Ponteiros;
- Aceitar argumento de entrada no programa;
- Realizar acesso a arquivo, lendo e escrevendo informações (busque requisitos que requerem persistência de dados);
- Gravar informações de operações em log;
Modelo
- Trabalho individual
Metodologia
- Apresentar a proposta de projeto ao professor
- Opcional: Documentar o escopo do projeto utilizando descrição narrativa (descrição simples)
- Cenário
- Problema
- Dados de entrada e saída
- O planejamento do cronograma não será cobrado porém cabe ao aluno se organizar quanto ao tempo para entrega no prazo
- Desenvolver o projeto
- Apresentar individualmente ao professor
- Serão realizados testes diversos, arguido sobre o funcionamento, possibilidades de alterações, etc
Algumas ideias de projetos
- Sugestão geral: veja em outras disciplinas que processos podem ser automatizados e proponha um projeto que realiza esta tarefa como de cálculos diversos de eletrônica, de rádio transmissão, etc.
- Implementar o jogo Pedra, papel ou tesoura. Neste jogo dois ou mais jogadores em diferentes computadores devem rodar um aplicativo que fará a leitura de um arquivo compartilhado. O algoritmo deve tratar as etapas do jogo (Setup do aplicativo, entrada na sala, escolha da figura e apresentação do resultado)
- Implementar o jogo da velha escrevendo em arquivo. Neste jogo dois jogadores em diferentes computadores devem rodar um aplicativo que fará a leitura de um arquivo compartilhado. O algoritmo deve tratar as etapas do jogo (Setup do aplicativo, entrada na sala, seleção das casas e apresentação do resultado)
- Implementar controle de empréstimo de objetos. Neste software o usuário poderá digitar nomes de objetos que emprestou, a pessoa a quem emprestou e automaticamente o software guarda a data. Deve haver uma opção para gerar relatório dos itens emprestados e opção para marcar a devolução (podendo manter o registro em histórico ou apagando o registro).
- Implementar software gerador de lista de compras. Neste software o usuário poderá digitar itens de supermercado com nome e quantidade. O software escreve num arquivo que poderá depois ser impresso. O software também pode ter função de numa segunda execução já trazer a antiga listagem digitada e permitir que o usuário apenas selecione novas quantidades ou inclua novos itens.
- Implementar software para realização de cálculos de eletrônica. Neste software um menu apresenta várias opções de cálculo como de potencia através de tensão e corrente, como obtenção do valor de um resistor, como solução de equivalência de paralelo de vários resistores e outras. Num arquivo texto pode ser armazenado um histórico de operações realizadas.
- Implementação de software para apostas na mega sena. Neste software são dadas sugestões de números para apostas de acordo com o número do sorteio da mega sena. Com este histórico armazenado é possível então entrar com um número de sorteio e digitar quais foram os números verdadeiramente sorteados na loteria federal checagem os acertos.
Projetos dos alunos
- Amanda: Sistema de agendamento de manutenção preventiva de equipamentos de refrigeração
- Ameliza: Caixa eletrônico 24h
- André: Software de cálculos de circuitos eletrônicos
- Fábio: Jogo da velha com opção continuar de onde parou
- Felipe: Sistema de registro de atendimento de monitores
- Gabriel Santos: ?
- Guilherme: Projeto multilinguas
- Jennifer: Sistem de controle de barbearia
- Luiza: Sistema financeiro pessoal
- Simone: ?
- Stefanie: Múltiplos cruzamentos com semáforos síncronos
Plano de aula
Cronograma | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Conceitos
Ver moodle da disciplina.