Você está na página 1de 43

Programação Científica em

Linguagem C

Luiz Fernando L. R. Silva

Lab. de Termofluidodinâmica
PEQ/COPPE/UFRJ

Curso de Nivelamento

2007
Ementa do Curso
● Introdução à ● Fluxos de Controle
Programação ● Condicionais if, switch
Científica ● Laços for, while
● Tipos e Operadores ● Tipos Derivados
Básicos
● I/O de Arquivos
● Variável inteira,
caractere, pt flutuante
● Arrays (matrizes)
● Operadores
● Ponteiros e
aritiméticos e Referências
relacionais ● Prática e Exercícios
● Prática e Exercícios
Computação Científica

Interpreta o código
Código fonte Executável
(*.c) (*.exe)

Podemos entender!! Alto Nível Linguagem binária!!


Computação Científica
● Aplicações científicas
● Eficiência computacional
● Estruturar qual é o problema e como resolvê-lo
● Montagem do algoritmo (papel!!)
● Análise do problema e suas limitações
● Organizar as idéias
● Verificar as etapas e a estrutura do código
● Programar o código
● Verificar a solução do problema
Computação Científica
● Exemplo 1 2
S =S 0v 0 t − a t
2

● Qual o objetivo?? Calcular S


● Quais as informações necessárias?? S0, v0, a e t
● Existe limitação física no modelo?? t > 0
● Cálculo de S.
● Informa ao usuário o resultado.
Computação Científica
● Primeiro programa em C
/* Este é um simples programa para escrever na tela
uma mensagem de texto.
Programado por: Luiz Fernando L. R. Silva */

#include<stdio.h> // Biblioteca padrão I/O em tela

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

Comentários no código com // ou /* */
● Acesso aos comandos declarando as bibliotecas
● Programa principal dentro do main()
Formato de edição livre (sensível à caixa)
● Escrever na tela: printf("Oi!"); Ponto e vírgula ao final do comando
Tipos e Operadores Básicos
● Tipos de variáveis
z = y + i*x → variáveis devem ser declaradas no código
● Reserva de espaço na memória RAM
Variáveis i e z declaradas e o espaço na memória é reservado

A qtde de memória alocada


depende do tipo da variável.

O programa vai buscar o endereço de memória


da variável e usar o valor alocado no mesmo.
Tipos e Operadores Básicos
● Variáveis Inteiras (2 bytes)
#include<stdio.h>

main()
{
int i; // Declaração de i como inteira
int j,k; /* Declaração 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ção do valor 2 para a variável i


j = i; // Atribuição do valor contido em i para j
k = 2.3; /* Não 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);
}
Tipos e Operadores Básicos
● Variáveis de Caracteres (2 bytes)
#include<stdio.h>
#include<conio.h>

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);
}
Tipos e Operadores Básicos
● Variáveis de Caracteres (2 bytes)
Caracteres Especiais
Caractere Função Específica
\n Nova linha
\t Tabulação horizontal
\v Tabulação vertical
\a Alerta sonoro
\r Cursor volta ao início da linha
\b Backspace

Interpretados como uma ação na saída de tela.

Brinquem à vontade!! :)
Tipos e Operadores Básicos
● Variáveis de Ponto Flutuante
● Precisão simples (6 casas decimais) float
● Precisão dupla (15 casas decimais) double
● Computação científica necessita de maior precisão.
● Uso de precisão dupla é mais usual.
● Detalhes na saída de tela.
#include<stdio.h>

main() 8 espaços de caractere


{ e 3 casas decimais na
float t = 2.50012; saída em tela
printf("Teste de saida: %8.3f\n", t);
}

|||2.500
Tipos e Operadores Básicos
● Variáveis de Ponto Flutuante
#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);
}

Execução do código:
Valor com precisao simples : 3.1415927410125732000 Qual destes valores é
Valor com precisao dupla : 3.1415926535897931000 mais confiável? Porque?
Tipos e Operadores Básicos
● Variáveis Booleanas
● Tipo bool, pode ser true ou false

#include<stdio.h>

main()
{
bool flag = true;
double d = 3.14;

/* Código que pode && - relação de E


eventualmente alterar
o valor de flag */
|| - relação de OU

if(flag == false) Por definição, false tem valor 0


{ d = 2.718; }
}
e true tem valor 1 (diferente de
0)
Tipos e Operadores Básicos
● Operadores Aritméticos (+, -, *, /)
● Importância do tipo de variável
double m = 13/4; /* Operação entre inteiros fornece um inteiro */
double n = 3/4; /* n = 0 */
double y = 3.0/4; /* Na operação, inteiro 4 é convertido para double*/
double x = 3/4.0; /* Idem */

