Você está na página 1de 52

UNIVERSIDADE FEDERAL DO RIO DE JANEIRO

Coordenação dos Programas de Pós–graduação em Engenharia


Programa de Engenharia Quı́mica
Laboratório de Termofluidodinâmica

Introdução à Programação em C

Curso de Extensão

Luiz Fernando Lopes Rodrigues Silva, PEQ/COPPE/UFRJ

RIO DE JANEIRO, RJ — BRASIL


2005
Sumário

1 Introdução à Programação 2
1.1 Interpretação do Arquivo–fonte . . . . . . . . . . . . . . . . . 2
1.2 Computação Cientı́fica . . . . . . . . . . . . . . . . . . . . . . 3
1.3 Exercı́cios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4 Primeiro Programa em C . . . . . . . . . . . . . . . . . . . . . 5

2 Tipos e Operadores Básicos 7


2.1 Tipos de Variáveis . . . . . . . . . . . . . . . . . . . . . . . . 7
2.1.1 Declaração de Variáveis . . . . . . . . . . . . . . . . . 8
2.1.2 Variáveis Inteiras . . . . . . . . . . . . . . . . . . . . . 8
2.1.3 Variáveis de Caracteres . . . . . . . . . . . . . . . . . . 9
2.1.4 Variáveis de Ponto Flutuante . . . . . . . . . . . . . . 10
2.1.5 Variáveis Booleanas . . . . . . . . . . . . . . . . . . . . 11
2.2 Tipos de Operadores . . . . . . . . . . . . . . . . . . . . . . . 12
2.2.1 Operadores Aritméticos . . . . . . . . . . . . . . . . . 12
2.2.2 Operadores Relacionais . . . . . . . . . . . . . . . . . . 15
2.2.3 Operadores Lógicos . . . . . . . . . . . . . . . . . . . . 15
2.3 Exercı́cios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3 Fluxos de Controle 19
3.1 Condicionais . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.1.1 Comando if–else if . . . . . . . . . . . . . . . . . . . . 19
3.1.2 Comando switch . . . . . . . . . . . . . . . . . . . . . 21
3.2 Laços . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.2.1 Comando for . . . . . . . . . . . . . . . . . . . . . . . 23
3.2.2 Comando while . . . . . . . . . . . . . . . . . . . . . . 26
3.3 Exercı́cios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

4 Tipos Derivados 31
4.1 Entrada e Saı́da em Arquivo . . . . . . . . . . . . . . . . . . . 31
4.2 Arrays - Vetores e Matrizes . . . . . . . . . . . . . . . . . . . 35
SUMÁRIO ii

4.3 Ponteiros e Referência . . . . . . . . . . . . . . . . . . . . . . 37


4.4 Exercı́cios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

5 Sub-rotinas 42
5.1 Funções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.1.1 Declaração e Definição de Função . . . . . . . . . . . . 42
5.1.2 Passagem de Argumentos . . . . . . . . . . . . . . . . 43
5.2 Exercı́cios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Prefácio

Atualmente em qualquer área de atuação profissional, o computador se torna


uma ferramenta cada vez mais presente. Seja para montar um relatório para
a empresa, estruturar uma apresentação sobre suas atividades e progressos,
executar tarefas repetitivas de análise de dados em uma fábrica, entre muitas
outras funções. Mas o principal objetivo deste curso é mostrar os meios
de uso do computador como ferramenta cientı́fica para realizar cálculos de
modo eficiente. Em outras palavras, como analisar um problema qualquer e
estruturar uma metodologia de solução de forma que o computador entenda
o que o usuário quer, execute os comandos o mais rápido possı́vel e forneça
resultados confiáveis.
O objetivo desta apostila é apenas de colocar as informações básicas sobre
a linguagem de programação C de modo a auxiliar o treinamento do aluno
durante o curso. Como alguns tópicos serão colocados em sala de aula, eu
coloquei alguns códigos com espaço suficiente para que vocês possam anotar
as discussões. A descrição da linguagem não depende do sistema operacio-
nal, seja Windows, Linux, DOS, MacOS, UNIX, desde que um compilador C
esteja instalado. Existem duas referências sobre C que eu gostaria de passar
para vocês. A primeira é o livro de Brian W. Kernighan e Dennis M. Ritchie
intitulado “C — A linguagem de programação padrão ANSI”. Estes autores
foram os desenvolvedores da linguagem C e apesar do livro possuir um bom
conteúdo, seus exemplos são mais voltados para manipulação de caracteres.
Já o livro “C++ and object-oriented numeric computing for scientists and
engineers” de Daoqi Yang é uma referência excelente para programação ci-
entı́fica. Apesar de ser um livro de C++, muitos fundamentos básicos de C
são usados e vale (muito!!) a pena dar uma olhada nele.
Espero que gostem do curso,
Luiz Fernando L. R. Silva
PEQ/COPPE/UFRJ
21/07/2005
Capı́tulo 1

Introdução à Programação

1.1 Interpretação do Arquivo–fonte

Os programas de computador são formados por uma série de instruções que


o computador executa. Quando você cria um programa, precisa especificar
as instruções que o computador precisará executar para realizar as operações
desejadas. A definição destas instruções é que você deve fazer e salvar em
um arquivo, chamado de arquivo ou código fonte. O arquivo fonte de um
programa escrito em C deve possuir a extensão C (ex. arqfonte.c). O ar-
quivo fonte é o modo do programador montar a sua lógica de programação e
comunicar isso ao computador. Mas o computador não entende diretamente
o arquivo fonte e precisa de um tradutor, chamado de compilador. Para criar
programas, é imperativo e necessário que o compilador esteja instalado no
computador. O compilador converte as instruções colocadas no arquivo fonte
para a linguagem de máquina (binária — 0 ou 1) que o computador compre-
ende. Os arquivos gerados com a extensão EXE ou COM contém o código
de máquina e podem ser executados pelo computador. A Fig. 1.1 ilustra
esse processo de geração do código fonte, compilação e criação do arquivo
executável.
1.2 Computação Cientı́fica 3

Figura 1.1: Conversão do código fonte para código de máquina.

1.2 Computação Cientı́fica

Em qualquer aplicação cientı́fica, a eficiência computacional é muito impor-


tante e o programador deve sempre ficar atento a esse detalhe. Mas como
montar um programa de forma eficiente? Bem, antes de pensar em pro-
gramação propriamente dita deve–se estruturar muito bem qual é o problema
e qual a melhor forma de resolvê–lo. Se isso não estiver bem assentado na
cabeça do programador, é melhor parar por aı́ e começar a ler o seu problema
novamente. Afinal de contas, como você vai resolver o seu problema de forma
eficiente, se nem sabe ainda como resolvê–lo?

Dica de programação do L.F.


Antes de pensar em programar, organize as idéias no papel. Mes-
mo para os problemas mais simples. Sempre!! A princı́pio
pode parecer perda de tempo, mas a facilidade de estruturar o
seu código fonte será recompensadora.

As etapas necessárias de solucionar o problema é o que vamos chamar de


metodologia de algoritmo e a forma como estas etapas são executadas está
incluı́da na lógica de programação. Estas definições vão ficar mais claras ao
analisarmos um exemplo.

1 Exemplo da distância percorrida pelo carro com aceleração constante.


1.3 Exercı́cios 4

Bem, temos o problema nas mãos, ou seja, um modelo matemático a ser


resolvido.
1
S = S0 + v0 t − at2 (1.1)
2

Quais são as perguntas que sempre devemos fazer para estruturar a me-
todologia do algoritmo?

• Qual o meu objetivo? Calcular S!!! ⇒ Então, essa é a última coisa


com que devemos nos preocupar.

• O que eu preciso para calcular S? S0 , v0 , a e t. ⇒ Aaah, então eu


