PRG29002 - Programação I - Eng.Telecom 2016-1 - MediaWiki do Campus São José
PRG29002 - Programação I - Eng.Telecom 2016-1
e-mail: cleber.amaral@ifsc.edu.br
1 Critérios e instrumentos de avaliação
3 Material de aula
3.2 Introdução aos Algoritmos e Pseudocódigo
3.3.2 Controle de fluxo
3.3.3 Funções
3.3.4 Variáveis e operadores
3.3.5 Tipos de dados compostos
3.3.6 Ponteiros
3.3.7 Mais sobre a função main()
3.3.8 A função exit
3.3.9 Diretivas de pré-compilação
3.3.10 Trabalhando com funções
3.3.11 Alocação dinâmica de memória
3.3.12 Acessando arquivos em C
3.3.13 Utilizando typedef
3.3.14 Recursividade
4.2 Referências adicionais
4.3 Ferramentas úteis
4.4 Sites úteis
4.5 Eventos da área de desenvolvimento
5.1 Horário de Atendimento Paralelo
5.2 Horário de Monitoria
5.3 Orientações para entrega dos trabalhos
5.4 Trabalhos entregues com atraso
6.4 Algumas ideias de projetos
7 Tópicos avançados
Conceito => Somatório(Nota)/QtNotas
Esta é uma previsão, eventuais mudanças serão comunicadas no decorrer das atividades
Nota[1]: Nota da Avaliação 1 (a definir data e formato)
Nota[2]: Nota da Avaliação 2 (a definir data, formato e necessidade desta segunda avaliação)
Nota[3]: Média das notas das Listas de exercícios
Nota[4]: Nota do Projeto final
Lista de exercícios 1: Entregar por e-mail ou manuscrito
Desafio 1 (projeto de cafeteira): Entregar por e-mail ou manuscrito a Narrativa, Fluxograma e Pseudocódigo
Lista de exercícios 2: Entregar por e-mail (seguir instruções) ou manuscrito
Gabarito dos pseudocodigos Q1 Q2 Q3 Q4 Q5
Lista de exercícios 3: Entregar por e-mail (seguir instruções) ou manuscrito
O estudante deve entregar na forma de fluxograma cada desafio que resolvemos na aula de 13/04 na forma de pseudocódigo, e devem ser entregues na forma de pseudocódigo os fluxogramas que fizemos em sala -
Os títulos dos algoritmos são citados na mídia 1.3 (link abaixo) mas os detalhes foram trabalhados em sala e fotografados pelos próprios alunos
Lista de exercícios 4: Entregar por e-mail (seguir instruções) ou manuscrito
Lista de exercícios 5: Entregar por e-mail (seguir instruções)
Prova 1: Algoritmos e lógica utilizando pseudocódigo e fluxogramas
Lista de exercícios 6: Entregar via moodle
Desafio 2 (jogo da velha): Entregar via moodle
Lista de exercícios 7: Entregar via moodle (atraso nos 6 primeiros dias contarão -1)
Apresentação das propostas de projeto final
Prova 2: Prática
Gabarito Prova A: Enunciados Prova A, q1A, q2A, q3A, q4A, q5A.
Gabarito Prova B: Enunciados Prova B, q1A, q2A, q3A, q4A, q5A,q6A.
04/07/2016 as 17:30
Prova Rec 1: Teórica (Lab. Redes 2)
19/07/2016 (horário de aula)
Prova Rec 2: Prática
26 e 27/07/2016
Avaliação 3: Apresentação do projeto
0.0: Ementa da disciplina
Introdução aos Algoritmos e Pseudocódigo
1.0: Introdução aos algoritmos
1.1: Algoritmos continuação
1.2: Algoritmos - repetição e subrotinas
1.3: Algoritmos - fixação
1.4 Algoritmos - Preparação para avaliação
1.4 Desafio 2
2.0: Introdução ao C
2.1: Introdução - continuação
A estrutura mais elementar de decisão? Saloni em Code.org
O que é uma estrutura de repetição? Chris Bosh em Code.org
O que é um laço de repetição? - Mark Zuckerberg Code.org
O que é uma estrutura de decisão? Bill Gates em Code.org
Como funciona a estrutura de repetição tipo contagem (for no C)? - Code.org
2.2: Condicionais no C
2.3: Estruturas de repetição no C
Sumário de operadores de controle de fluxo em ANSI C
O que é uma função? Khan Academy
O que é uma função - noção intuitiva - MeSalva!
2.4: Funções no C
2.5: Funçoes no C - continuação
Alguns cuidados com formatos de entrada do scanf
Captura de caractere: pode ser utilizar o especificador '%c', porém este especificador captura inclusive "\n" (o ENTER, ou nova linha). O efeito é que uma captura anterior de scanf pode ter deixado uma "sujeira". Para previnir isso utilize " %c" (observe o espaço antes do '%c'
Captura de strings incluindo espaços: o especificador '%s' não captura espaços em branco, isto pode ser muito útil se o desejado é exatamente separar uma string (frase) em palavras. Porém quando a string que se deseja capturar pode conter espaços e ainda assim deseja-se processar como uma string única é necessário utilizar outros especificadores. Um deles o "%[^\n]s" ou "%[^\n]" indica que deseja-se obter um conjunto de caracteres que pode ter qualquer valor exceto '\n'. Outra opção é um especificador como "%10[0-9a-zA-Z ]" que indica que deseja-se obter até 10 caracteres que enteja dentro das variações "0 a 9", "a a z", "A a Z" e " ".
Fonte: cppreference
Variáveis e memória computacional
O que é uma variável? - Khan Academy
Como funciona a memória de computadores? - Khan Academy
2.6 Vetores em C
Exercicios complementares - Vetores
2.7: Operadores e precedências
2.8: Exercicios Vetores (lista 7)
Se variáveis são declaradas dentro de uma função, então a visibilidade (ESCOPO) destas variáveis é LOCAL. Nenhuma outra função tem acesso a estas variáveis. Uma variável pode ser GLOBAL, ou seja, declarada FORA das funções. Neste caso a variável é VISTA por todas as funções.
NOTE que como a função media_nums() não retorna valor então declaramos seu tipo de retorno como void que significa aqui NADA ou VAZIO.
NOTE também que MESMO que a função retorne um valor, não é obrigatório colocá-la no lado direito do sinal de atribuição.
void media_nums(float num1, float num2)
media = (num1 + num2)/2;
float aux1, aux2; /* Variáveis LOCAIS */
media_nums(aux1, aux2);
printf ("\nmedia dos 2 numeros é %f\n", media);
Uma função pode ser chamada dentro de qualquer expressão. Por exemplo, para o caso em que a função media_nums() retorna um valor, ela poderia ser usada como:
NOTE agora que a função media_nums() retorna um float, podemos utilizar este retorno no contexto em que a função foi chamada que é o que ocorre dentro do printf abaixo
NOTE também que MESMO que a função retorne um valor, não é obrigatório que este valor este sendo recebido por alguma variável, uma função que possui retorno e este retorno não está sendo recebido se comportará como um procedimento (sem retorno).
OBS.: Variáveis LOCAIS e GLOBAIS podem ter o mesmo nome. A variável LOCAL terá preferência no uso.
float media_nums(float num1, float num2)
media_local = (num1 + num2)/2;
float media, aux1, aux2;
media = media_nums(aux1, aux2);
printf ("\nmedia dos 2 numeros multiplicada por 10 é %f\n", 10*media_nums(aux1, aux2));
Computadores executam instruções, portanto, não são realmente capazes de criar números aleatoriamente. Porém 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:
printf("Numero gerado: %d\n", rand() % 10); //Gera números que variam de 0 a 9
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 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
Dicas para resolução dos exercícios da lista 7
Dicas para resolução dos exercícios da lista
Uma forma de resolver é criando dois vetores de inteiros (ex.: l1[26] e l2[26]). Então, recebidas as palavras 1 e 2, inicia-se a decomposição da palavra 1. Em um laço, para cada 'A' ou 'a' encontrado incrementamos uma vez o l1[0], para cada 'B' ou 'b' incrementamos l1[1] e assim por diante. O mesmo será feito com a palavra 2 em outro laço. Na prática, se a letra que está sendo analisada for maiúscula (pela tabela ASCII de 65 a 90, o índice de "lx" que deve ser incrementado é letra-65, e se form minúscula é letra-97). O resultado será dois vetores contendo a quantidade de vezes que a letra se repetiu. Um terceiro laço compara l1[0] com l2[0] e assim por diante. Havendo qualquer divergência, NÃO é um anagrama. Para simplificar solicite ao usuário para digitar palavras sem acentos (algoritmos com tratamento de palavras acentuadas terão bônus).
Para resolver basta criar um laço para checar cada caractere, se o usuário solicitou converter para maiúsculas, por exemplo, e a letra que está sendo indicada no laço está entre 97 e 122, então basta subtrair de 32 o valor da letra que esta será convertida para maiúscula. Se a letra já está entre 65 e 90, não precisa alterar. Para criar as funções faça antes a prototipagem, facilitará a codificação.
A solução deste é muito parecida com o primeiro exercício, porém este pede que o programa seja capaz de tratar maiúsculas e minúsculas juntas. Então, basta fazer uma "normalização" primeiro, que é o processo do exercício 2, por exemplo, converta tudo para maiúscula primeiro e depois faça a contagem, isso garante que maiúsculas e minúsculas sejam tratadas da mesma forma.
Para este exercício é sugerido criar um vetor char nomes[n][10], sendo 10 o tamanho máximo de um nome e 'n' a quantidade de alunos que é variável. Crie então um outro vetor de float notas [n][5] onde 5 são as notas. O programa então começa solicitando a quantidade de alunos gravando em 'n'. Depois recebe nomes e notas. Pode-se utilizar uma variável de apoio para computar o índice do aluno com a maior nota que será no final do processo impresso. Durante o calculo das médias já pode-se imprimir os alunos que ficaram em recuperação.
Para resolver este, utilize as instruções presentes nesta página da wiki. Atente-se que para se obter um número de 1 a 60, um jeito fácil é pegar o resto da divisão por 60 do resultado de rand(), porém o resultado desta matemática será um número de 0 a 59, então no final basta somar com 1 para se obter entre 1 e 60. Para garantir que os 6 números sejam distintos pode-se criar um laço while que dentro está a geração do randômico e teste se este número já está presente no vetor de int numeros[6], se sim, basta não incrementar a variável de apoio e retornar ao início do laço para que um novo número seja gerado.
Obtendo o código de um caractere UTF-8
Para obter o código UTF-8 de um caracter especial (retorno 0xC3) é necessário executar um segundo scanf, conforme exemplo:
printf("Digite um caracter especial ou não:\n");
//Primeiro scanf
//Se for um caracter especial
if(0xC3 == c)
//Segundo scanf para obter segunda codificação deste char UTF8
printf("Digitado um caracter especial: 0x%x\n",c);
printf("Digitado um caracter convencional: 0x%x\n",c);
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"
Alterar o programa para que ele calcule e imprima a média de cada aluno.
//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);
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.
struct TRoupeiro{
struct TRoupeiro roupeiro;
char descricao_generica[sizeof(int)+sizeof(float)+20];
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, descrição=%s, cor=%s, volume=%d, peso=%f\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
vaso_decorativo.nome,
vaso_decorativo.descricao_generica,
vaso_decorativo.roupeiro.cor,
vaso_decorativo.roupeiro.volume,
vaso_decorativo.roupeiro.peso
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.
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 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:
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));
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
"&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):
Endereço de i:     &i = 0xbfadc2b8
Conteúdo de p:      p = 0xbfadc2b8
Endereço de p:     &p = 0xbfadc2bc
Tamanho do ponteiro   = 4 bytes
Tamanho do lont int   = 4 bytes
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.
Mais sobre a função main()
Início e fim do programa
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).
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.
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 07 06 2016 O programa deverá imprimir:
$ 07 de junho de 2016
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
A função 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).
Uma diretiva de pré-compilação é um código processado pelo pré-compilador que "prepara", então, o código que será efetivamente compilado. Nesta execução pode haver definições, mudanças de comportamento do compilador e mesmo blocos lógicos que decidirão o que será ou não compilado. As diretivas de pré-compilação são úteis para configurações diversas que o compilador precisa conhecer para poder gerra o código objeto como também são úteis para criar "macros" diversas que podem tornar processos mais simples ou tornar o código mais inteligível.
O pré-processador busca o conteúdo de um outro arquivo e traz este código para que o compilador possa realizar a compilação e posterior link.
No exemplo acima a função "printf" está definida na biblioteca "stdio", portanto, é necessário incluir este código para que o compilador passe a "conhecer" o que é a função printf e possa então chamar este código quando esta função é evocada. Quando a biblioteca é incluída com sinais de maior e menor (< e >) envolvando o nome do arquivo significa que o pré-compilador deve buscar esta biblioteca nos diretórios listados como endereços de biblioteca do compilador. Portanto precisa estar definido nos arquivos de configuração do compilador. O diretório onde se encontra a libc (biblioteca padrão do C) já vem por padrão listada nestes endereços. Quando a biblioteca é incuída entre aspas duplas (" ") envolvendo o nome do arquivo significa que o pré-compilador deve procurar em um endereço específico. Se o caminho não está todo definido será o endereço relativo, havendo apenas o nome do arquivo da biblioteca será buscado no diretório em que se encontra o código c que está sendo compilado.
A diretiva "define" cria uma macro que é uma substituição de um valor por um código ou lista de códigos de programa. Há algumas vantagens de se utilizar macros:
O programa fica mais fácil de ler e entender pois pode-se substituir códigos complexos por textos auto-explicativos (exemplo: #define PI 3.14159)
Fica mais fácil de modificar já que o identificador pode se repetir várias vezes no código, bastando modificar o #define para modificar todo o código.
Evita inconsistências e erros de digitação possíveis como por exemplo definindo PI como 3.14159 e utilizando esta definição garante-se que este valor será utilizado em todo o programa igualmente, evita o engano de usar em algum lugar 3.1416, por exemplo.
#define qtnotas 5
float notas[qtnotas], soma=0;
for(i = 0;i < qtnotas;i++)
printf("Digite a nota = %d\n",i+1);
printf("A média é: %.1f\n",soma/qtnotas);
Observe acima como o uso da macro qtnotas traz benefícios como os listados anteriormente.
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:
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:
Construir uma função que imprime a Tabela usando ponteiros. A função deve receber como parâmetro um ponteiro para o início da tabela e o tamanho da tabela.
Implementar a função str_cat que concatena duas strings usando ponteiros.
Ordenar valores de um vetor de inteiros passando por referencia o ponteiro para esse vetor.
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.
Exercício de preparação para a prova 2
Elabore um programa em C que declara duas estruturas (TRetangulo e TTriangulo) tendo uma instancia para cada na forma global (retangulo e triangulo). Cada estrutura deve conter dois campos (base e altura, ambos unsigned int). Na função main atribua valores quaisquer para as bases e alturas das formas geométricas. Por fim imprima em tela os valores de base, altura e área obtendo dos campos da struct sabendo que a área do retângulo é base x altura e do triangulo é base x altura dividido por 2.
Crie uma funcao calculaAreaRetangulo e outra calculaAreaTriangulo para executar o calculo das áreas sem receber parâmetros e retornando a área calculada na forma unsigned int. A função deve ser chamada de dentro das chamadas de impressão dos valores já feitas no main. Declare os protótipos de ambas as funções no início do arquivo ".c".
Mova as declarações de instancia (retangulo e triangulo) originalmente criadas como globais para dentro do main e modifique as funções de cálculo para que passem a receber o ponteiro da struct como parâmetro.
Declare as variáveis retangulo e triangulo como vetores de structs (com quantidade definida por MAX_R e MAX_T respectivamente). Altere o código para que os valores de base e altura sejam obtidos do usuário (crie loops para receber os valores e calcular as áreas utilizando as funções já existentes)
As estruturas de dados em C são normalmente de tamanho fixo. Mesmo num vetor de tamanho variável, seu tamanho apesar de ser determinado em tempo de execução, seu tamanho se mantém fixo até que seja destruído. Nos exemplos que trabalhamos a maioria tinha número de registros fixo, esta situação é bastante restringente. Em um programa de registros de notas de alunos, por exemplo, se determinado um número de registros pequeno pode-se deparar com a necessidade de aumentar este vetor, havendo necessidade de recompilação. Se por outro lado for alocado um grande espaço de memória, pode-se estar desperdiçando bastante espaço caso fiquem obsoletos. Para resolver este problema há funções de alocação dinâmica de memória, que permitem que seja alocada e desalocada memória conforme crescimento ou redução da necessidade de espaço. As funções mais conhecidas são (http://en.wikipedia.org/wiki/C_dynamic_memory_allocation):
calloc: aloca n bytes de memória e zera.
Alocação dinâmica de números inteiros (exercício puramente didático):
Observe que há duas chamadas de alocação utilizando "malloc", onde são alocados a quantidade de bytes que uma variável "int" ocupa (normalmente 4 bytes). Conforme consta na documentação lincada acima, o malloc retorna um void*, que é um ponteiro genérico. Para que este ponteiro seja tratado como um ponteiro de int (que é nossa necessidade neste código) é realizado um typedef de (int*). Após as alocações são escritos e lidos valores destes espaços de memória através do apontamento de "px" e "py". Por fim, é chamada a função "free" que recebe como argumento um ponteiro e libera o bloco de memória apontado por e este ponteiro.
A função free deve ser chamada sempre que a memória alocada (por malloc, calloc ou realloc) não for mais necessária. A não desalocação correta de memória pode causar "vazamentos de memória" (ou memory leak). Uma chamada de free em um espaço de memória já desalocado poderá também causar falha, trata-se de um comportamento não previsível, possivelmente uma falha de segmentação.
Após desalocar o bloco de memória apontado por um ponteiro, setar este ponteiro para NULL é uma boa prática. Esta ação evita que acidentalmente será tentado utilizar ou desalocar um espaço de memória já livre o que poderia causar falhas graves. Esta prática de "anular" o ponteiro transforma este em um "ponteiro nulo" (ou NULL pointer), sendo um tipo especial que não aponta para lugar nenhum (nulo não é um endereço de memória válido), isso significa que uma desalocação em um ponteiro nulo não deverá ter nenhum efeito.
As funções malloc e calloc são muito parecidas, o efeito prático é que o calloc além de alocar a memória também "zera" este espaço. O ato de "zerar" a memória faz do calloc uma função menos performática, isso ocorre por duas razões: o simples processo de escrever "zeros" é dispendioso e também o ato de escrever algo na memória obriga o sistema operacional a "tocar" na memória e realizar possíveis ações de swap de outros processos. No caso do malloc estas ações só ocorrerão quando houver real necessidade de escrever naquele determinado espaço de memória.
Alocando memória com calloc
Diferente da função malloc, calloc além de inicializar os espaços de memória ainda atribui o valor 0 (zero) para cada um deles.
Observe o calloc sendo realizado no início do loop, garantindo que as variáveis iniciem em zero
int *valores, *aux;
int qtd, i, j, vlr;
printf("\nEntre com a quantidade de números: ");
if(qtd == 0) exit(0);
//Aloca um vetor com a quantidade recebida em qtd
valores = (int *) calloc(qtd, sizeof (int));
printf("Rodada %d:\n", j);
//Imprime o endereco e valor (zero) para a quantidade de números informada
for(i = 1; i <= qtd; i++){
printf("Situação inicial da memória: %p\t%d\n", aux, *aux);
aux = valores; //Ponteiro para o primeiro espaço de memória
printf("Digite o número %d ->: ", i);
scanf("%d", &vlr);
*aux = vlr;
aux = valores; //Retorna à posição inicial no mapa de memória
printf("Situação final da memória  : %p\t%d\n", aux, *aux);
free(valores);
malloc versus calloc
Observe o malloc sendo realizado no início do loop, não há garantia de qual valor se encontra naquele espaço de memória.
valores = (int *) malloc(qtd * sizeof (int));
Alocando uma estrutura e testando o memory overflow
#define QT_INSTANCIAS 100000
teste = (struct TTeste *) malloc (QT_INSTANCIAS*sizeof(struct TTeste));
//Boa pratica: se o retorno do malloc é NULL houve erro de alocação
if (teste!=NULL) {
printf("%li MBytes de memória alocados com sucesso!\n",QT_INSTANCIAS*sizeof(struct TTeste)/(1024*1024));
printf("Digite qualquer tecla para prosseguir: ");
printf("Erro ao tentar alocar %li MBytes de memória!\n",QT_INSTANCIAS*sizeof(struct TTeste)/(1024*1024));
struct TTeste *aux;
aux = teste;
for (i=0;i<QT_INSTANCIAS;i++,aux++)
aux->x=i+1;
aux->y=aux->x*2;
sprintf(aux->st,"%li",i*1000000);
printf("%li: teste.x=%li, teste.y=%li e teste.st=%s\n",i,aux->x,aux->y,aux->st);
Observe agora o uso da função "realloc". Como argumentos além do tamanho em bytes (idem ao malloc), também é necessário setar o ponteiro que será redimensionado. Este tamanho poderá ser maior ou menor ao tamanho anteriormente alocado, fazendo com que seja liberada memória ou alocado um bloco ainda maior. O retorno da função é o próprio ponteiro do bloco realocado. Se o retorno for nulo, a operação de alocação não foi bem sucedida.
Refazer o exemplo "Alocação dinâmica de números inteiros" tentando desalocar duas vezes o mesmo ponteiro e também tentando desalocar o ponteiro após se tornar um ponteiro nulo.
Estude atentamente o exemplo "Alocando memória com calloc", por que foi criado um ponteiro aux? Crie linhas de código com printf para exibir o endereço de memória que aux tem em cada iteração do loop, o que se conclui desta observação? aux não está sendo liberado por free, o que isso significa? O que aconteceria se fosse dados free de aux também? aux e valores não estão sendo setados para NULL, que risco isso traz?
Altere o exemplo "Alocando uma estrutura e testando o memory overflow" na quantidade de instancias e/ou tamanho do vetor teste.st para obervar até quanto de memória é possível alocar e quando não há memória disponível suficiente para esta operação.
Rode o exemplo "Alocando uma estrutura e testando o memory overflow" com uma quantidade grande de memória sendo alocada. Enquanto são impressas as linhas mostrando que os espaços de memória estão sendo preenchidos abra um outro terminal e rode o programa chamado "free" que mostra a ocupação de memória ram. Rode o free algumas vezes seguidas para observar o aumento de uso de memória e finalmente após finalizar o programa veja a memória ser desalocada.
Rode o exemplo "Alocando uma estrutura e testando o memory overflow" com uma quantidade grande de memória sendo alocada. Enquanto são impressas as linhas mostrando que os espaços de memória estão sendo preenchidos abra um outro
O que aconteceria se fosse tentado acessar um espaço de memória apontado por um ponteiro nulo ou de memória já desalocada? Utilize um dos exemplos anteriores para testar ambos os casos.
Acessando arquivos em C
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
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).
FILE *fopen(const char * restrict filename, const char * restrict mode)
arquivo = fopen("IFSC.txt", "r");
Onde "r" significa somente leitura
Outro exemplo de chamada seria
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.
Modos de acesso a arquivos (arquivos tipo texto)
"r": Abre para leitura
"w": Abre para escrita (o arquivo não necessariamente precisa existir, o conteúdo possivelmente será sobrescrito caso não tratado, ver modo "a")
"a": Abre para escrita adicionando dados no final do arquivo (o arquivo não necessariamente precisa existir)
"r+": Abre para leitura e escrita
"w+": Abre para leitura e escrita (trunca se o arquivo existir, apagando o conteúdo anterior)
"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.
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".
Outras operações de escrita
int fputc(int c, FILE *stream); //Escreve um caracter
int fputs(const char * restrict s, FILE restrict *stream); //Escreve uma string
Exemplo: Abrindo e escrevendo um arquivo
printf("Arquivo aberto com sucesso. Iniciando escrita no arquivo...\n");
/* 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");
printf("Fim da escrita, observe o arquivo 'IFSC.txt' criado na mesma pasta deste executável!\n");
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
Exemplo: Abrindo e lendo um arquivo
printf("Problemas na abertura do arquivo, o arquivo existe?\n");
printf("Arquivo aberto com sucesso. Inicio da leitura do arquivo...\n");
if((fscanf(p_arq,"%s %d",buff,&j))==EOF) {
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
if ((fp=fopen("leituras.log", "a")) == NULL) {
if ((fputs(ctime(&ltime), fp)) != EOF ) {
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'.
char buffer[100],b2[100];
if ((fp=fopen("tst.log", "a+")) == NULL) {
info = localtime( &ltime );
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 ) {
if((fscanf(fp,"%s %s",buffer,b2))==EOF) {
printf("Marcação lida: %s %s\n",buffer,b2);
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.
if ((p_arq=fopen("IFSC.txt", "w+")) == NULL) {
printf("Aberto com sucesso. Iniciando escrita no arquivo...\n");
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);
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+"?
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.
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) {
printf("Código de erro %d, arquivo não existe!\n",errno);
printf("Erro não previsto (código: %d)!\n",errno);
Outras funções com arquivos
int getc(FILE *stream), int fgetc(FILE *stream) int putc(char ch, FILE *s), int fputc(char ch, FILE *s) These are like getchar, putchar. getc is defined as preprocessor MACRO in stdio.h. fgetc is a C library function. Both achieve the same result!!
fflush(FILE *stream) -- flushes a stream. fclose(FILE *stream) -- closes a stream. We can access predefined streams with fprintf etc.
fprintf(stderr,"Cannot Compute!!\n"); fscanf(stdin,"%s",string);
1) 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".
2) 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".
3) 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+".
4) 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+".
5) 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+".
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:
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
typedef long quantidade; //Plataformas 16 bits
Utilizando typedef em estruturas
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.
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.
printf("Entre com o valor de entrada da fatorial: ");
out = fact(in);
printf("Resultado: %d\n",out);
printf("Finalmente n = 1, a função para se se chamar e retorna o resultado final.\n");
printf("Nesta chamada n = %d, esta função se chamará recursivamente se for maior que 1.\n",n);
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.
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?
Exemplo de recursividade múltipla
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 fonte.
int in, out = 0, i;
printf("Entre com o sequencial que deseja saber o fibonacci: ");
out = fib(in);
printf("A sequencia fica assim:\n");
for (i=1;i<=in;i++) printf("%d ",fib(i));
if (n-1 <= 0)
else if (n-1 == 1)
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').
Códigos de formatação do printf
Ordem de precedência de operadores C
Tabela ASCII (ISO 8859-1)
Lista de Caracteres codificação UTF-8
C Reference Card (ANSI) - Guia rápido
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.
cplusplus.com: Traz tutoriais, artigos e descrições de funções C e C++
codechef.com: Permite a edição, compilação e testes online de códigos em várias linguagens inclusive ANSI C
codecademy.com: Tem cursos gratuitos de programação, bastante didáticos em inclusive em português. Porém não tem curso de C, uma alternativa interessante para quem quiser aprender uma outra linguagem que tem boa aceitação inclusive para desenvolvimento de sistemas embarcados é o Python
kaggle.com: Site tem publicado centenas de algoritmos em diversas linguagens para resolver os mais variados problemas. Tem também competições de algoritmos
Interface PythonTutor para C: Permite compilar programas em C e acompanhar passo a passo o que ocorre com as variáveis
Eventos da área de desenvolvimento
Maio de 2016 - Florianópolis TDC2016
Outubro de 2016 - Rio de Janeiro QConRio2016
Ver agenda oficial do professor Cleber Jorge Amaral
Monitoria 2016-1
Orientações para entrega dos trabalhos
Para listas a serem entregues via moodle:
Nome do arquivo: "seu nome completo/ demais colegas do grupo" - "lista de exercícios 1", "desafio 1", etc.
Formato do arquivo em anexo: PDF (cada lista num único arquivo feito em editor de texto, exportado para PDF).
Para listas a serem entregues por email (para cleber.amaral@ifsc.edu.br), período válido até a meia noite do prazo:
Assunto do e-mail: "PRG29002 - xxx" onde xxx é "lista de exercicios 1", "desafio 1", etc.
Nome do arquivo anexo: "seu nome completo/ demais colegas do grupo" - "lista de exercícios 1", "desafio 1", etc.
Regra válida a partir de 14/04 (isento apenas trabalhos "lista 1")
Trabalhos entregues com atraso
Para os trabalhos não entregues no prazo (não justificados) temos a penalidade de 1 ponto por dia de atraso
Excepcionalmente para as lista1 e lista7 com até 6 dias de atraso terá desconto de 1 ponto apenas, seguindo a regra acima para entregas deste trabalho após este prazo
Desafios não tem extensão de prazo, não adianta enviar se o prazo se esgotou
Para o desafio1 o sistema de desconto por dia de atraso foi utilizado, este ficou como exceção
Realizar acesso a arquivo, lendo e escrevendo informações
Utilizar funções (ao menos duas sendo ao menos uma com argumentos)
Apresentar menu utilizando switch case e conter laço infinito
Aceitar argumento de entrada no programa
Utilizar alguma biblioteca (além da stdio.h)
Utilizar ao menos 3 dos seguintes recursos
Utilizar diretivas de pré-compilação
Utilizar Structs ou Unions
Utilizar alocação dinâmica de memória
Documentar o escopo do projeto utilizando descrição narrativa (descrição simples)
C++ para sistemas embarcados - mitos e verdades, comparação C e C++ (parte 1/2)
C++ para sistemas embarcados - mitos e verdades, comparação C e C++ (parte 2/2)
memory layout in c..data segment,bss, code segment, stack, heap segment
Calculo de tempo recursividade simples
Na recursividade simples o tempo aumenta linearmente como mostra o código abaixo
uint64_t tempoFact10 = 0;
uint64_t tempoFact20 = 0;
uint64_t tempoFact30 = 0;
in = 10;
tempoFact10 += timeElapsed / 10;
in = 20;
tempoFact20 += timeElapsed / 10;
in = 30;
tempoFact30 += timeElapsed / 10;
printf("\nMédia de tempo Fact(10): %li\n",tempoFact10);
printf("\nMédia de tempo Fact(20): %li\n",tempoFact20);
printf("\nMédia de tempo Fact(30): %li\n",tempoFact30);
Calculo de tempo recursividade multipla
uint64_t tempoFib10 = 0;
uint64_t tempoFib20 = 0;
uint64_t tempoFib30 = 0;
uint64_t tempoFib40 = 0;
tempoFib10 += timeElapsed / 10;
tempoFib20 += timeElapsed / 10;
tempoFib30 += timeElapsed / 10;
in = 40;
tempoFib40 += timeElapsed / 10;
printf("\nMédia de tempo Fib(10): %li\n",tempoFib10);
printf("\nMédia de tempo Fib(20): %li\n",tempoFib20);
printf("\nMédia de tempo Fib(30): %li\n",tempoFib30);
printf("\nMédia de tempo Fib(40): %li\n",tempoFib40);
Média de tempo Fib(10): 1113
Média de tempo Fib(20): 113658
Média de tempo Fib(30): 9817635
Média de tempo Fib(40): 427706133
Ou seja, há um aumento exponencial no tempo
Disponível em "https://wiki.sj.ifsc.edu.br/index.php?title=PRG29002_-_Programação_I_-_Eng.Telecom_2016-1&oldid=113185"