● Precedência das operações (esq. à dir. — *, /, +, -)


int m; m = (5+10+15)/3; /* m = (15 + 15)/3;
m = 1 + 2 + 3*4; /* m = 1 + 2 + 12; m = 30/3;
m = 3 + 12; m = 10; */
m = 15; */ m = ((5+3)*2) - 3; /* m = (8*2) – 3;
m = 1 + (2 + 3)*4; /* m = 1 + (5)*4; m = 16 - 3;
m = 1 + 20; m = 13; */
m = 21; */
m = 5 + 10 + 15/3; /* m = 5 + 10 + 5;
m = 15 + 5;
m = 20; */
Tipos e Operadores Básicos
● Operadores de incremento (++) e decremento (--)
int conta = 0;
int m = 2;
conta++; /* conta = conta + 1; (conta = 1) */
conta--; /* conta = conta - 1; (conta = 0) */
++m; /* m = 3; */
m = conta++; /* m = 0; conta = 1;*/
● Operadores de Atribuição (=, +=, -=, *=, /=)
total += 100; // Significa: total = total + 100;
conta -= 5; // Significa: conta = conta - 5;
metade /=2; // Significa: metade = metade/2;

● Operadores Relacionais (>,>=,<,<=,==,!=)


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ão é 0
if(b != c) // Se b não é igual a c, x = 17
x = 17;
Tipos e Operadores Básicos
● Operadores Relacionais (cont.)
if(b) // Se b é true, então faça x = 19
x = 19;
if(!b) // Se b é false, então faça x = 221
x = 221;

CUIDADO!!
int m = 7;
bool b, c;
Sempre avaliado como true!!
b = 3 < m < 9; // b = true 1 (true) < 9
m = 20; 0 (false) < 9
c = 3 < m < 9; // c = true

● Operadores Lógicos (&&, ||)


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

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


Fluxos de Controle
● Condicionais if-else if
if(condição)
{ Executado se a condição for true
// Executo um ou mais comandos aqui
}