tenho que me preocupar em saber os valores dessas variáveis antes de
calcular S...
- Atribuir valores para S0 , v0 , a e t.
- Existe alguma restrição numérica e/ou fı́sica no meu modelo? ⇒
Não existe tempo negativo.
♦ Condição para que o programa rejeite valores negativos de
tempo.

• Calculamos S.

• Mostramos ao usuário o resultado. ⇒ Senão, não adianta nada, né?

Mesmo para um problema simples, algumas perguntas devem ser respon-


didas antes de tentar calcular qualquer coisa. Eu ficaria muito feliz se ao
longo do curso (e depois também) vocês sempre montassem o algoritmo de
solução. Vocês notarão que, com o tempo e a prática, estruturar a solução de
um problema ficará muito mais fácil e poderá até ser feita automaticamente.
Podem confiar.

1.3 Exercı́cios

I Monte a estrutura de solução para os problemas abaixo.


1.4 Primeiro Programa em C 5

1) A troca de calor em um reator não isotérmico precisa ser otimizada.


Você foi incumbido de calcular a quantidade de calor trocada entre o reator
e o meio ambiente. O modelo de troca térmica está colocado abaixo, onde Q
é a quantidade de calor trocada, UA é o coeficiente global de transferência
de calor, Tr é a temperatura do reator e T∞ é a temperatura do ambiente.

Q = UA(Tr − T∞ ) (1.2)

2) A Baı́a de Guanabara possui uma concentração de petróleo que está


se dispersando com o passar do tempo. A dispersão de petróleo na baı́a segue
o modelo descrito na Eq. 1.3 onde Cp é a concentração de petróleo no instante
t, Cp0 é a concentração inicial de petróleo e A é a área onde o petróleo está
disperso. Contudo, a área de dispersão do petróleo varia devido à condições
climáticas e com o próprio tempo. Um modelo (Eq. 1.4) descreve a variação
da área em que o petróleo está disperso, onde o parâmetro M̄ é uma média
dos efeitos climáticos.
Cp = Cp0 e−At (1.3)
A = M̄ e−t (1.4)

1.4 Primeiro Programa em C

Agora que já vimos como montar os nossos problemas de uma forma estru-
turada, já podemos ver um primeiro exemplo de programação em C. Note
que ainda não vimos nenhum comando desta linguagem e este exemplo irá
servir apenas como uma forma de começarmos uma discussão em torno da
lógica de programação em C.

Um programa em C necessariamente deve conter a função main() que


contêm as instruções (ou comandos) entre entre { e }. Cada comando pode
ser escrito em mais de uma linha e deve ser terminado por um ponto e
vı́rgula (; — se não tiver, vai dar erro na compilação do programa). Os
comentários no código fonte estão entre /* e */ (todo o texto entre esses dois
sı́mbolos é desconsiderado pelo compilador) ou precedidos por // (a linha
toda fica comentada). Os comentários podem conter várias informações sobre
o problema (qual o problema a ser resolvido, qual a metodologia que vai ser
utilizada, quem programou o código, quando, etc.), o código fonte (o porquê
de usar um comando especı́fico) ou mesmo detalhes de como usar o programa
1.4 Primeiro Programa em C 6

depois de compilado. A princı́pio, comentar o arquivo fonte pode parecer uma


tarefa exaustiva, porém estes são muito úteis para o entendimento futuro do
código.

Dica de programação do L.F.


Sempre comente o seu código. Não preciso colocar os porquês,
apenas comente!!!!!

O objetivo deste primeiro programa é simplesmente escrever uma mensa-


gem na tela para o usuário. O código fonte está colocado a seguir.

/* Este é um simples programa para escrever na tela


uma mensagem de texto.
Programado por: Luiz Fernando L. R. Silva */

#include<stdio.h> // Biblioteca padr~


ao I/O em tela

main()
{
// Comando para escrever em tela.
printf("Eu adoro programar em C");
}

As três primeiras linhas do código são comentários (estão entre /* e */) e


colocam algumas informações sobre o programa. Os outros comentários desse
programa são menores e são precedidos por duas barras //. As bibliotecas em
C contêm vários comandos e funções padrões para auxiliar na programação.
Para habilitar esses comandos, é necessário declarar as bibliotecas no inı́cio
do programa. A biblioteca padrão que contêm os comandos de entrada e
saı́da de dados em tela chama-se <stdio.h> e está declarada logo após o
primeiro comentário. Essa declaração habilita que o programador use todos
os comandos de entrada e saı́da padrão em tela. O comando printf efetua essa
operação mostrando ao usuário a seguinte mensagem: Eu adoro programar
em C. Pronto, o nosso primeiro programa está pronto.
Capı́tulo 2

Tipos e Operadores Básicos

2.1 Tipos de Variáveis

Vamos considerar a seguinte expressão matemática:

z =y+i∗x (2.1)

Para que a Eq. 2.1 faça sentido em no código fonte, os identificadores 1


x, y, z e i devem ser declarados de forma adequada. Todos os identificadores
devem ser declarados antes de serem usados. As declarações introduzem
entidades representadas por seus identificadores e seus tipos (ex. inteiro ou
caractere).

Os tipos das variáveis fornecem propriedades diferentes tanto na forma


de armazenamento da variável na memória RAM quanto na própria mani-
pulação e operação da variável. Existem operações que só são possı́veis ou
são muito mais eficientes se realizadas com um determinado tipo de variável.

if( i == 0 )
{ /* realiza os comandos aqui dentro */ }
1
Os identificadores são as letras usadas para identificar as suas variáveis.
2.1 Tipos de Variáveis 8

2.1.1 Declaração de Variáveis

A declaração das variáveis no código fonte indica que o programa deve re-
servar um espaço na memória RAM para cada variável declarada. Vamos
considerar que a memória RAM está distribuı́da como na Fig. 2.1.1 e divi-
dida em bytes.

Figura 2.1: Representação da memória RAM subdividida em bytes.

Agora vamos considerar que no inı́cio do programa as variáveis i e z este-


jam sendo declaradas e portanto, um espaço na memória deve ser reservado.
Este procedimento é o que chamamos de alocação de espaço na memória.
Deste modo, os valores que forem atribuı́dos nestas variáveis ficam alocados
em um endereço especı́fico da memória RAM. A quantidade de memória que
deve ser reservada depende do tipo da variável.

Figura 2.2: Alocação de variáveis na memória RAM.

2.1.2 Variáveis Inteiras

Cada variável inteira ocupa 2 bytes de memória e o valor armazenado na


memória não inclui a parte fracionária. Um exemplo segue abaixo.

#include<stdio.h>

main()
{
int i; // Declaraç~
ao de i como inteira
2.1 Tipos de Variáveis 9

int j,k; /* Declaraç~ao de j e k como inteiros.


É possı́vel declarar mais de uma
variável por linha */
int ii = 123; // Ao declarar ii, já atribuo um valor.

i = 2; // Atribuiç~
ao do valor 2 para a variável i
j = i; // Atribuiç~
ao do valor contido em i para j
k = 2.3; /* N~
ao considera a parte fracionária!
k assume o valor de 2 */

/* Saı́da em tela das variáveis*/


printf("O valor de i eh: %d \n",i);
printf("O valor de j e k sao: %d %d \n",j,k);
printf("%d mais %d igual a %d \n",1,2,1+2);
}

2.1.3 Variáveis de Caracteres

Cada variável caractere ocupa 1 byte na memória e armazena valores inteiros


que são convertidos em caracteres ASCII (o valor do inteiro 98 corresponde ao
caractere ’b’ no conjunto ASCII). Normalmente, o que se quer é uma variável
que armazene um conjunto de caracteres para que seja possı́vel construir
palavras ou sentenças. Alguns caracteres são especiais e possuem algumas
funções especı́ficas.

Caractere Função especı́fica


\n nova linha
\t tabulação horizontal
\v tabulação vertical
\a alerta sonoro
\r cursor volta para o inı́cio da linha
\b backspace

