Caros alunos, a disciplina está terminada. Espero que ela tenha sido um espaço de aprendizado verdadeiro e que tenha contribuído para a formação de vocês. Parabéns e obrigado pelo trabalho e pelo esforço!
A turma virtual desta disciplina no SIGAA foi fechada assim que as notas foram consolidadas. Por essa razão, o restante da nossa comunicação se dá por meio desta página. Você também pode me mandar um e-mail, caso deseje.
Aqui estão as notas da disciplina. Se você ainda não pegou algumas das suas provas, por favor passe no meu gabinete para pegá-las, exceto no caso da AF, que a universidade requer que fique com o professor – mas você pode dar uma olhada nela mesmo assim.
Agora que a disciplina acabou, aqui vão algumas sugestões de tópicos interessantes que complementam o assunto que estudamos:
Nós estudamos 3 ferramentas poderosas para a organização de dados: ponteiros, vetores e estruturas (ou "registros"). Essas ferramentas permitem a organização de dados de múltiplas formas, as quais muito influenciam os programas que escrevemos. O estudo das maneiras clássicas de se organizar dados em algoritmos e programas de computador é feito na disciplina Estruturas de Dados (ou, no curso de Matemática, "Estruturas de Informação"), que é a continuação natural da nossa disciplina. Se você gostou de Fundamentos de Programação, imagino que Estruturas de Dados também lhe agradará. Este livro é uma ótima referência nesse assunto em português (ele usa pseudocódigo, para não poluir as ideias dos algoritmos com a sintaxe complicada das linguagens de programação reais).
Na nossa disciplina, o foco foi conseguir escrever programas que realizassem as tarefas desejadas. Uma vez, porém, que nós já sabemos fazer isso, novas preocupações aparecem, como por exemplo a velocidade e o uso de memória dos programas que escrevemos. O primeiro passo para escrever programas que façam uso eficiente dos recursos de computação disponíveis é utilizar bons algoritmos e estruturas de dados; esse assunto se vê na disciplina "Estruturas de Dados", mencionada acima. Na prática, porém, a habilidade de programação vai muito além dos algoritmos que se utiliza. O meu conselho básico, nesse aspecto, é o seguinte: saiba, tenha em mente e tente diminuir o custo da execução de cada instrução do seu código. Para exemplificar a questão, considere este código:
int i, n; ... // n recebe um valor positivo double v[n*n]; for (i=0; i < n*n; ++i) v[i] = 0;
Esse não é um código "otimizado", porque o produto "n*n" é calculado toda vez que a condição do laço é testada. Muito mais eficiente que isso é realizar o cálculo apenas uma vez guardar o resultado numa variável:
int i, n; ... // n recebe um valor positivo int tam = n*n; double v[tam]; for (i=0; i < tam; ++i) v[i] = 0; // mais eficiente ainda seria usar ponteiros aqui
A "otimização" de código é uma tarefa que pode ser muito ampla, e é importante conhecer bem a linguagem de programação utilizada. Aqui estão conselhos de um autor de renome no assunto.
Você deve ter percebido uma diferença grande entre os programas que escrevemos e aqueles que costumamos utilizar na prática: nós escrevemos programas que executam com interface de texto, enquanto os programas que costumamos utilizar têm interface gráfica, com botões, menus, etc. Acredite, a essência dos dois é a mesma, a diferença é "apenas" a interface, a comunicação entre o programa e o usuário. No caso da linguagem C, eu sugiro você usar GTK: há um exemplo na Wikipédia e aqui está um tutorial.
Esta página está em contínua construção. Por favor, entre em contato comigo caso você precise de alguma informação adicional, ou caso acredite ter encontrado um erro nesta página. Obrigado!
2014-04-24 (quinta-feira): AP1.
2014-05-28 (quarta-feira), 10h: 2a chamada da AP1.
2014-06-03 (terça-feira): AP2.
2014-06-12 (quinta-feira): AP3.
2014-06-23 (segunda-feira): AF.
Segunda chamada: conforme disposto no manual do aluno, o aluno tem direito a fazer uma segunda chamada caso falte a alguma prova. Entretanto, somente serão realizadas segunda-chamadas para alunos que atenderem a todas as seguintes exigências:
Solicitar a segunda chamada por escrito na secretaria do Departamento de Computação em até 3 dias úteis após a realização da 1a chamada, anexando à solicitação um atestado médico ou comprovação inequívoca da impossibilidade de comparecer à prova.
Solicitações que não atendam às exigências acima não serão aceitas. Em particular, não ter estudado o suficiente para a primeira chamada não é motivo aceitável para requerer uma segunda-chamada. Além disso, os solicitantes devem estar cientes de que as provas de segunda-chamada são intencionalmente mais difíceis, dado que o aluno tem, em princípio, mais tempo para estudar para elas.
Programando em C - Volume I - Fundamentos.
Ulysses de Oliveira.
Editora Ciência Moderna.
2008.
ISBN: 9788573936599.
Eu não conhecia o livro, mas o folheei e me surpreendi:
esse é o primeiro livro em português que eu abri
e que me lembrou o palavreado técnico do padrão C99.
Não li, mas desconfio que seja um material de muito boa qualidade,
principalmente do ponto de vista da literatura disponível em português.
C: A Reference Manual, Fifth Edition.
Samuel P. Harbison, Guy L. Steele.
Editora Prentice Hall.
2002.
ISBN-13: 978-0-13-089592-9.
Nunca li, mas me parece ser um livro de qualidade, e descreve tanto C89 quanto C99
(não conheço um livro sobre C11).
Entretanto, parece ser uma boa referência, e não um texto para iniciantes.
No Linux, um compilador C já deve estar instalado. Se não estiver, experimente tentar instalar o compilador "gcc" pelo instalador de pacotes. Eu recomendo a distribuição Ubuntu do Linux, pela simplicidade de uso.
Eu infelizmente não possuo um computador com Windows
para fazer pequenos experimentos de instalação de compilador.
Acredito que o
MinGW
e o
Cygwin
sejam duas opções bastante completas;
não sei se é fácil e pouco trabalhoso instalá-los.
Talvez uma saída bastante simples seja usar o
TCC:
simplesmente baixe o arquivo tcc-0.9.26-win32-bin.zip
(ou a versão com 64
no lugar de 32
,
se o seu computador for de 64 bits),
descompacte o pacote, salve o seu código-fonte
(digamos, main.c
) na pasta tcc
e então simplesmente execute tcc main.c
na interface de linha de comando
(para chegar à pasta via linha de comando,
você deverá ter que executar o programa cmd.exe
e chegar à pasta tcc
: cd tcc
;
veja
isto).
Além de um compilador, você vai precisar utilizar um editor de texto para escrever o código-fonte dos seus programas, então:
No Windows, o Notepad++ me parece ser uma boa escolha, embora eu nunca o tenha utilizado.
Pessoalmente, eu uso o gVim, que está disponível tanto em Linux quanto Windows, tem muitas capacidades e é muito bem documentado, mas a maioria das pessoas o considera difícil de usar de início. Ele é rivalizado pelo Emacs, que dizem ser muito bom, mas eu nunca usei -- exceto pelo psicoterapeuta do Emacs, que é muito divertido.
Alguns exercícios passados para sala ou para casa podem não estar listados abaixo:
No livro: capítulo 1 de [LP].
main
;
execução de um programa em C como execução de um conjunto de funções;
leitura e escrita de variáveis inteiras em pseudo-código e C;
atribuição de variáveis inteiras em pseudo-código.
if-else
;
argumentos e retorno de funções.
(Faltou void
.)
No livro: capítulo 2 e início do capítulo 3 de [LP].
Exercícios importantes (devem ser feitos mesmo que no papel; o computador não é o principal!):
main
para a obtenção do módulo do número
digitado pelo usuário.
O programa deve imprimir mensagens de comunicação com o usuário
("digite um inteiro: ", "o módulo é ...").
Aula 4 (2014-02-20):
estrutura de seleção com múltiplas escolhas, em pseudocódigo e em C (switch
);
estruturas de repetição com teste no final e no início,
em pseudocódigo e em C (do-while
, while
);
atribuição a uma variável que está envolvida na expressão que define o valor a ser atribuído.
Exercícios importantes (devem ser feitos mesmo que no papel; o computador não é o principal!):
n
dos números que vão ser digitados.
Em seguida, o programa deve proceder à leitura dos n
números,
e, para cada um deles, deve ser impressa uma mensagem ("digite um número"),
de forma a guiar a interação do usuário com o programa.
Ao final, como antes, a soma dos números lidos deve ser impressa na tela.
Aula 5 (2014-02-25):
uso de repetição em programas para contar e somar;
declaração, leitura, escrita e aritmética de números racionais em C (double
);
conversões implícitas entre tipos numéricos em expressões aritméticas;
conversão explícita de tipos numéricos em C (casts).
Observações:
Como dito em sala, em C,
quando um número racional é convertido para inteiro (como de double
para int
),
a parte fracionária é descartada [C99, 6.3.1.4, 1].
Em math.h
,
há também funções para piso
e teto.
Na aula, foi inicialmente dito que a atribuição de um número racional a um inteiro geraria um erro de compilação,
e, depois, que esse poderia não ser o caso, mas que um aviso (warning) poderia ser gerado na compilação.
A verdade é que o padrão C99 não exige a geração de tais avisos [C99, anexo I, 1],
mas a conversão implícita de um tipo mais abrangente para um menos abrangente
(como de double
para int
) está na lista dos avisos mais comuns
[C99, anexo I, 2, item 3].
Exercícios (o primeiro é importante, o segundo é interessante e o terceiro é difícil):
Em sala, foi escrito um programa que calcula a média
de um conjunto de números racionais digitados pelo usuário;
nesse programa, o usuário é repetidamente solicitado a responder
se mais algum número será ou não digitado.
Escreva uma variação desse programa na qual se começa lendo do usuário a quantidade n
dos números que vão ser digitados.
Em seguida, o programa deve proceder à leitura dos n
números,
e, para cada um deles, deve ser impressa uma mensagem ("digite um número"),
de forma a guiar a interação do usuário com o programa.
Ao final, como antes, a média racional dos números lidos deve ser impressa na tela.
Escreva um programa que lê um inteiro positivo "n" do usuário e então imprime o valor de fib(n) na tela, sendo fib(n) o n-ésimo número da sequência de Fibonacci -- 1, 1, 2, 3, 5, 8, 13, 21, 34, 55... -- que é definida por:
Escreva um programa que calcula, de forma exata ou aproximada, a raiz quadrada de um número racional positivo digitado pelo usuário. O seu programa não deve chamar qualquer função já pronta que calcule a raiz quadrada de um número, mas sim usar a estratégia a seguir. Primeiramente descubra um limite inferior LI e um limite superior LS para a raiz quadrada do número digitado X (por exemplo, LI=0 e LS=X). Se LI ou LS forem a raiz quadrada de X (como se testa isso?), então o valor desejado já foi encontrado. Depois disso, verifique se M = (LI+LS)/2 ao quadrado é menor ou maior que X; se for menor, a raiz quadrada de X está entre M e LS; se for maior, a raiz está entre LI e M; se for igual, a raiz é M. Caso a raiz não seja M, você pode repetir o processo, agora com um intervalo com a metade do tamanho do original. Caso a raiz quadrada exata não seja encontrada em IT iterações, o seu programa pode imprimir, como valor aproximado, o valor médio do último intervalo encontrado; o valor IT deve ser fornecido pelo usuário no início da execução, juntamente com X.
Aula 6 (2014-02-27):
repetição com variável de controle usando o laço "enquanto" (while
);
estrutura de repetição com variável de controle, em pseudocódigo e em C (laço "para", for
).
Exercícios importantes -- devem ser feitos mesmo que no papel, pois o maior aprendizado da disciplina é saber escrever instruções que, se executadas, corretamente realizam uma tarefa; o uso do computador complementa o aprendizado, mas é secundário.
Escreva um programa em C que lê um inteiro não negativo n
do usuário
e então imprime na tela o fatorial de n
.
Escreva duas versões do programa,
uma que utilize o laço while
(ou o do-while
)
e outra que utilize o laço for
.
Em ambos os casos, certifique-se de que o programa lê n
novamente caso o usuário digite um número negativo.
Escreva uma modificação do programa da questão anterior na qual o cálculo do fatorial é realizado por uma função. (Recapitule de aulas anteriores a sintaxe para a definição de funções que recebem argumentos e retornam valores.) Dessa vez, você não precisa escrever duas versões: escolha o laço (isto é, a estrutura de repetição) que lhe pareça mais adequada.
Escreva um programa em C que lê uma temperatura em graus Fahrenheit
e a converte para graus Celsius,
usando a fórmula
.
O tipo numérico para o armazenamento de temperaturas deve ser double
.
Escreva uma versão mais geral do programa da questão anterior, da seguinte maneira. O cálculo da conversão de Fahrenheit para Celsius deve ser encapsulado numa função. Você deve também escrever uma função que faz a conversão oposta, de Celsius para Fahrenheit. Finalmente, o programa deve pedir ao usuário que informe não apenas a temperatura, mas também a conversão que deve ser feita (se de Fahrenheit para Celsius, ou se o inverso).
([LP], cap. 3, exercício proposto 2.) Utilizando as funções escritas no exercício anterior, escreva um programa que imprime na tela uma tabela como
0 C -- 32 F |
1 C -- 33.8 F |
2 C -- 35.6 F |
... |
100 C -- 212 F |
([LP], cap. 3, exercício proposto 4.) Escreva um programa que lê um conjunto de votos e ao final apresenta a contagem e a estatística dos votos. Os votos possíveis são números de 1 a 5: um número "i" de 1 a 3 significa um voto para o candidato "i"; um número 4 significa um voto em branco; um número 5 significa um voto nulo. O programa deve repetidamente ler votos ("Digite o próximo voto: "), até que um número menor que 1 ou maior que 5 seja digitado; tal número indicará o fim da votação. Ao fim da votação, deve ser impressa a quantidade de votos que cada candidato recebeu, bem como as quantidades de votos brancos e de votos nulos. Também devem ser impressas as porcentagens de cada tipo de voto ("33.5% para o candidato 1", etc).
2014-03-04: sem aula (feriado nacional: carnaval).
Aula 7 (2014-03-06):
introdução a bases numéricas
(sistema unário,
abreviações,
sistema posicional);
conversão de e para a base decimal;
conceitos sobre armazenamento de dados no computador
(bit, byte, palavra de memória, modelo de memória de C);
o tipo unsigned int
de C e sua representação.
Aula 8 (2014-03-11):
a constante UINT_MAX
(de limits.h
)
e transbordo do tipo unsigned int
em C;
representação de inteiros com sinal
(representação deslocada por constante; sinal e magnitude; complemento de um; complemento de dois);
conversão de e para a representação por complemento de dois.
Observação:
em printf
e scanf
,
a conversão para o tipo unsigned int
é %u
.
Converta os seguintes números para a representação por complemento de dois utilizando 8 bits (ou informe se a quantidade de bits é insuficiente, quando for o caso): 10, 15, 16, 27, 53, 101, 127, 128, 130.
Análogo ao exercício anterior, mas utilizando o inverso aditivo de cada número listado (por exemplo, -10 no lugar de 10).
Os seguintes são números inteiros representados na notação de complemento de dois; escreva-os na notação decimal: 10010011, 00010001, 10101010, 10001000.
Aula 9 (2014-03-13):
as constantes INT_MIN
e INT_MAX
(de limits.h
);
transbordo de inteiros com sinal (como int
) em C
(o "comportamento indefinido" do padrão e os comportamentos comuns na prática);
notação posicional com vírgula (isto é, para a representação de partes subunitárias);
notação científica, em diferentes bases;
notação científica normalizada;
(início da) representação, em bits, de números binários na notação científica normalizada.
Observações:
Faltou explicar a conversão da notação posicional decimal com vírgula para a base binária.
Falta complementar a representação de números binários em notação científica normalizada, explicando a representação do expoente que convenientemente permite a representação do zero.
Os exercícios da aula anterior mencionam vários números, positivos e negativos. Escreva-os na notação científica normalizada na base decimal.
Análogo ao exercício anterior, mas agora utilizando a base binária.
Aula 10 (2014-03-18): prática de programação em laboratório (exercícios da aula de 2014-02-27).
Exercícios:Escreva um programa em C que lê do usuário os coeficientes de uma equação do 2o grau e então imprime na tela as raízes reais da equação (quando existirem).
Escreva em C uma função que recebe como argumentos os coeficientes de uma equação do segundo grau e que retorna o número de raízes reais dessa equação.
Aula 11 (2014-03-20): representação de números racionais segundo o padrão IEEE 754: representação do expoente por excesso-de-k; números normalizados; zero; infinitos; NaN's; números denormalizados (também chamados "subnormais" no padrão).
Fontes a consultar: este texto da Wikipédia é uma boa explicação sobre o padrão IEEE 754. Este artigo é um trabalho famoso sobre a representação de números racionais em memória; uma versão mais recentemente editada dele é esta aqui; esse texto é, porém, longo e matematicamente denso; não é necessário lê-lo para entender o conteúdo das aulas.
Observação: o padrão IEEE 754 determina 3 formatos binários básicos para a representação de números racionais. Cada formato especifica o número de bits utilizados para armazenar o expoente e o significando, de acordo com a tabela abaixo (a qual, para os propósitos da nossa disciplina, não precisa ser memorizada):
Comprimento | Sinal | Expoente | Significando |
32 | 1 | 8 | 23 |
64 | 1 | 11 | 52 |
128 | 1 | 15 | 112 |
Exercícios:
As seguintes são representações de valores segundo o padrão IEEE 754, no formato de 32 bits explicado na tabela acima; descubra o valor correspondente a cada representação:
1 0000 0000 000 0000 0000 0000 0000 0000 |
1 0000 0000 000 0000 0000 0000 0000 0001 |
1 1111 1111 000 0000 0000 0000 0000 0001 |
1 1111 1111 000 0000 0000 0000 0000 0000 |
1 1111 1110 111 1111 1111 1111 1111 1111 |
0 1000 0111 100 1000 0000 0000 0000 0000 |
0 1000 0111 010 0000 0000 1000 0000 0000 |
1 1000 0011 110 0010 0000 0000 0000 0000 |
1 0111 1111 000 0000 0000 0000 0000 0000 |
2014-03-25: sem aula (Feriado Estadual: Data Magna do Ceará).
2014-03-27: sem aula, em função da obrigatoriedade da participação do professor no "Seminário de Ambientação aos Novos Servidores" da UFC, conforme ofício circular número 06/PROGEP-UFC.
Aula 12 (2014-04-01):
apresentação e justificação de um algoritmo de conversão de números reais
não-inteiros para a notação posicional em base binária;
o tipo char
e a sua representação.
Fontes a consultar: este artigo da Wikipédia também fala da conversão de números não-inteiros para a base binária (seções 7 e 8).
Exercícios:
Escolha você mesmo alguns números não inteiros escritos em base decimal e os converta para a base binária. Algumas sugestões: 9,3125; 2,1; 4,025; etc.
O raciocínio apresentado em sala para a conversão de números fracionários decimais para a base binária funciona, na verdade, para qualquer base maior que um! Generalize então o algoritmo e a demonstração de que ele funciona. Em seguida, converta números fracionários decimais para outras bases, como a base 3. Verificar a correção da resposta não é um processo elaborado, como vimos em sala, mas é mais rápido conferir na internet.
Aula 13 (2014-04-03):
argumentos e retorno de funções: presença e ausência (void
);
funções recursivas.
Exercícios:
Considere o código abaixo:
#include <stdio.h> int par (int n); // para a função "impar" saber o que é "par" antes de a definição desta última ser lida. int impar (int n) // Supõe n >= 0 { switch(n) { case 0: return 0; case 1: return 1; default: return par(n-1); } } int par (int n) // Supõe n >= 0 { switch(n) { case 0: return 1; case 1: return 0; default: return impar(n-1); } } int main (void) { int n; do { printf("Digite um número inteiro: "); scanf("%d", &n); if (n >= 0) { printf( " par(%d): %d\n", n, par(n) ); printf( "impar(%d): %d\n", n, impar(n) ); } else { printf("Saindo...\n"); } } while (n >= 0); return 0; }
Agora:
Simule a execução do código acima supondo que o usuário digita 2, 5 e -3.
O que faz o código acima? Por que ele funciona?
O que acontece numa chamada como par(-3)
?
Escreva uma função void digitos_binarios (int n)
que imprime na tela os dígitos de um número n
quando escrito em base binária.
A função deve supor que n ≥ 0
.
A estratégia para imprimir os dígitos é recursiva e é ilustrada pelo seguinte exemplo:
Como 29 = 14*2 + 1, então, para imprimir os dígitos binários de 29, nós podemos imprimir primeiramente os dígitos de 14, e, em seguida, imprimir o dígito menos significativo de 29, que é 1.
Escreva uma função void digitos_na_base (int n, int b)
que generaliza a função da questão anterior das seguintes maneiras:
O argumento b
é a base na qual o número deve ser impresso.
A função deve supor que b ≥ 2
.
Se n
for negativo, a função imprime o número corretamente,
primeiramente imprimindo um "-" e depois imprimindo os dígitos de -n
.
Escreva uma função recursiva int fib (int n)
que calcule o n-ésimo número da sequência de Fibonacci,
sabendo que os dois primeiros números da sequência valem 1,
e que, para qualquer n ≥ 3, o n-ésimo número da sequência
é a soma do (n-1)-ésimo e do (n-2)-ésimo números da sequência.
Aula 14 (2014-04-08): declarações ("protótipos") de funções e seu uso em funções mutuamente recursivas; prática de programação em laboratório (exercícios de aulas anteriores).
Aula 15 (2014-04-10):
o operador sizeof
;
os comandos break
, continue
e goto
;
a sintaxe de identificadores em C;
blocos em C e nomes "escondidos" por declarações dentro de blocos;
a convenção de verdadeiro (≠ 0) e falso (= 0) em C.
Observações:
Identificadores não podem ser iguais às palavras-chave da linguagem C, pois estas últimas são reservadas para o significado especial que elas têm na linguagem. A lista de palavras-chave da linguagem C está aqui, por exemplo.
Em C, o segundo operando dos operadores lógicos apenas é avaliado caso a avaliação do primeiro operando não seja suficiente para se deduzir o resultado da operação como um todo. Assim, por exemplo, este código
int a,b; ... if (b == 0 || a/b > 5) { ... } // teste 1 if (b != 0 && a/b > 5) { ... } // teste 2é correto do ponto de vista da avaliação da expressão
a/b
:
Teste 1: caso b
seja zero,
o teste resultará em verdadeiro e a expressão a/b
não chega a ser avaliada.
Se, por outro lado, b
não for zero,
então a expressão b == 0
será falsa e a expressão
a/b > 5
será avaliada,
sem risco de divisão por zero.
Teste 2: se b
for zero,
a expressão b != 0
será falsa
e portanto a condição completa do teste será falsa,
sem que a expressão a/b > 5
seja avaliada.
Se, por outro lado, b != 0
for verdadeira,
a expressão da direita será avaliada,
novamente sem risco de divisão por zero.
Exercícios:
Recapitule cada novidade apresentada na aula de hoje, e as tenha em mente ao escrever programas em C daqui para a frente; elas podem lhe ajudar a escrever programas mais claros e/ou sucintos de vez em quando, e podem ser essenciais na compreensão de programas escritos por outras pessoas.
Recapitule o problema das torres de Hanói, da primeira aula da disciplina: (1) há 3 pinos: A, B e C; (2) há "n" discos no pino A, todos de tamanhos diferentes e empilhados do menor (no topo) ao maior (na base); (3) o objetivo é mover todos os discos para o pino C; (4) pode-se mover, de um pino a outro, um disco por vez, sempre aquele disco que estiver no topo da pilha de um pino, e que passará então ao topo da pilha do outro pino, mas o movimento só é possível se não levar um disco a estar sobre outro menor que ele.
Na primeira aula da disciplina, você foi convidado a solucionar o problema das torres de Hanói com 3 e 4 discos, o que deve ter lhe dado a ideia da solução do problema para qualquer quantidade "n" de discos. Escreva então um programa em C que lê do usuário um número inteiro positivo "n" e então escreve na tela uma sequência de passos que levam à solução do problema das torres de Hanói com "n" discos.
Aula 16 (2014-04-15):
o operador ternário de C (A ? B : C
);
vetores: conceito, declaração e acesso a elementos;
uso de vetores para reduzir repetição de código;
inicialização de vetores: completa e parcial, de trecho inicial e aleatória;
ponteiros: declaração,
dereferência (acesso ao elemento apontado: operador *
) e
referência (obtenção de ponteiro: operador &
).
Observações:
Numa expressão A ? B : C
,
a expressão B
é avaliada se e somente se A
for verdadeira;
analogamente, C
é avaliada sse A
for falsa.
Assim, por exemplo,
o trecho
int a,b,c; // ... int d = (b != 0) ? a/b : c;
está correto, pois a divisão somente será efetuada se b
não valer zero.
Exercícios:
Escreva um programa que:
Lê as notas de 5 alunos na AP1 de uma disciplina.
Em seguida (isto é, após todas as leituras acima), lê as notas dos mesmos 5 alunos na AP2.
Em seguida, imprime as médias de cada aluno; os alunos devem estar numerados de 1 a 5.
Escreva uma variação do programa anterior na qual, após a leitura das notas, o programa, ao invés de imprimir as médias, repetidamente pede ao usuário que digite o número do aluno, e então imprime a média do aluno digitado, até que o usuário digite um número que não seja de 1 a 5, quando então o programa deve terminar. Por exemplo:
Digite a AP1 do aluno 1: 8 Digite a AP1 do aluno 2: 9 Digite a AP1 do aluno 3: 8.7 Digite a AP1 do aluno 4: 8.2 Digite a AP1 do aluno 5: 7 Digite a AP2 do aluno 1: 9 Digite a AP2 do aluno 2: 9.9 Digite a AP2 do aluno 3: 10 Digite a AP2 do aluno 4: 7.8 Digite a AP2 do aluno 5: 8.1 Digite o número de um aluno: 4 A média do aluno 4 é 8 Digite o número de um aluno: 1 A média do aluno 1 é 8.5 Digite o número de um aluno: 5 A média do aluno 5 é 7.55 Digite o número de um aluno: 1 A média do aluno 1 é 8.5 Digite o número de um aluno: -8 Saindo...
Escreva um programa que lê 6 números do usuário e os armazena num vetor "v". Em seguida, o programa deve imprimir o conteúdo do vetor, elemento-a-elemento. Em seguida, o programa deve repetidamente fazer o seguinte:
Ler dois números "i" e "j" de 0 a 5.
Trocar os valores de v[i] e v[j].
Imprimir o novo conteúdo do vetor.
Digite v[0]: 5 Digite v[1]: 7 Digite v[2]: 1 Digite v[3]: 2 Digite v[4]: 3 Digite v[5]: -7 v: [ 5 7 1 2 3 -7 ] Digite i: 0 Digite j: 4 v: [ 3 7 1 2 5 -7 ] Digite i: 5 Digite j: 5 v: [ 3 7 1 2 5 -7 ] Digite i: 5 Digite j: 2 v: [ 3 7 -7 2 5 1 ] Digite i: 5 Digite j: -2 Saindo...
Considere o programa abaixo:
#include <stdio.h> void f (int a, int b) { int c = a; a = b; b = c; } int main (void) { int x = 5, y = 8; f(x,y); printf("X: %d\nY: %d\n", x, y); return 0; }
O que ele faz? O que era pretendido? Como é possível corrigir f
?
2014-04-17: sem aula (recesso escolar).
Aula 17 (2014-04-22): aplicação simples de ponteiros: função para trocar os valores de 2 variáveis; prática de programação em laboratório (exercícios anteriores, preferivelmente aqueles envolvendo vetores).
Exercícios:
Escreva uma programa que (1) lê do usuário 6 números, (2) armazena esses números num vetor, (3) em seguida, desloca os elementos do vetor à frente e circularmente (isto é, o primeiro elemento passa a ser armazenado na 2a posição, o segundo elemento na 3a posição, …, o penúltimo elemento na última posição, e o último elemento na primeira posição), e (4) imprime os elementos do vetor na tela. Observação: o efeito exemplificado abaixo pode ser conseguido sem que se efetivamente altere as posições dos números no vetor; entretanto, o seu programa tem que realmente fazer os deslocamentos. Exemplo de execução:
Digite v[0]: 8 Digite v[1]: -7 Digite v[2]: 5 Digite v[3]: 9 Digite v[4]: 9 Digite v[5]: 0 Após o deslocamento, temos: v[0]: 0 v[1]: 8 v[2]: -7 v[3]: 5 v[4]: 9 v[5]: 9
2014-04-24: Prova 1.
Aula 18 (2014-04-29):
incremento e decremento de ponteiros;
adição e subtração entre ponteiros e inteiros;
comparação e subtração entre ponteiros;
os tipos ptrdiff_t
e size_t
de stddef.h
;
percurso de vetores utilizando ponteiros.
Correção:
em printf
, a conversão para o tipo
ptrdiff_t
é %td
(e não %t
),
e a conversão para o tipo size_t
é
%zd
(e não %z
);
veja, por exemplo, esta tabela.
Exercícios:
Ponteiros permitem que se percorra, numa certa função, um vetor declarado noutra função, como no programa abaixo:
#include <stdio.h> void imprimir_vetor (double *inicio, int tam) { double *p = inicio; double *fim = p + tam; for (; p < fim; ++p) printf("vetor[%td]: %g\n", p - inicio, *p); // %td => ptrdiff_t } int main (void) { double v[5]; int i; for (i=0; i < 5; ++i) { printf("Digite v[%d]: ", i); scanf("%lg", &v[i]); } imprimir_vetor(&v[0], 5); return 0; } // main
Simule manualmente uma execução do programa acima.
Depois, tomando como base o exemplo acima, escreva uma função
double maior_elemento (double *inicio, int tam)
que descubra e retorne o maior elemento de um dado vetor.
Em seguida, escreva um programa completo no qual essa função seja utilizada.
Escreva uma função que preencha um dado vetor com números lidos a partir do usuário. Como no exercício anterior, a função deve receber como argumentos (1) um ponteiro para o primeiro elemento do vetor e (2) o tamanho do vetor.
Escreva uma função que inverta a ordem dos elementos de um dado vetor.
Assim, por exemplo, se o vetor fornecido tiver como elementos
[ 5 7 -8 0 9 9 ]
,
então a função deve alterar o vetor de forma que os elementos deste passem a ser
[ 9 9 0 -8 7 5 ]
.
Escreva uma função
void separar (double *p, int tam, double s)
que reorganiza os tam
elementos de um vetor
(iniciado em *p
) da seguinte maneira:
os elementos menores ou iguais a s
devem ficar
à esquerda dos elementos maiores que s
.
Assim, por exemplo,
se o vetor for [ 9 -1 8 -7 15 7 6 5 ]
e s == 5
,
então uma reorganização possível é
[ 5 -1 -7 8 15 7 6 9 ]
.
Escreva um programa para se jogar Torres de Hanói com 4 discos. Utilize vetores para armazenar a informação de quais discos estão em quais pinos, e em qual ordem. Os movimentos dos discos devem ser informados pelo usuário. A cada movimento fornecido pelo usuário, o programa deve executar o movimento e então imprimir na tela a configuração de discos resultante da jogada. O programa não deve permitir movimentos inválidos, como por exemplo um que leve um disco a estar sobre outro menor.
2014-05-01: sem aula (feriado nacional: dia do trabalho).
Aula 19 (2014-05-06):
definição do operador [ ]
para ponteiros;
conversão implícita de vetores para ponteiros em C;
uso de ponteiros para a manipulação de vetores declarados noutras funções;
percurso de vetores via indexação ([ ]
) e via apontamento direto dos elementos;
alocação dinâmica de memória por meio das funções
calloc
e malloc
,
e desalocação pela função free
,
todas de stdlib.h
.
Exercícios:
Escreva uma função void move_max (double *i, int t, int p)
que recebe como argumentos um ponteiro i
apontando para o primeiro elemento de um vetor de t
números,
e que então rearranja os elementos do vetor de forma
que o maior elemento do vetor passe a estar na “posição” (índice) p
,
caso não já esteja lá.
Por exemplo:
Se o vetor for [ 3 6 -1 9 0 ]
e p == 1
,
então um rearranjo válido é [ 3 9 -1 6 0 ]
.
Se o vetor for [ 7 3 9 6 8 -1 9 0 ]
e p == 4
,
então um rearranjo válido é [ 7 3 8 6 9 -1 9 0 ]
.
Fazendo uso da função escrita no exercício anterior,
escreva uma função void ordenar (double *i, int t)
que ordena os elementos de um dado vetor em ordem crescente.
A ideia a ser utilizada na função é a seguinte:
Colocar o maior elemento do vetor na última posição do vetor (usar a função do exercício anterior).
Repetir o passo anterior,
“fingindo que o vetor possui apenas os t-1
primeiros elementos”.
Repetir o passo anterior,
“fingindo que o vetor possui apenas os t-2
primeiros elementos”.
Etc, até restar apenas um elemento a ser ordenado, que então está trivialmente ordenado.
Exemplo:
[ 7 3 9 6 8 -1 9 0 ] --> [ 7 3 0 6 8 -1 9 9 ] --> [ 7 3 0 6 8 -1 9 9 ] --> [ 7 3 0 6 -1 8 9 9 ] --> [ -1 3 0 6 7 8 9 9 ] --> [ -1 3 0 6 7 8 9 9 ] --> [ -1 0 3 6 7 8 9 9 ] --> [ -1 0 3 6 7 8 9 9 ] .
Escreva uma função de ordenação como a do exercício anterior, exceto que a função de ordenação deve conter todo o código necessário nela mesma, não fazendo, portanto, chamada a nenhuma outra função.
Escreva uma função recursiva que ordene um vetor de números por meio da estratégia das questões anteriores.
Escreva uma função
void unir (double *i1, int t1, double *i2, int t2, double *i3)
que recebe como entrada dois vetores já ordenados (em ordem crescente),
e que então escreve os elementos desses dois vetores num terceiro vetor,
de forma que este último também fique ordenado.
A sua função não deve simplesmente copiar os elementos dos vetores de origem no vetor de destino e então chamar uma função de ordenação para este último; ao invés disso, a função deve fazer uso inteligente do fato de que os dois primeiros vetores já estão ordenados.
Exemplo: se os dois primeiros vetores forem
[ -8 -3 -1 2 7 ]
e
[ -1 0 3 6 7 8 9 9 ]
,
então o vetor resultante deve ser
[ -8 -3 -1 -1 0 2 3 6 7 7 8 9 9 ]
.
Observação: a função não precisa receber como argumento o tamanho do terceiro vetor; ela supõe que o vetor é grande o suficiente.
Aula 20 (2014-05-08):
declarações das funções calloc
,
malloc
e free
,
conforme encontrado em stdlib.h
;
ponteiros para void
;
lista das operações válidas sobre ponteiros;
dimensionamento implícito de vetores inicializados;
armazenamento de matrizes por meio de vetores em C, linha-a-linha,
e correspondência entre os índices na matriz e no vetor;
exemplo de programa
que armazena e manipula uma matriz por meio de um vetor.
Observações:
Tecnicamente, a constante NULL
, definida em stddef.h
,
é uma expressão constante inteira de valor zero,
ou então uma tal expressão convertida para void *
(como "(void*) 0"
)
[padrão C99, 7.17 e 6.3.2.3].
Algumas pessoas acham melhor comparar ponteiros com NULL
,
ao invés de com zero, para indicar mais claramente que se trata
de uma comparação com um valor especial para ponteiros.
A comparação com zero é, entretanto,
perfeitamente válida do ponto de vista sintático.
Um vetor declarado numa função certamente deixa de existir ao fim da chamada à função (assim como qualquer outra variável local), ao passo que um vetor alocado dinamicamente pertence ao programa até que seja explicitamente desalocado, e portanto pode continuar alocado mesmo depois do fim da chamada da função na qual ele foi alocado. Veja este exemplo.
Atenção: (1) uma expressão *p
, p
sendo um ponteiro,
somente deve ser avaliada num programa se p
não for um ponteiro nulo (isto é, se p != 0
).
Além disso, (2) se p
aponta para uma variável local,
então *p
só pode ser avaliada enquanto a variável local exista,
isto é, enquanto a função na qual ela foi declarada
não tiver terminado de executar.
Por fim, (3) se p
aponta para um elemento de um vetor alocado dinamicamente,
então *p
só pode ser avaliada enquanto o vetor ainda estiver alocado.
Exercícios:
Reescreva o laço que imprime a matriz no último programa escrito em sala; a versão reescrita deve ter dois laços aninhados: um externo, para percorrer as linhas, e um interno, para percorrer os elementos de cada linha. Tente fazer esse exercício antes de olhar o arquivo anexado na lista dos tópicos tratados na aula, pois o programa desse arquivo já contém a resposta desta questão.
Escreva um programa que lê do usuário "n" números, e que, após a leitura de todos eles, imprime na tela os quadrados dos números digitados. A quantidade "n" de números deve ser informada pelo usuário inicialmente. O programa deve armazenar os números num vetor, o qual deve ser alocado dinamicamente. Exemplo de execução:
Digite a quantidade de números: 3 Digite o número 1: 7 Digite o número 2: -8 Digite o número 3: 9.7 Quadrado do número 1: 49 Quadrado do número 2: 64 Quadrado do número 3: 94.09
Escreva um programa que lê do usuário os coeficientes de um sistema de equações lineares, um a um, e que então imprime os coeficientes de volta para o usuário, preferivelmente na forma retangular que costumamos utilizar para a impressão de tais sistemas. Utilize um único vetor para armazenar todos os coeficientes.
Escreva uma extensão do programa do primeiro exercício, na qual o usuário digita vários valores para "n", e na qual vários vetores são alocados, lidos, utilizados e desalocados, um depois do outro, até que o usuário digite um valor não-positivo para "n", quando então o programa deve parar.
Escreva uma função que compara dois vetores "v" e "w" de "n" números inteiros, e que retorna 1 se os vetores são iguais e 0 em caso contrário.
Que faz esta função? (Tente descobrir antes de executá-la num computador.)
int f (int *i, int t) // Supõe t > 0. { int *p = v; int m = *p; int i = 1; while (i++ < t) { if ( m < *(++p) ) m = *p; } return m; }
Escreva uma função que recebe uma matriz inteira "M" de "m" linhas e "n" colunas – representada na forma de um vetor de m*n números, linha-a-linha, da mesma maneira como no exercício sobre sistemas lineares – e que adiciona 0 aos elementos da linha 0, 1 aos elementos da linha 1, …, "m-1" aos elementos da linha "m-1". Depois, escreva um programa que lê os elementos de uma matriz, chama a função acima e depois imprime a matriz resultante.
Escreva uma função int checar_ordenacao (int *v, int n)
que receba um ponteiro para um vetor de "n" inteiros
e retorne 0 se o vetor está ordenado em ordem crescente.
Caso o vetor não esteja ordenado,
deve ser retornado o menor índice "i" tal que v[i-1] > v[i]
.
A função deve supor que n >= 1
(atente ao caso n == 1
).
Aula 21 (2014-05-13): gramática simplificada das declarações de C; ideia por trás da sintaxe das declarações de C; exemplos de declarações complexas em C.
Observações:
Esta página (em inglês) contém uma ótima e breve explicação sobre declarações de tipos derivados em C. Em sala, nós nos concentramos em, dado um tipo, escrever a declaração correspondente. Na página em questão, faz-se o exercício oposto.
Esta página (em português) contém conteúdo semelhante àquela citada acima.
Exercícios:
Tente declarar os tipos abaixo:
Vetor de 5 vetores de 7 float
's.
Ponteiro para vetor de 5 int
's.
Ponteiro para ponteiro para ponteiro para vetor de 5 int
's.
Vetor de 5 vetores de 8 ponteiros para ponteiros para vetores de 3 char
's.
Descubra os tipos declarados abaixo:
int *p[7];
int **p[4][6];
int *(*p)[4][6];
int *(*p[3])[4][6];
Como um vetor de vetores se relaciona com uma matriz? Modifique o programa da aula anterior de forma que ele use, no lugar de um vetor de "m*n" números, um vetor de "m" vetores de "n" números. Qual versão você considera mais simples?
2014-05-15: sem aula — professor secretariando concurso da UFC, conforme portaria nº 180/2014 do Centro de Ciências da UFC.
Aula 22 (2014-05-20): matriz como vetor de vetores em C: exemplo de programa; aula de laboratório (exercícios anteriores).
Exercícios:
Escreva uma função void somar_na_coluna (double *M, int m, int n, double x, int c)
que soma um número "x" a todos os elementos da coluna "c" de uma dada matriz
de "m" linhas e "n" colunas; o argumento "M" é um ponteiro para a matriz,
que está armazenada num vetor, linha-a-linha, conforme discutido em sala.
Para testar a sua função, escreva também um programa que lê do usuário
os valores de "m", "n", "x" e "c", assim como os elementos da matriz,
e que então chama a função em questão e depois imprime na tela a matriz modificada.
Faça o análogo do programa da questão anterior, exceto que não mais com relação a uma coluna da matriz, mas sim uma linha.
Escreva uma função void trocar_colunas (double *M, int m, int n, int a, int b)
que troca os elementos das colunas "a" e "b" de uma matriz "M" de "m" linhas e "n" colunas.
Além disso, assim como no caso dos exercícios anteriores,
escreva um programa simples que use a função escrita.
Faça o análogo do programa da questão anterior, exceto que não mais com relação a colunas da matriz, mas sim linhas.
Escreva uma função void soma_das_linhas (double *M, int m, int n, double *S)
que recebe uma matriz "M" de "m" linhas e "n" colunas
e calcula a linha que resulta da soma de todas das linhas de "M", coluna-a-coluna,
como no exemplo abaixo.
A linha resultante deve ser gravada no vetor (apontado pelo argumento) "S"
(a sua função deve supor que "S" já aponta para um vetor de tamanho pelo menos "n").
[ 3 -1 2 ] M: [ 6 0 5 ] --> S: [ 10 1 7 ] [ 1 2 0 ]
Escreva uma função void id (int n, int (*M)[n])
que transforma (a matriz apontada por) "M" em matriz identidade.
Aula 23 (2014-05-22): exemplo do uso de ponteiros para vetores, para percorrer uma matriz alocada na mesma função; exemplo no caso em que a matriz é declarada em outra função (aqui um programa semelhante).
Exercícios:
Escreva um programa em C que lê do usuário e depois imprime na tela uma matriz com linhas de diferentes tamanhos, conforme exemplo abaixo. Para tanto, o seu programa deverá usar um vetor de ponteiros (um ponteiro para cada linha), e cada ponteiro deverá apontar para um vetor do tamanho da linha em questão; cada vetor deve, naturalmente, ser alocado dinamicamente. O número de linhas e o tamanho de cada linha devem ser positivos. Não esqueça de desalocar todos os vetores ao final do programa!
Digite o numero de linhas: 3 Digite o tamanho da linha 0: 2 Digite M[0,0]: 7 Digite M[0,1]: 0 Digite o tamanho da linha 1: 4 Digite M[1,0]: 1 Digite M[1,1]: 2 Digite M[1,2]: 3 Digite M[1,3]: 4 Digite o tamanho da linha 2: 0 Digite o tamanho da linha 2: -2 Digite o tamanho da linha 2: 3 Digite M[2,0]: 8 Digite M[2,1]: 5 Digite M[2,2]: 6 A matriz digitada foi: 7 0 1 2 3 4 8 5 6
Escreva um programa que repetidamente lê números do usuário, e que, ao final, imprime para o usuário todos os números lidos. A quantidade de números a serem lidos não é conhecida de início, e também não é informada pelo usuário no início do programa. Ao invés disso, o programa deve repetidamente perguntar ao usuário se ele deseja informar outro número, como no exemplo abaixo. Como a quantidade de números é desconhecida, o seu programa deve armazená-los da seguinte maneira: de início, deve-se utilizar um vetor de 3 elementos; se "esse vetor ficar cheio" (isto é, o usuário digitar 3 números e ainda quiser digitar outro), um novo vetor, com o dobro do tamanho, deve ser alocado, e os números já digitados devem ser copiados para ele; esse procedimento deve ser repetido sempre que o vetor atual ficar cheio. Não esqueça de desalocar os vetores à medida em que eles vão deixando de ser utilizados, e nem de desalocar o último vetor alocado.
Digite um número: 7 Outro? (0: não, outro: sim) 1 Digite um número: -5.8 Outro? (0: não, outro: sim) 1 Digite um número: 0.1 Outro? (0: não, outro: sim) 1 Digite um número: 3.4 Outro? (0: não, outro: sim) 1 Digite um número: 4.9 Outro? (0: não, outro: sim) 1 Digite um número: -90 Outro? (0: não, outro: sim) 0 Os números digitados foram: 7 -5.8 0.1 3.4 4.9 -90
Aula 24 (2014-05-27):
recapitulação sobre o tipo char
e
sequências de escape;
sequências de caracteres (strings) em C:
definição, inicialização, exemplo simples de uso e acesso aos elementos;
ponteiros e sequências constantes de caracteres;
exemplo de função que opera sobre sequências de caracteres,
e de programa usando essa função.
Exercícios:
Escreva uma função que receba (um ponteiro apontando para o início de)
uma sequência de caracteres e que retorne o tamanho da sequência.
ATENÇÃO: a sequência vazia ""
(que contém apenas o caractere \0
)
deve ser levada em consideração.
Escreva uma função void concatenar (char *a, char *b, char *c)
que escreve no vetor apontado por c
a concatenação (justaposição) das sequências de caracteres
apontadas por a
e b
.
Escreva uma função que inverta os caracteres de uma sequência de caracteres — transformando, por exemplo, "Casa" em "asaC".
Escreva uma função int eh_prefixo (char *r, char *s)
que retorna 1 se (a sequência de caracteres apontada por) r
é um prefixo de (aquela apontada por) s
,
e que retorna 0 em caso contrário.
Exemplo: "papa" é prefixo de "paparicar'', mas "papai" não o é.
Escreva uma função int eh_substr (char *r, char *s)
que responde se "r" é uma subsequência contígua de "s".
Em caso positivo, a função deve retornar o índice
do início da primeira ocorrência de "r" em "s";
em caso negativo, a função deve retornar -1
.
EXEMPLO: se "r" for "en" e "s" for "aprendendo",
então a resposta é 3, já que "en" ocorre duas vezes em "aprendendo",
uma vez começando no índice 3 e outra no índice 6 de "s".
DICA: use a função do exercício anterior para descobrir
se há uma ocorrência de "r" começando na posição 0 de "s";
caso não haja, faça o teste para a posição 1 de "s", e assim sucessivamente.
Escreva uma função que receba uma sequência de caracteres
e que retorne o número de linhas do texto armazenado na sequência;
a sua função deve pressupor que toda linha da sequência está terminada
por um \n
.
Escreva uma função que receba uma sequência de caracteres
e retorne o número de caracteres da maior linha do texto
armazenado na sequência (sem contabilizar o \n
que termina cada linha).
Escreva uma função int eh_substr_2 (char *r, char *s)
semelhante àquela do exercício acima,
mas que retorna não o índice da primeira ocorrência de "r" em "s",
mas sim o número da linha dessa ocorrência (numerando-se as linhas
de zero em diante).
Aula 25 (2014-05-29):
as funções getchar
e putchar
para a leitura/escrita de caracteres na tela;
exemplo de leitura e escrita de sequência de caracteres
caractere-a-caractere, sem verificação, na leitura,
dos limites do vetor utilizado;
exemplo de leitura com verificação dos limites do vetor;
as funções fgets
, fputs
,
puts
e printf
para
a leitura/escrita de sequências inteiras.
Correção:
diferentemente do que foi dito em sala, há uma diferença entre
a chamada puts(p)
e as chamadas
fputs(p,stdout)
e printf("%s",p)
:
a chamada puts(p)
imprime na tela não apenas
os caracteres da sequência apontada por p
,
mas também um \n
ao final, enquanto as chamadas
fputs(p,stdout)
e printf("%s",p)
imprimem apenas os caracteres da sequência.
Veja aqui uma documentação de puts
.
Observação:
todas as funções apresentadas são declaradas em
<stdio.h>
e estão documentadas
aqui, por exemplo.
Há também funções que não foram apresentadas mas que são
bastante úteis e frequentemente utilizadas na prática,
como isdigit
, strtod
e strcmp
;
veja as declarações delas e o que elas fazem
aqui.
Exercícios:
Escreva um programa que lê um texto do usuário, da seguinte maneira: (1) o usuário digita um limite para o número de caracteres do texto; (2) o programa lê uma linha de texto; (3) o programa pergunta ao usuário se mais uma linha de texto deve ser digitada, e, se sim, deve retornar ao passo 2. Ao final da leitura do texto, o programa deve imprimir na tela todo o texto que foi digitado. ATENÇÃO: se certifique de que o texto lido não contém mais caracteres do que informado pelo usuário inicialmente.
Faça uma variação do programa anterior, na qual, inicialmente, o usuário informa também o número de linhas a serem digitadas, de forma que não é necessário perguntar, após a leitura de cada linha, se há mais linhas a serem digitadas.
Escreva um programa no qual:
(1) o usuário digita o número de linhas do texto a ser digitado;
(2) antes de digitar a 1a linha, o usuário informa um limite
para o número de caracteres da linha;
(3) após a leitura da 1a linha, o usuário informa um limite para o
número de caracteres da 2a linha, e então digita o conteúdo
da linha.
(4) etc, até que todas as linhas tenham sido lidas,
quando então o texto completo é impresso na tela.
ATENÇÃO: como o tamanho do texto completo não é conhecido de início,
mas sim apenas o número de linhas, use alocação dinâmica para
armazenar o texto: aloque um
vetor de ponteiros para char
(cada ponteiro para uma linha); cada ponteiro deve servir para
apontar para um vetor, que será alocado dinamicamente,
de acordo com o tamanho da linha correspondente.
NÃO ESQUEÇA de desalocar tudo ao fim do programa.
Escreva um programa que lê um texto do usuário (por praticidade, um limite para o número de caracteres do texto pode ser fornecido pelo usuário no início do programa), e que então imprime as linhas do texto na ordem contrária àquela em que foram digitadas (os caracteres de uma mesma linha devem ser impressos na ordem em que foram digitados, apenas a ordem das linhas é que deve ser invertida).
Escreva um programa que lê um texto do usuário e que então imprime na tela a linha mais longa do texto. O número de caracteres do texto não é conhecido, e nem o número de linhas, mas o usuário deve fornecer, antes de digitar o texto, um limite para o tamanho da maior linha.
Escreva um programa que: (1) lê um texto do usuário; (2) lê uma sequência de caracteres; (3) informa quantas vezes a sequência ocorre no texto. Limites para o tamanho do texto e da sequência de caracteres podem ser fornecidos pelo usuário no início do programa.
Aula 26 (2014-05-29 14h – aula extra): solução dos exercícios 1 a 3 da aula de 2014-05-27 (soluções aqui).
2014-06-03: AP2.
Aula 27 (2014-06-05): motivação, introdução e exemplo simples do uso de estruturas em C; escrita, explicação e simulação de exemplo mais elaborado, envolvendo cópia de estruturas, estruturas aninhadas, funções e ponteiros para estruturas; inicialização de estruturas (notação por ordem de declaração dos membros).
Observações importantes:
Como dito em sala, há uma maneira de se inicializar estruturas pelo nome dos membros, e não pela ordem em que eles foram declarados na definição do tipo em questão. A sintaxe é aquela que foi apresentada em sala, e que é exemplificada nas observações 1 e 2 deste anexo (preparado por mim para disciplina de semestre anterior).
Como explicado no anexo acima, é possível, na inicialização de uma estrutura, não fornecer inicializadores para todos os membros da estrutura; nesse caso, os membros restantes são inicializados com zero.
O acesso a estruturas por meio de um ponteiro é tão comum
que há em C uma notação especial para isso:
se p
é um ponteiro para uma estrutura que
possui um membro m
, então p->m
é, por definição, o mesmo que (*p).m
.
Exercícios:
Escreva um programa que lê do usuário os dados de uma circunferência e de um ponto em ℜ², e que então informa se o ponto está dentro, fora ou sobre a circunferência. Os dados da circunferência (centro e raio) e do ponto (coordenadas x e y) devem ser armazenados em estruturas definidas pelo seu programa.
Escreva uma função que recebe como argumentos duas datas, e que então retorna a diferença entre elas, também na forma de uma data (isto é, em anos, meses e dias). Em seguida, escreva um programa que lê a data de nascimento de uma pessoa e uma data posterior qualquer, e que então imprime na tela a idade da pessoa na data em questão (em termos de anos, meses e dias).
Escreva um programa que lê do usuário os dados de "n" alunos
(nome, data de nascimento, notas nas AP1, AP2, AP3),
e que então repetidamente informa ao usuário os dados de
um aluno qualquer, identificado pelo nome, como no exemplo abaixo.
O programa deve terminar quando o usuário digitar um nome não cadastrado.
DICA: nesse programa você vai precisar de uma função que responda se duas
sequências de caracteres são ou não iguais;
você pode escrever uma tal função por conta própria, ou então usar a função
strcmp de string.h
.
Digite o número de alunos: 3 Digite o nome do aluno 0: Fulano Digite a data de nascimento do aluno 0: 1/2/1993 Digite as notas do aluno 0: 9 8.1 9 Digite o nome do aluno 1: Beltrano ... Digite o nome do aluno 2: Cicrano ... Digite o nome do aluno: Beltrano Nascimento: 9/8/1997 Notas: 5, 6, 8. Digite o nome do aluno: Fulano Nascimento: 1/2/1993 Notas: 9, 8.1, 9. Digite o nome do aluno: João. Aluno não cadastrado, terminando o programa.
Aula 28 (2014-06-10): tira-dúvidas e solução de exercícios: exercício 1 da aula 25 (2014-05-29); programa para ler e imprimir uma data que faça sentido, levando em conta inclusive anos bissextos; exercício 1 da aula 27 (2014-06-05).
— Fim das aulas! —