if(condição)
{ // Executo um ou mais comandos aqui } Execução do else somente se
else
{ // Executo outros comandos aqui }
a condição for false

if(condição1)
{ // Executo bloco 1 de comandos }
Se a primeira condição
else if(condição2) for satisfeita, os testes de
{ // Executo bloco 2 de comandos } condição seguintes (e
else if(condição3)
{ // Executo bloco 3 de comandos } seus comandos) serão
else ignorados.
{ // Executo bloco 4 de comandos } Preste atenção!!
Fluxos de Controle
● Condicionais if-else if
// Usuário entra com T (em K)
printf("Entre com a temperatura da agua (K)");
scanf("%16le",&T); O que vai ler? Qual variável?
printf("\n");

if(T <= 273.15) scanf("%16le", &T);


{ // Gelo! - comando para ler dados em
// Calcula parâmetros para gelo tela
}
else if( (T >= 273.15) && (T <= 373.15) )
{ // Líquido!
// Calcula parâmetros para água líquida
}
else
{ // Só sobrou o vapor (fora o plasma!!)
// Calcula parâmetros para vapor d’água
}
Fluxos de Controle
● Condicionais switch
● Usualmente aplicado a casos de múltipla escolha
char choice = 'B';
switch(choice)
{
case 'A':
// Gelo! Calcula parâmetros para gelo
break;
case 'B':
// Líquido! Calcula parâmetros para água líquida
break;
default:
// Só sobrou o vapor (fora o plasma!!)
// Calcula parâmetros para vapor d’água
};
Fluxos de Controle
● Laços for
● Muito útil e aplicado a passos iterativos do algoritmo
● Utiliza uma variável inteira como contador
for(inicia;condição;expressão)
int n = 10;
int i;

for(i=0;i<n;i++)
{
// Processo será repetido n vezes
// Valor de i é atualizado a cada iteração
printf(“%d\n”,i);
}

Então, o que acontece int i;


for(i=3;i>5;i*=2)
nesse caso? { printf(“%d\n”,i); }
Fluxos de Controle
● Laços for
● Comandos break e continue
int i;
for(i=3;i<50;i*=2)
break termina o laço
{ if(i == 6) continue; continue passa para a próxima iteração
printf(“%d\n”,i);
if(i == 24) break;
}
Então, o que acontece nesse exemplo?

3 Vamos fazer o primeiro chinezinho!!


12 Faça cada comando da iteração no
24 papel para conferir o algoritmo e o
resultado

Cuidado!! Nunca altere o valor do contador dentro do laço


Fluxos de Controle
● Laços for
● Laços combinados
int i,j;
99 500
double a = 0.;
for(i=0;i<100;i++) ∑∑ i− j
i=0 j =200
{ for(j=200;j<=500;j++)
a+=i-j; // int são convertidos para double
}

int i,j;
long double b = 5.; 4 27
2
for(i=1;i<5;i++) 5∏ ∏ i j 
for(j=27;j>=-3;j--) i=1 j=−3
b*=i+j*j;

Faça o chinezinho destes códigos para conferir as expressões


Fluxos de Controle
● Laços for
● Processos iterativos ou repetitivos
Fluxos de Controle
● Laços while
int i,j;
int i = 0;
while(i<=100)
while(condição) {
comandos printf("%d",i++);
}
A condição é testada antes de executar os comandos

do
comandos
while(condição)

Os comandos são executados e depois pode


existir ou não a iteração dependendo da condição
Tipos Derivados
● Entrada e saída de arquivos
● Biblioteca <stdio.h> contém funções para manipular arquivos
● Variável de controle da manipulação deve ser declarada para
cada arquivo aberto
Apontador de um endereço na memória
int i,j;
FILE *stream;

stream = fopen(“NomeArquivo”,“Regra”);
fclose(stream);

As regras definem as ações que podem ser realizadas


no arquivo (escrita, leitura, ambas, etc)
Tipos Derivados
● Regras para abrir arquivos

Abre um arquivo somente para leitura. Se o arquivo não existe ou não


“r” pode ser encontrado, a função fopen retorna um ponteiro nulo.
Abre um arquivo vazio para escrita. Se o arquivo existe, seu conteúdo é
“w” destruído.
Abre um arquivo para escrita, a partir de seu final. Se o arquivo não
“a” existe, um novo arquivo é criado.
“r+” Abre um arquivo para escrita e leitura (o arquivo deve existir).
Abre um arquivo para escrita e leitura. Se o arquivo existe, seu conteú-
“w+” do é destruído.
Abre um arquivo para leitura e escrita, a partir de seu final. Se o ar-
“a+” quivo não existe, um novo arquivo é criado.
Tipos Derivados
● Entrada e saída de arquivos

#include<stdio.h> fprintf() e fscanf() são


usados da mesma maneira, com
int main()
{
I/O para o arquivo
FILE *arq;
arq = fopen("nome.txt", "w");
fprintf(arq,"Consegui escrever\n");
fclose(arq);
return(0);
}
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>

int main()
{
FILE *fp;
char nomearq[40], nome[40];
int idade;
float altura;
/* Le o nome do arquivo na tela: */
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);
}
Tipos Derivados
● 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.
● Elementos são indexados de 0 até n-1 continuamente na
memória.
int n = 10;
double v[n];
Tipos Derivados
● Arrays – Vetores e Matrizes
int i,j;
float vec[3]; // array com 3 floats; vec[0], vec[1], vec[2]
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];
O que acontece com vec[1]?
Foi alocado, mas não foi inicializado...
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; }
}
Tipos Derivados
● Arrays – Vetores e Matrizes
● Podem ser inicializados na declaração
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

● Depois de declarados, os arrays devem ser inicializados


elemento a elemento
int v[4];
v = {2, 4, 5, 9}; // Erro!! Não dá para ser assim.

/* A forma correta seria


v[0] = 2;
v[1] = 4;
v[2] = 5;
v[3] = 9; */
Tipos Derivados
● Ponteiros e Referência
● Ponteiro pode armazenar o endereço ou local na memória de
uma variável com o mesmo tipo de ponteiro.
● Referência retorna o endereço da memória de uma variável
int *p; // p é um ponteiro de um inteiro
&i; // retorna o endereço da memória onde i está alocado

... i j ...

*pi
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!!
Tipos Derivados
● Ponteiros e Referência
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

(a) .. d1 d2 .. (b) .. d d2 ..
. . . 1 .
*p

(c) .. d d2 .. (d) .. d d2 ..
. 1 . . 1 .
*p *p
5.1
Funções
● Evitar duplicidade de código
● Incorporar códigos de terceiros
● Flexibilidade de uso e legibilidade do código

Passagem de parâmetros
do programa principal
para a subrotina (e vice-
Programa versa) Subrotina
Principal
Considere a subrotina como um
outro mundo fora do programa
principal e você deve dizer a ela
quais variáveis ela deve conhecer

Arquivo fonte 1 Arquivo fonte 2


Funções
● Declaração e Definição
● Considera-se parâmetros de entrada e saída
double sqrt(double); // recebe double, retorna double
● Pode-se considerar uma função sem retorno algum (void) e
entrada não especificada.
int square(int); // recebe int, retorna int
double sum(double,double); // recebe 2 double, retorna 1 double
int f(); // não recebe nada, retorna int
void g(double,double); // recebe 2 double, não retorna nada

/* Os nomes dos parâmetros tambem podem ser incluídos */


int square(int i); // retorna i*i
double pow(double base, double exp);
// retorna a base elevado ao expoente

Estas declarações são chamadas de prototypes


Funções
● Declaração e Definição
● Os prototypes podem conter apenas a declaração da subrotina
(com ;) ou a declaração e seu conteúdo (sem ;)
int square(int i)
{ return(i*i); }
A declaração deve ser
double pow(double base, int exp) feita antes de main e
{int i; depois de include
double temp = 1.0;
for(i=0;i<exp;i++) {temp*=base;}
return(temp);
}
Funções
● Passagem de argumentos
● Passagem por valor (padrão)
int pass_val(int x) // No programa principal
{ x = x*x; int i = 5, j;
return(x + 5); j = pass_val(i);
} // i = 5, j = 30
Valor do parâmetro i é passado para x,
que executa a função e retorna um valor
● Passagem por referência
int pass_val(int &x) // No programa principal
{ x = x*x; int i = 5, j;
return(x + 5); j = pass_val(i);
} // i = 25, j = 30

Endereço (e consquentemente o valor) do parâmetro i é


passado para x, que pode modificar o valor armazenado no
Funções
● Passagem de argumentos
● Uso de ponteiros
void swap(int& p, int& q) { // Programa principal
int tmp = p; int i = 2, j = 3;
p = q; swap(i, j);
q = tmp; // Agora i = 3 e j = 2
}

void swap(int* p, int* q) { // Programa principal


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

Este tipo de abordagem usualmente implica que os valores


dos argumentos serão alterados durante a chamada da
função
Funções
● Uso de const
● Não permite que o valor do parâmetro seja alterado

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


ref = 5; // Erro!!! Compilador vai reclamar!!
val++;
return(ref + val);
}
Funções
● Aplicação em arrays
● Array é um ponteiro const que aponta para seu primeiro
elemento
● A passagem de arrays são tratadas como ponteiros e possuem
passagem por referência
double h(double* const d, int n) {
int i;
Uso de const no ponteiro -
double sum = 0.; não pode alterar o local para
for(i=0;i<n;i++) o qual ele aponta
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
Funções
● Aplicação em arrays

double h(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);
}

Uso de const na variável e no ponteiro -


não pode alterar nada!! Só usar a variável!
Funções
● Subrotinas gratuitas para programação científica
● Netlib (www.netlib.org)
● Banco de dados de qualquer tipo de programa/rotinas em várias
linguagens (científicas ou não)
● Numerical Recipes (www.nr.com)
● Algoritmos numéricos em C, C++ e Fortran
● The Free Country (www.thefreecountry.com/sourcecode/mathematics.shtml)
● Site com várias rotinas numéricas, matemáticas e estatísticas
● GSL (www.gnu.org/software/gsl/)
● GNU Scientific Library contém várias rotinas de domínio público para
aplicações científicas
● DASSLC (www.enq.ufrgs.br/enqlib/numeric/)
● Rotina gratuita para resolução de sistemas algébricos diferenciais
Funções
● GNU Scientific Library (licensa GNU GPL)
● Rotinas programadas com alta eficiência (otimizadas)
● Alta frequência de atualização e incorporação de novos
algortimos
● Rotinas documentadas e grupos de discussões