Tabela 2.1: Caracteres especiais para operar a saı́da de dados.

Um exemplo (extremamente idiota!) de aplicação de caracteres.

#include <stdio.h>
#include <conio.h>
2.1 Tipos de Variáveis 10

main()
{
char ch1, ch2;

printf("Entre com 1 caracter: ");


/* le um caracter do console sem mostra-lo */
ch1 = getch();

printf("\nEntre com 1 caracter: ");


/* le um caracter do console mostrando-o em seguida:
getche() = getch() and echo */
ch2 = getche();

/* Mostra os caracteres lidos na tela: */


printf("\nO primeiro caracter lido foi: %c", ch1);
printf("\nO ultimo caracter lido foi: %c\n", ch2);

/* Mostra os caracteres lidos na tela usando putch(): */


printf("\nCaracterres lidos:\n");
putch(ch1);
putch(ch2);
printf("\n");

/* A funcao getchar le um caracter da


entrada padrao (stdin): */
ch1 = getchar();

/* A funcao putchar mostra um caracter


na saida padrao (stdout): */
putchar(ch1);

printf("\n");
}

2.1.4 Variáveis de Ponto Flutuante

As variáveis de ponto flutuante são capazes de armazenar as casas decimais


de um número. Existem duas classificações para as variáveis de ponto flutu-
2.1 Tipos de Variáveis 11

ante, com precisão simples (normalmente com 6 casas decimais) ou de dupla


precisão (normalmente com 15 casas decimais). Um exemplo quantifica bem
a diferença entre esses dois tipos de variáveis.

#include<stdio.h>
#include<math.h>

int main()
{
float pi_float;
double pi_double;

pi_float = acos(-1.);
pi_double= acos(-1.);

printf("Valor com precisao simples: %21.19f\n", pi_float);


printf("Valor com precisao dupla : %21.19f\n", pi_double);
return(1);
}

Compilando e executando o código acima em um AthlonXP 2.2GHz, os


resultados para π estão colocados abaixo.

Valor com precisao simples : 3.1415927410125732000


Valor com precisao dupla : 3.1415926535897931000

Obs.: executem o programa colocado acima mudando a referência %21.19f


da variável de ponto flutuante. Mude o número das casas decimais, o número
dos espaços reservados para a variável e troque o f do ponto flutuante por
%g, %e e %E. Vejam e analisem as mudanças.

2.1.5 Variáveis Booleanas

Um booleano, representado por bool, pode armazenar dois valores: true ou


false, e é usado para expressar os resultados de expressões lógicas.
2.2 Tipos de Operadores 12

bool flag = true; // declaraç~


ao de flag como booleana
double d;

/* Algum código no meio do caminho, que pode


eventualmente mudar o valor de flag */

d = 3.14;
if(flag == false)
{ d = 2.718; }

/* E o programa continua... */

Algumas comparações estão descritas na tabela abaixo.

true && true = true true || true = true


true && false = false true || false = true
false && true = false false || true = true
false && false = false false || false = false

Tabela 2.2: Comparações de booleanos — && significa E; || significa OU

Por definição, false tem o valor 0 e true tem valor 1. Por isso, pessoal-
mente, eu não uso variáveis booleanas, mas um inteiro com valor de 0 (falso)
ou 1 (verdadeiro).

2.2 Tipos de Operadores

Pronto, já sabemos quais são os tipos de variáveis que existem e agora esta-
mos aptos a realizar operações com elas.

2.2.1 Operadores Aritméticos

São os operadores básicos de soma (+), subtração (−), multiplicação (∗)


e divisão (/). É muito importante entender o tipo de variável que se está
operando, pois perda de informação pode ocorrer. Veja o exemplo a seguir.
2.2 Tipos de Operadores 13

double m = 13/4; /* é uma operaç~


ao entre dois inteiros, e vai
fornecer um resultado inteiro!!
m = 3, as casas decimais s~ ao ignoradas */
double n = 3/4; /* n = 0 */
double y = 3.0/4; /* Ao operar, o inteiro 4 é convertido para
double! y = 0.75 */
double y = 3/4.0; /* Idem, 3 é convertido em 3.0! y = 0.75 */

As operações matemáticas são iniciadas da esquerda para a direita. A


multiplicação e a divisão possuem uma maior precedência em relação à soma
e subtração. Contudo, o que está contido nos parênteses possue uma pre-
cedência maior que a dos operadores. Vejamos as etapas de execução das
expressões colocadas no exemplo abaixo.

int m;
m = 1 + 2 + 3*4; /* m = 1 + 2 + 12;
m = 3 + 12;
m = 15; */

m = 1 + (2 + 3)*4; /* m = 1 + (5)*4;
m = 1 + 20;
m = 21; */

m = 5 + 10 + 15/3; /* m = 5 + 10 + 5;
m = 15 + 5;
m = 20; */

m = (5+10+15)/3; /* m = (15 + 15)/3;


m = 30/3;
m = 10; */

Dica de programação do L.F.


Sempre que você tiver dúvidas sobre a ordem das operações, use
o parênteses sem medo de ser feliz.

int m;
m = ((5+3)*2) - 3; /* m = (8*2) - 3;
2.2 Tipos de Operadores 14

m = 16 - 3;
m = 13; */

Os operadores de incremento (++) e decremento (−−) são muito usados


em qualquer programa C, pois facilitam a leitura do código (alguns dizem
quem não...).

int conta = 0;
int m = 2;
conta++; /* Essa operaç~ao pode ser substituı́da por:
conta = conta + 1; (conta = 1) */
conta--; /* Essa operaç~ao pode ser substituı́da por:
conta = conta - 1; (conta = 0) */
++m; /* m = 3; */
m = conta++; /* m = 0; conta = 1;*/

Vamos analisar o seguinte código (em aula).

#include<stdio.h>

int main()
{
int valor = 1;

printf("Usando depois da variavel : %d\n", valor++);


printf("Valor apos incremento: %d\n", valor);
valor = 1;
printf("Usando depois da variavel : %d\n", ++valor);
printf("Valor apos incremento: %d\n", valor);
return(1);
}

Ainda é possı́vel realizar operações de atribuição da seguinte maneira.

total += 100; // Significa: total = total + 100;


conta -= 5; // Significa: conta = conta - 5;
metade /=2; // Significa: metade = metade/2;
2.2 Tipos de Operadores 15

2.2.2 Operadores Relacionais

Os operadores relacionais são > (maior que), < (menor que), >= (maior ou
igual a), <= (menor ou igual a), == (igual a) e ! = (não igual). A notação ! é
o operador de negação. O resultado destas operações é sempre um booleano.

int x = 5;
bool b, c;
b = (x < 6); // b = true, já que x é menor que 6
c = (x == 0); // b = false, já que x n~ao é 0

if(b != c) // Se b n~
ao é igual a c, x = 17
x = 17;
if(b) // Se b é true, ent~
ao faça x = 19
x = 19;
if(!b) // Se b é false, ent~
ao faça x = 221
x = 221;

Deve-se tomar muito cuidado ao usar essas operações. Considere o exem-


plo abaixo.

int m = 7;
bool b, c;
b = 3 < m < 9; // b = true
m = 20;
c = 3 < m < 9; // c = true

Os operadores relacionais são executados da esquerda para a direita, por


isso a expressão 3 < m < 9 é equivalente à (3 < m) < 9. Então, esta
expressão sempre é avaliada como true pois 3 < m pode ser tanto 1 (true)
quanto 0 (false).

2.2.3 Operadores Lógicos

Os operadores lógicos, assim como os relacionais, resultam em um valor bo-


oleano. Esses operadores são && (e) e || (ou) e posuem uma precedência
2.3 Exercı́cios 16

maior que os operadores relacionais (são executados antes). A Tabela 2.2


mostra as relações com os booleanos. Veja os exemplos a seguir.

bool b =true, c = false;


bool d = b && c; // d = false
bool e = b || c; // e = true
bool f = (e == false) && c; // f = false
bool g = (d == true ) || c; // g = false
bool h = (d = true ) || c; // h = true

Vamos agora rever a expressão 3 < m < 9. A maneira correta de progra-


mar esta expressão em C seria

(3 < m) && (m < 9)

Como o operador < possue uma precedência maior que o operador &&,
os dois pares de parênteses poderiam ser omitidos.

2.3 Exercı́cios

I As variáveis a, b, c, d, e, f e g foram inicializadas com os seguintes valores:

a = 3. b = 2. c = 5. d = 4. e = 10. f = 2. g = 3.

Efetue as seguintes expressões em C :

1. saida = a*b+c*d+e/pow(f,g)

2. saida = a*(b+c)*d + pow(e/f,g)

3. saida = a*(b+c)*(d+e)/pow(f,g)
2.3 Exercı́cios 17

II Analise o código abaixo para calcular a média das notas de um aluno.


Programe este código e retire alguns dos comentários e comente alguns co-
mandos para entender como o código funciona.

/*Cálculo da média dos alunos*/


#include <stdio.h>

int main()
{
double M,P1,P2;
// Entrada de dados pelo código
P1 = 5.0;
P2 = 7.5;
/* Entrada de dados em tela
printf("Digite o valor da primeira prova P1\n");
printf("Digite o valor da segunda prova P2\n");
scanf("%16le",&P1);
scanf("%16le",&P2);
*/

// Cálculo da média
// Cálculo direto
M=(P1+P2)/2.;
// Cálculo em duas etapas
//M = P1 + P2;
//M /= 2;

if(M >= 7.) { printf("O aluno esta aprovado\n"); }


else { printf("O aluno devera fazer prova final\n"); }

printf("A media do aluno e: %.2f\n",M);

return(1);
}

III Faça um programa em C que calcule a velocidade em queda livre de


um corpo
√ a 6 m de altura. Sabe-se que a fórmula de queda livre é dada por
v = 2gh, onde g = 9.81. Faça um outro programa em que seja possı́vel que
o usuário entre na tela com o valor da altura (cuidado, porque até onde eu
2.3 Exercı́cios 18

sei e os limites da fı́sica permitem, não existe altura negativa).

IV Faça um programa em C que leia uma temperatura em graus Farenheit,


converta-a para uma temperatura absoluta em kelvins, e escreva o resultado.
A fórmula de conversão é:

5.
T (K) = [T (F ) − 32.0] + 273.15 (2.2)
9.

V O coseno hiperbólico é definido pela equação:

ex + e−x
!
cosh(x) = (2.3)
2.

Escreva um programa em C para calcular o coseno hiperbólico para um


valor x fornecido pelo usuário. Use o programa para calcular o coseno hi-
perbólico de 3.0. Compare a resposta que seu programa fornece com a res-
posta produzida pela função intrı́nseca do C cosh(x), contida na biblioteca
<math.h>.
Capı́tulo 3

Fluxos de Controle

3.1 Condicionais

3.1.1 Comando if–else if

A forma mais simples de um condicional é o comando if, cuja sintaxe é:

if(condiç~
ao) express~
ao

onde condição é uma expressão lógica que possui valor de true ou false, e
expressão pode ser apenas um ou vários comandos. A expressão é executada
quando o valor de condição é avaliado em true, senão a expressão não é
executada. A condição deve estar entre parênteses.

if(n>10) x*=10; // Qdo n>10, faça x*=10


if(n==0) { // Qdo n é 5, faça x*=5
x*=5;
}
if(n) y*=7; // Qdo n!=0, faça y*=7

Os condicionais devem ser usados com cuidado quando a expressão requer


a comparação de variáveis não inteiras. Considere x e y como variáveis de
3.1 Condicionais 20

ponto flutuante, a condição if(x == y) deve ser substituı́da por if(fabs(x - y)


< erro) onde erro é um número muito pequeno definido pelo usuário e fabs é
a função matemática que retorna o valor absoluto de uma variável de ponto
flutuante.

Dica de programação do L.F.


Tome muito cuidado ao avaliar variáveis em ponto flutuante.
Note ainda que existe diferença entre == e =! Aqui, o pro-
gramador pode e deve “usar e abusar” dos parênteses e dos
operadores lógicos e relacionais.

O condicional if pode ser acompanhado pela parte opcional else.

if(condiç~
ao)
express~ao1
else
express~ao2

onde, se a condição for avaliada como true, a expressão1 é executada


enquanto a expressão2 é desprezada. Se a condição retornar um valor false,
a expressão1 é desprezada e a expressão2 é executada. Ainda é possı́vel
combinar os condicionais if–else da seguinte maneira:

if(condiç~
ao1)
express~ao1
else if(condiç~
ao2)
express~ao2
else if(condiç~
ao3)
express~ao3
else
express~ao4

onde a expressão1 é executada só e somente só se a condição1 for true.


Se for true, todo o resto do bloco é ignorado; e se for false a expressão1 não
é executada e a condição2 é testada. Se a última for true, a expressão2 é
3.1 Condicionais 21

avaliada e o resto do bloco é desprezado, enquanto se a condição2 for false o


resto do bloco é ignorado. Esse procedimento é repetido até o final do bloco,
onde se nenhuma das condições forem true, a expressão4 será executada.
Note que a última expressão é um caso que não possui nenhuma restrição e
será efetuado sempre que nenhuma das condições anteriores forem satisfeitas.

Vamos ver o exemplo abaixo. O programador quer montar uma estrutura


que retorne os parâmetros fı́sicos da água. Porém, até onde eu sei e a fı́sica
explica, dependendo da temperatura, a água pode se apresentar na forma
sólida, lı́quida ou gasosa e seus parâmetros fı́sicos dependem de sua forma.
Como fazer isso?

// Usuário entra com T (em K)


printf("Entre com a temperatura da agua (K)");
scanf("%16le",&T);
printf("\n");

if(T <= 273.15)


{ // Gelo!
// Calcula par^
ametros para gelo
}
else if((T >= 273.15) && (T <= 373.15))
{ // Lı́quido!
// Calcula par^
ametros para água lı́quida
}
else
{ // Só sobrou o vapor (fora o plasma!!)
// Calcula par^ametros para vapor d’água
}

3.1.2 Comando switch

O comando switch é muito usado em situações de múltipla escolhas. Este


testa o valor da condição, colocada entre parênteses após a palavra chave
switch. Então este valor é usado como um controle de escolha para os casos
que satisfazem esse valor. Se o valor não satisfaz nenhum caso, o controle irá
para o caso default. O comando break é usado para sair do comando switch.
3.1 Condicionais 22

Vamos analisar o exemplo abaixo, onde o programador quer calcular o


coeficiente de transferência de calor em alguns casos especificados. Sabe-se
que os modelos matemáticos são diferentes para cada situação fı́sica, então
devemos colocar restrições no usos destes ou deixar que o usuário especifique
o caso que ele quer o resultado.

#include<stdio.h>
#include<conio.h>
#include<ctype.h>
#include<stdlib.h>

int main()
{
char letra;
// Tem q declarar os par^
ametros dos modelos, né?
// Se n~
ao já era!!!!

printf("Lista de opcoes para calcular o coeficiente ");


printf("de transferencia de calor:\n");
printf("A - Modelo para Conveccao Interna em tubos\n");
printf("B - Modelo para Conveccao Externa\n");
printf("C - Modelo para Conveccao Natural\n");
printf("D - Modelo para Conveccao com condensacao\n");
printf("Entre com sua opcao : ");
letra = getch();
// Transforma letra minúscula em maiúscula
letra = toupper(letra);
printf("\n");

// Vamos escolher qual modelo? Depende do q o usuário quer...


switch (letra)
{
case ’A’:
// Escreve aqui como calcula o h para conv. interna
// Comandos
printf("Fingi que calculei h por A.\n");
break;
case ’B’:
// Escreve aqui como calcula o h para conv. externa
// Comandos
3.2 Laços 23

printf("Fingi que calculei h por B.\n");


break;
case ’C’:
// Escreve aqui como calcula o h para conv. natural
// Comandos
printf("Fingi que calculei h por C.\n");
break;
case ’D’:
// Escreve aqui como calcula o h para conv. com condensacao
// Comandos
printf("Fingi que calculei h por D.\n");
break;
default :
// Entra aqui se nenhum das condiç~
oes foram satisfeitas
printf("Esse caractere nao e valido. Foi mal...\n");
};
return (1);
}

Dica de programação do L.F.


Você sabia que os condicionais são um dos comandos mais com
maior custo computacional que existem? Se quiser montar
um programa com menor custo de computação, use o me-
nor número de condicionais possı́vel e somente quando for
necessário.

3.2 Laços

3.2.1 Comando for

O laço for é muito utilizado e extremamente conveniente de ser usado. Sua


forma geral está colocada abaixo.

for(inicia;condiç~
ao;express~
ao)
comandos
3.2 Laços 24

A inicia representa a inicialização da variável de controle do laço, a


condição fornece um controle para determinar se o laço deve continuar a
ser executado e expressão indica como a variável inicializada deve ser ava-
liada a cada vez que os comandos são executados. O laço é continuamente
executado até que a condição seja avaliada como false. Este processo de
repetição é chamado iteração. Vamos ver o seguinte exemplo.

int i;
for(i=3;i<50;i*=2)
{ printf("%d\n",i); }

O exemplo irá retornar na tela os inteiros 3, 6, 12, 24 e 48. O valor inicial


é 3. Como a condição i < 50 é true, 3 aparece na tela. Então, a expressão
i∗ = 2 fornece o segundo valor de i que é 6. Como i < 50 ainda é true, 6
também é retornado na tela. Este processo é repetido até que i∗ = 2 forneça
96, one i < 50 é false.

O que acontece no exemplo abaixo?

int i;
for(i=3;i>5;i*=2)
{ printf("%d\n",i); }

Nada!! A condição não é satisfeita quando i = 3 e portanto o laço não é


executado, muito menos a expressão i∗ = 2.

O comando break pode ser usado para terminar a execução do laço, e o


continue para a execução da iteração atual e passa para a próxima. Para
deixar mais claro, vamos ver o exemplo.

int i;
for(i=3;i<50;i*=2)
{ if(i == 6) continue; // Passa para a próxima iteraç~
ao
printf("%d\n",i);
if(i == 24) break; // Pára a execuç~
ao do laço qdo i é 24
}
3.2 Laços 25

Os inteiros 3, 12, 24 irão aparecer na tela. Quando i for 6, o valor de i


não será retornado na tela já que o comando continue faz com que o controle
vá para a próxima iteração com i = 6 ∗ 2 = 12. Quando i = 24, o laço é
terminado devido ao comando break.

Dica de programação do L.F.


Muito cuidado! Ao usar for, não altere o valor do contador dentro
do laço, pois isso é uma alta fonte de erros.

Um laço pode ser acoplado como qualquer outra expressão. Por exemplo,

int i,j;
double a = 0.;
for(i=0;i<100;i++)
{ for(j=200;j<=500;j++)
a+=i-j; // int s~ao convertidos para double
}

computa a seguinte equação


99 X
X 500
(i − j) (3.1)
i=0 j=200

int i,j;
long double b = 5.;
for(i=1;i<5;i++)
for(j=27;j>=-3;j--)
b*=i+j*j;

computa a seguinte equação (Putz!)


4 Y
Y 27
5 (i + j 2 ) (3.2)
i=1 j=−3
3.2 Laços 26

Dica de programação do L.F.


Façam a programação de Chinês. Ao montarem um laço, exe-
cutem cada comando no papel e vejam se os resultados estão
chegando ao que você tinha em mente. Mas execute cada co-
mando mesmo! Vocês vão exercitar a visão de programação e
fica mais difı́cil de deixar o programa com erros (pelo menos
nessa parte que você fez o chinêzinho).

3.2.2 Comando while

O comando while possui a seguinte forma

while(condiç~
ao)
comandos

Os comandos são executados até que a condição seja avaliada em false.


Se o valor inicial da condição for falso, os comandos nunca serão executados.
Por exemplo,

int i = 0;
while(i<=100)
{
printf("%d",i++);
}

O comando entre as chaves será executado até que i <= 100 não seja
mais true. O laço seguinte não irá retornar para a tela nenhum valor já que
sua condição inicial é false.

int i = 10;
while(i>100) {
printf("Ainda bem que isso nao vai ser executado!! ");
printf("Eu ia escrever um monte de besteiras aqui!!\n");
printf("%d",i++); }
3.3 Exercı́cios 27

O comando do-while é parecido com o laço while, porém possui a seguinte


forma.

do
comandos
while(condiç~
ao)

A execução dos comandos é continuada até que a condição seja avali-


ada como false. Neste caso, os comandos serão executados pelo menos uma
vez. Os comandos continue e break também podem ser usados dentro dos
comandos while e do-while.

3.3 Exercı́cios

I As linhas de comando seguintes implementadas em C tem a função de


alertar oralmente o usuário de perigosas leituras de temperatura em um sis-
tema de controle (valores em graus Fahrenheit). Este procedimento de aviso
está correto ou incorreto? Se estiver incorreto, explique porque e corrija-o
(não programe!! Faça no papel!!).

if( temp < 97.5 )


{ printf("Temperatura abaixo do normal"); }
else if ( temp > 97.5 )
{ printf("Temperatura normal"); }
else if ( temp > 99.5 )
{ printf("Temperatura levemente alta"); }
else if ( temp > 103.0)
{ printf("Temperatura perigosamente alta"); }

II Use um laço acoplado para escrever o programa que computa a soma

100 X
X 300 q
cos(i2 + j) (3.3)
i=0 j=5
3.3 Exercı́cios 28

III Escreva um programa em C para calcular a função

1
y(x) = ln( ) (3.4)
1−x

para qualquer valor de x pré-especificado pelo usuário, onde ln é o loga-


ritmo natural ( logaritmo na base e). Escreva o programa com um laço while
de forma que o programa repita o cálculo para cada valor legal de x aplicado
ao programa. Quando um valor ilegal de x for avaliado, termine o programa.

IV Escreva um programa em C para calcular y(t) vinda da Eq. 3.5 para


valores de t entre -9 a 9 em passos de 3.



 −3t2 + 5, t≥0
y(t) = (3.5)
3t2 + 5, t>0

Escreva o resultado em tela e depois em um arquivo de saı́da (neste último


caso, crie intervalos de 0.1 entre -9 e 9).

V Faça um programa em C que calcule o resultado numérico e analı́tico da


seguinte série, onde o usuário entra com o número de pontos N do somatório:

N
X 1
Snum =
n=1 n
2

π2
Sana = (3.6)
6

Compare os resultados numéricos fornecidos para diferentes valores de N


com o resultado analı́tico.

VI O método de Newton-Raphson é uma metodologia iterativa de solução


de equações não lineares. Ou seja, um método numérico que encontre o(s)
3.3 Exercı́cios 29

valores de x que façam com que f (x) = 0. Como estamos em um computa-


dor e trabalhando com variáveis de ponto flutuante, deve-se assumir que o
objetivo de x foi atingido quando f (x) é menor que um erro pré-definido. O
método de Newton-Raphson é colocado abaixo.

f (x)
xk+1 = xk − , até que |xk+1 − xk | < erro (3.7)
f ′ (x)

O código abaixo coloca o método de Newton-Raphson da função f (x) =


x
e + sen(x) utilizando um laço while. Modifique o programa de modo que
ele passe a usar o laço for e que o usuário possa entrar com o número de
iterações do método. Existe diferença entre usar um laço sem controle do
número de iterações ou um que use? O que você acha?

#include<math.h>
#include<stdlib.h>

int main()
{
double x, xk, f, df, erro;
int i;
// Funç~
ao f(x) = exp(x) + sen(x)
xk=10.; // Chute inicial
x = 0.; // Tem q inicializar a variável
erro = 1.e-5; // Erro do método
// Loop de converg^encia
i = 1; // 1a Iteraç~
ao
printf("It \t X\n");
while(fabs(xk - x) > erro)
{
x = xk; // Atualiza a variável
f = exp(x) + sin(x); // Calcula funç~ ao
df= exp(x) + cos(x); // Calcula a derivada da funç~ao
xk= x - f/df; // Método de Newton-Raphson
printf("%d \t %g\n", i,xk);
i++;
}
printf("\n Valor convergido!\n");
printf(" x = %g \n", xk);
3.3 Exercı́cios 30

return (0);
}
Capı́tulo 4

Tipos Derivados

4.1 Entrada e Saı́da em Arquivo

A biblioteca <stdio.h> possui funções especı́ficas para tratar a manipulação


de arquivos em C. Assim é possı́vel ler dados vindos de um arquivo ou escrever
os resultados do seu programa em outro (ou o mesmo) arquivo. Um arquivo
de controle deve ser declarado para que possa abrir um arquivo. Por exemplo,

int i,j,k;
FILE *stream;

A variável stream será como referência em todas as manipulações de ar-


quivo. Para manipular mais de um arquivo ao memso tempo, deve-se decla-
rar mais variáveis do tipo F ILE. O comando fopen é usado para abrir um
arquivo.

stream = fopen("NomeArquivo","Regra");

O comando fopen retorna para a variável F ILE um valor indicando se o


processo de abertura do arquivo teve sucesso ou não e ainda atribui à F ILE
as propriedades sobre o arquivo aberto (como as regras de I/O). O nome do
arquivo pode ser colocado diretamente ou usando uma variável de caracteres
4.1 Entrada e Saı́da em Arquivo 32

contendo o nome do arquivo. As regras definem as ações que podem ser


realizadas no arquivo (escrita, leitura, ambas, etc). O exemplo abaixo abre
um arquivo para escrever caracteres no arquivo até que o usuário entre com
um caractere especı́fico.As regras mais usuais para manipulação de arquivos
estão listadas no exemplo abaixo.

/* Arquivos sao uma coletanea de bits armazenados


em algum tipo de midia.

Para abrir um arquivo:

FILE *fopen( const char *filename, const char *mode );

"r" - abre um arquivo somente para leitura. Se o arquivo


nao existe ou nao pode ser encontrado, a funcao
fopen retorna um ponteiro nulo.

"w" - abre um arquivo vazio para escrita. Se o arquivo


existe, seu conteudo e destruido.

"a" - abre um arquivo para escrita, a partir de seu final.


Se o arquivo nao existe, um novo arquivo e criado.

"r+" - abre um arquivo para escrita e leitura


(o arquivo deve existir).

"w+" - abre um arquivo para escrita e leitura.


Se o arquivo existe, seu conteudo e destruido.

"a+" - abre um arquivo para leitura e escrita, a partir de


seu final. Se o arquivo nao existe, um novo arquivo
e criado.
*/

#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

main()
{
4.1 Entrada e Saı́da em Arquivo 33

FILE *fp;
char ch;

fp = fopen("texto.txt","w");

while( (ch = getche()) != 13)


putc(ch, fp);

fclose(fp);
}

Após usar o arquivo, o comando fclose fecha o arquivo. Os comandos


fprintf e fscanf() podem ser usados da mesma forma que os comandos para
tela, contudo é necessário indicar o F ILE nos comandos.

#include<stdio.h>

int main()
{
FILE *arq;

arq = fopen("nome.txt", "w");


fprintf(arq,"Consegui escrever\n");
fclose(arq);
return(0);
}

O exemplo a seguir cria um banco de dados de nome (char), idade (int) e


altura (float) em arquivo definido pelo usuário. A entrada no banco de dados
é finalizada quando o nome contiver nenhum ou apenas 1 caractere.

#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>

int main()
{
4.1 Entrada e Saı́da em Arquivo 34

FILE *fp;
char nomearq[40], nome[40];
int idade;
float altura;

/* Le o nome do arquivo na telea: */


printf("Entre o nome do arquivo: ");
gets(nomearq);

/* Abre o arquivo: */
fp = fopen(nomearq,"w");

/* Le os dados na tela e grava no arquivo: */


do
{
printf("Entre o nome, idade e altura: ");
scanf("%s %d %f", nome, &idade, &altura);
fprintf(fp, "%20s %5d %5.2f\n", nome, idade, altura);
} while(strlen(nome) > 1);

/* Fecha o arquivo: */
fclose(fp);
return(0);
}

O exemplo a seguir lê os dados de nome (char), idade (int) e altura (float)
que estão dentro de um arquivo definido pelo usuário e mostra na tela seus
valores até o final do arquivo (EOF — end of file).

#include <stdio.h>
#include <conio.h>
#include <string.h>

int main()
{
FILE *fp;
char nomearq[40], nome[40];
int idade;
float altura;
4.2 Arrays - Vetores e Matrizes 35

/* Le o nome do arquivo na tela: */


printf("Entre o nome do arquivo: ");
gets(nomearq);

/* Abre o arquivo: */
fp = fopen(nomearq,"r");

/* Le os dados no arquivo e exibe na tela: */


while( fscanf(fp, "%s %d %f", nome, &idade, &altura) != EOF)
printf("%20s %3d %.2f\n", nome,idade,altura);

/* Fecha o arquivo: */
fclose(fp);
return(0);
}

4.2 Arrays - Vetores e Matrizes

Sendo T uma variável declarada, T [n] é um array unidimensional (vetor) de


n elementos com o mesmo tipo de T . Os elementos são indexados a partir
de 0 até n − 1 e são armazenados de forma contı́nua na memória, ou seja,
um elemento após o outro. A Fig. 4.2 mostra como esse procedimento é
realizado na memória RAM.

Figura 4.1: Alocação do vetor v com n elementos na memória.

A definição de arrays é colocada a partir de exemplos.

int i,j;
float vec[3]; // array com 3 floats; vec[0], vec[1], vec[2]
4.2 Arrays - Vetores e Matrizes 36

int st[30]; // array com 30 ints; st[0], ... st[29]

vec[0] = 1.0;
vec[2] = 2.0;
for(i=0;i<30;i++) st[i]=i*i+7;
j = st[29];

Os elementos dos arrays são manipulados como se fossem variáveis esca-


lares e, portanto, todas as operações que vimos até agora também se aplicam
aos arrays. Uma array bidimensional (matriz) com n linhas e m colunas pode
ser definido como T [n][m]. Os ı́ndices das linhas vão de 0 a n − 1 e os das
colunas vão de 0 a m − 1. Por exemplo,

int i,j;
double mt[2][5]; // array 2D c/ 2 linhas e 5 colunas
mt[0][0] = 5.0;
mt[1][4] = 5.0;

for(i=0;i<2;i++)
{ for(j=0;j<5;j++)
{ mt[i][j] = i + j; }
}

Um laço acoplado é muito usado para acessar todos os elementos de uma


matriz. Um array bidimensional pode ser visto como um array unidimen-
sional de arrays unidimensionais. Por exemplo, mt pode ser visto como se
possuı́sse apenas 2 elementos: mt[0] e mt[1], onde mt[0] é um array uni-
dimensional de 5 elementos que representa a primeira linha de mt, e mt[1]
também é um array unidimensional de 5 elementos, mas que representa a
segunda linha de mt. A Fig. 4.2 mostra como um array bidimensional é
alocado na memória.

Figura 4.2: Alocação de matriz n por m na memória.


4.3 Ponteiros e Referência 37

Arrays com três ou mais dimensões podem ser usados de forma similar.
Um array com k dimensões declarado como a[s1 ][s2 ] . . . [sk ] irá alocar espaço
para s1 × s2 × . . . × sk elementos que são alocados contiguamente na memória
linha por linha

Os arrays podem ser inicializados ao serem declarados.

int v[] = {1, 2, 3, 4};


int a[3]= {2, 4, 5};
int u[][3] = { {1, 2, 3} , {4, 5, 8} };

char filename[30] = "output"; // Array de caracteres

Contudo, depois de declarados os valores dos elementos devem ser atri-


buı́dos um por vez.

int v[4];
v = {2, 4, 5, 9}; // Erro!! N~
ao dá para ser assim.
/* A forma correta seria
v[0] = 2;
v[1] = 4;
v[2] = 5;
v[3] = 9; */

Será que é possı́vel inicializar uma matriz do seguinte modo??

int v[2][3] = {1, 2, 0, 4, 0, 0};

4.3 Ponteiros e Referência

Os ponteiros são definidos da seguinte forma ∗T . Um ponteiro ∗T pode


armazenar o endereço ou local na memória de uma variável com o mesmo
tipo do ponteiro. Por exemplo,
4.4 Exercı́cios 38

int *p; // p é um ponteiro de um inteiro

declara a varável p como um ponteiro de inteiro. Este ponteiro pode ser


usado para armazenar o endereço na memória de variáveis inteiras. Sendo v
uma variável, o seu endereço na memória pode ser retornado usando &v. Se
p é uma variável ponteiro, ∗p retorna o valor da variável apontada por p.

int i = 5; // i é int, valor do objeto i é 5. (a)


int* pi=&i; // p é um ponteiro de um inteiro (b)
// e atribui o endereço de i a pi
int j = *pi; // valor da variável apontada por pi (c)
// é atribuı́do a j, j = 5;
double* = &j; // Ilegal!!

Para uma variável de ponteiro p, o valor em ∗p ao qual a variável aponta


pode ser alterado.

double d1 = 2.7, d2 = 3.1; (a)


double* p = &d1; // p aponta para d1, *p = 2.7 (b)
double a = *p; // a = 2.7

p = &d2; // p aponta para d2, *p = 3.1 (c)


double b = *p; // b = 3.1

*p = 5.5; // o valor para qual p aponta é 5.5 (d)


double c = *p; // c = 5.5
double d = d2; // d = 5.5, já que *p = 5.5

4.4 Exercı́cios

I Simples e direto. Qual o objetivo do código abaixo? Não programe!!


Leia e entenda o que está acontecendo!! Repare na identação do programa!!
Tente usar uma identação parecida nos seus programas! Depois, você pode
programar para ver a execução do programa.
4.4 Exercı́cios 39

#include<stdio.h>
#include<stdlib.h>
#include<math.h>

#define n 20

int main()
{
double Tc[n+1], Tf[n+1];
double Tci, Tfi;
double dT;
int i;

//Intervalo de 0 a 100oC
dT = (100. - 0.)/(double)n;

// Cálculo das Temperaturas em Celsius


Tc[0] = 0.;
for(i=1;i<=n;i++)
Tc[i] = Tc[i-1] + dT;

// Cálculo das Temperaturas em Farenheit


printf("TC \t TF\n");
for(i=0;i<=n;i++)
{
Tf[i] = 9.*Tc[i]/5. + 32.;
printf("%.5g \t %.5g\n", Tc[i], Tf[i]);
}

// Entrando com a temperatura a ser interpolada


printf("\n Entre com a temperatura em C : ");
scanf("%le",&Tci);

// Testando limites da tabela


if((Tci < Tc[0]) || (Tci > Tc[n]))
{ printf("Oooopss... Temperatura fora da faixa.\n");
exit(1);
}

// Interpolando a temperatura
for(i=0;i<=n;i++)
4.4 Exercı́cios 40

{
if(Tci <= Tc[i])
{
/*Interpolaç~
ao*/
Tfi = Tf[i-1] + (Tci-Tc[i-1])*(Tf[i]-Tf[i-1])
/(Tc[i]-Tc[i-1]);
break;
}
}

// Saı́da em tela
printf("\n Temperatura em F interpolada : %.7f", Tfi);
Tfi = 9.*Tci/5.+32.;
printf("\n Temperatura em F calculada : %.7f \n", Tfi);
return(0);
}

II O comando #def ine é uma macro em C, ou seja, é uma forma de definir


operações ou variáveis constantes. Veja o exemplo abaixo, onde SQ é uma
macro para calcular o quadrado de uma função.

#include<stdio.h>
#define SQ(x) ((x) * (x))

int main()
{
double x, y;

// faz as operaç~
oes usando SQ

return(0);
}

Será que tem diferença se eu programar SQ como colocado na sequência?

#define SQ(x) (x * x)
4.4 Exercı́cios 41

III Existe a seguinte tabela de relação entre κT /ǫ e ΩD,AB e deseja-se in-


terpolar os valores de ΩD,AB ao entrar com κT /ǫ. Escreva um algoritmo
para a interpolação para ΩD,AB , sem esquecer de verificar a validade do valor
fornecido em tela pelo usuário.

κT /ǫ ΩD,AB
0.30 2.662
0.35 2.476
0.40 2.318
0.45 2.184
0.50 2.066
0.55 1.966
0.60 1.877
0.65 1.798
0.70 1.729
0.75 1.667
0.80 1.612
0.85 1.562
0.90 1.517
0.95 1.476
1.00 1.439

IV Calcule o tempo necessário para calcular a multiplicação de duas ma-


trizes quadradas (n × n), como ilustrado no código abaixo, onde n = 100,
200, 300, . . ., 1900, 2000. Gere as matrizes de forma randômica (valores en-
tre 0 e 1). Grave um arquivo guardando o tamanho n da matriz e o tempo
necessário para efetuar o cálculo (procure na internet a biblioteca necessária
para marcar o tempo em C). Faça dois testes de multiplicação, trocando a
ordem dos laços i—j por j—i. Faça um gráfico com os valores de tempo e
tamanho de matriz para as duas situações. O que acontece e porque?

for(k=0;k<n;k++)
{ for(i=0;i<n;i++)
{ for(j=0;j<n;j++)
{ C[i,j] += A[i,k]*B[k,j]; }
}
}
Capı́tulo 5

Sub-rotinas

5.1 Funções

5.1.1 Declaração e Definição de Função

A função pode considerar uma entrada de dados, realizar algumas instruções


e retornar algum resultado. Os parâmetros de entrada e saı́da devem ter
seus tipos especificados. Por exemplo, uma das funções sqrt() contidas em
<math.h> recebe um double na entrada, calcula sua raiz quadrada e retorna
um double como resultado. Esta função é declarada como

double sqrt(double); // recebe double, retorna double

Quando uma função não retorna nenhum valor, usa-se void para represen-
tar isso. Quando o tipo da saı́da não é especificado, o padrão do compilador
é assumi-la como int. Quando a entrada da função não é especificada indica
que não existe dado de entrada. Alguns exemplos,

int square(int); // recebe int, retorna int


double sum(double,double); // recebe 2 double, retorna 1 double
int f(); // n~
ao recebe nada, retorna int
void g(double,double); // recebe 2 double, n~
ao retorna nada
h(int); // recebe int, retorna int
5.1 Funções 43

Nas declarações acima, as variáveis de entrada (chamadas de parâmetros


ou argumentos) e de saı́da (chamadas de valores de retorno) possuem apenas
tipos, sem descrição dos nomes. Os nomes podem ser incluı́dos para melhor
leitura do código, mas serão ignorados pelo compilador.

int square(int i); // retorna i*i


double pow(double base, double exp);
// retorna a base elevado ao expoente

Todas essas declarações são chamadas de prototypes e só os tipos são


importantes para ele.

Todas as funções devem ser definidas em algum lugar do código uma única
vez e com suas instruções entre chaves.O comando return é usado para que
a função produza um retorno. A definição da função pode servir como sua
declaração também. Por exemplo, a função square() pode ser definida como

int square(int x)
{ return x*x; }

Após a execução da função no programa principal, a próxima linha será


executada.

int i = 5, j, k;
j = square(i); // chamada da funç~ao com i = 5
k = j; // após execuç~
ao, k = j;

5.1.2 Passagem de Argumentos

Existem dois mecanismos de passagem de argumentos em C: passagem por


valor ou por referência. Na passagem por valor, o argumento é avaliado
quando a função é chamada e seu valor se torna o valor do parâmetro durante
a sua execução. O padrão é a passagem por valor, como mostrado no exemplo
abaixo.

int pass_val(int x)
5.1 Funções 44

{
x = x*x;
return(x + 5);
}

int i = 5, j;
j = pass_val(i);
// i = 5, j = 30

Na chamada da função j = pass val(i), o valor do argumento i, que é


5, é passado ao parâmetro x. A função pass val() é iniciada com x = 5,
executa seu bloco de instruções e retorna o valor 30 para j. Então, após a
execução da função, i = 5 e j = 30.

Na passagem por referência, o argumento deve ser uma variável com um


endereço na memória e o argumento é passado para a função como uma
referência, de forma que o parâmetro se torne apenas um “apelido” do argu-
mento e as mudanças ocorridas no parâmetro ocorrem no argumento também.

int pass_val(int& x)
{
x = x*x;
return(x + 5);
}

int i = 5, j;
j = pass_val(i);
// i = 25, j = 30

Na chamada de j = pass val(i), o argumento i é passado como uma


referência ao parâmetro x. Ou seja, as duas variáveis se referem ao mesmo
espaço da memória. Então qualquer mudança realizada em x dentro da
função, irá afetar i. No caso acima, x é alterado dentro da função e assume
valor de 25. Então, após a execução da função, i = 25 e j = 30.

Passagem para ponteiros possuem o mesmo efeito de uma passagem por


referência, já que os dois ponteiros apontam para o mesmo local. Veja o
exemplo de três variações da função swap().

void swap(int& p, int& q) {


5.1 Funções 45

int tmp = p;
p = q;
q = tmp;
}
int i = 2, j = 3;
swap(i, j); // Agora i = 3 e j = 2

-----------------------------------

void swap(int* p, int* q) {


int tmp = *p;
*p = *q;
*q = tmp;
}
int i = 2, j = 3;
swap(&i, &j); // Agora i = 3 e j = 2

-----------------------------------

void swap(int p, int q) {


int tmp = p;
p = q;
q = tmp;
}
int i = 2, j = 3;
swap(i, j); // Agora i = 2 e j = 3

A passagem por valor não muda o valor dos argumentos, enquanto a


passagem por referência ou valor de ponteiros usualmente implica que o valor
dos argumentos serão alterados durante a chamada da função, a não ser que
seja explicitamente especificado para não fazer isso usando a palavra-chave
const.

int g(int val, const int& ref) {


ref = 5; // Erro!!! Compilador vai reclamar!!
val++;
return(ref + val);
}
5.1 Funções 46

O nome de um array é um ponteiro const que aponta para o seu pri-


meiro elemento. Então, os argumentos em forma de array são tratados como
ponteiros e possuem o mesmo efeito de uma passagem por referência.

double h(double* const d, int n) {


int i;
double sum = 0.;
for(i=0;i<n;i++)
sum += d[i];
d[n-1] = 1000.; // Mas pode alterar d!
return(sum);
}

double a[] = {1,2,8,20,-10,30};


double sum, d5;
sum = h(a,6); // Soma dos elementos de a
d5 = a[5]; // d5 = 1000

Ponteiros constantes apontam para locais constantes, ou seja, não é per-


metido alterar o local em que ele aponta. Mas também é possı́vel não permitir
que o array seja modificado.

double gh(const double* const d, int n) {


int i;
double sum = 0.;
for(i=0;i<n;i++)
sum += d[i];
d[n-1] = 1000.; // Péééé!! Erro!!
return(sum);
}

Qualquer função que não seja declarada como void deve retornar um valor
(com exceção de main). Por exemplo,

int square(int& i)
{ i*=i; } // Erro!! Um valor precisa ser retornado!
5.2 Exercı́cios 47

5.2 Exercı́cios

I O que está acontecendo no programa abaixo? Analise a passagem de


parâmetros pelas funções.

#include<stdio.h>
// Prototypes
int swap(int* p, int q, double x, double* y)
{
int i; /* Vc pode definir variáveis na sua
rotina q já foram definidas em main */
double f;

*p = 25; // Valor foi atualizado fora da subrotina


for(i=0;i<q;i++)
{ printf("%g - ", y[i]); y[i] = 0.; }
printf("\n");
f = x;

return(0);
}

int main()
{
int i,j;
double z, v[5]= {0., 1.25, 2.55, 3.5, 4.};

i = 2;
j = 5;
z = 2.5;
// v = {0., 1.25, 2.55, 3.5, 4.};

swap(&i, j, z, v);
printf("%d - %d - %g\n", i, j, z);
for(i=0;i<5;i++) { printf("%g - ", v[i]); }
printf("\n");

return(0);
}
5.2 Exercı́cios 48

II Um algoritmo para encontrar a raiz quadrada de um número real positivo


é:
h i
1 b
x0 = 1, xn+1 = 2
xn + xn
, n = 0, 1, 2, . . .

Escreva uma função que implemente este algoritmo.√ Observe uma rápida
convergência (quadrática) no resultado de xn → b. Compare a acurácia da
função com a função intrı́nseca sqrt() da biblioteca <math.h>.

III Considerando o polinômio de grau n

pn (x) = an xn + an−1 xn−1 + . . . + a1 x + a0

Considere os coeficientes do polinômio de grau 8 como um vetor (a[]={1,


-8, 28, -56, 70, -56, 28, -8, 1}).

a. Escreva uma função que avalie este polinômio.


b. O cálculo direto de um polinômio não é eficiente devido à computação
de cada expoente em x. Um método eficiente é o algoritmo de Horner
(ou multiplicações acopladas). A idéia é reescrever pn (x) da seguinte
forma:
pn (x) = (. . . ((an x + an−1 )x + an−2 )x + . . . + a1 )x + a0
Isto introduz a seguinte notação:
un = an ;
un−1 = un x + an−1 ;
un−2 = un−1 x + an−2 ;
..
.
u1 = u2 x + a1 ;
u0 = u1 x + a0 ;
Assim, pn (x) = u0 . Em princı́pio, parece necessário guardar un , un−1, . . . ,
e u0 em um array. Porém, isso não é preciso já que depois do cálculo
de un−1 o valor de un não é mais necessário. O algoritmo de Horner é
escrito na sequência.

• Inicializa u = an
• for(i = n − 1, n − 2, . . . , 0)
u ← ux + ai ;
5.2 Exercı́cios 49

Construa uma outra função para calcular o polinômio usando o algo-


ritmo de Horner.

c. Compare os resultados fornecidos pelas duas funções.

IV Exercı́cio mais fácil não existe!! Vá no seguinte site:

http://www.gnu.org/software/gsl/manual/gsl-ref toc.html#TOC471

Este é o site das bibliotecas livres da GNU (distribuição livre — de graça!)


escritas em C e C++. São todas as rotinas existentes para cálculo cientı́fico
da GNU e são residentes do sistema operacional linux (contudo, podem fun-
cionar em compiladores windows). Procure na lista de rotinas o método dos
mı́nimos quadrados (Least-Squares Fitting) e estude os exemplos ali coloca-
dos.