Você está na página 1de 128

Fortran 90/95

Joo Batista Aparecido jbaparec@dem.feis.unesp.br

Dezembro 1999

PREFCIO Comecei a programar em Fortran em 1977 ainda no curso de graduao em Engenharia Mecnica. O computador no caso tratava-se de um IBM-1130 com 8k de memria RAM. Desde ento tenho programado em Fortran. As pessoas no Brasil que programam usando Fortran em geral ainda praticam o padro 77. Este texto procura dar uma viso geral do atual padro da linguagem Fortran, o padro 90 j amplamente difundido nos compiladores disponveis dos principais fabricantes, tal como Microsoft, Lahey e Digital, bem como do padro 95 ainda pouco difundido.

PREFCIO CAPTULO 1 - INTRODUO LINGUAGEM FORTRAN 1.1 - O COMPUTADOR 1.1.1 - A CPU


1.1.2 Memria principal e memria secundria. 1.1.3 Unidades de entrada e sada de dados. 1.2 - Representao de dados em um computador. 1.2.1 - O sistema binrio de nmeros. 1.2.2. - Tipos de dados que o Fortran 90 armazena na memria. 1.2.2.1. Dados inteiros 1.2.2.2. Dados reais 1.2.2.3. Dados alfanumricos 1.3 Linguagens de computador. 1.4 - Histria do Fortran

2 9 9 9
10 10 10 10 11 11 11 12 12 13

CAPTULO 2 - ELEMENTOS BSICOS DO FORTRAN 90


2.1 O conjunto de caracteres do Fortran 90. 2.2 A estrutura de uma declarao Fortran 90. 2.2.1 Formato livre do cdigo fonte. 2.3 A estrutura de um programa Fortran 90. 2.3.1 Estilo de programao 2.3.2. Primeiro programa em Fortran 90 2.4 Constantes e Variveis 2.4.1. Constantes e variveis inteiras 2.4.2. Constantes e variveis reais 2.4.3. Constantes e variveis lgicas 2.4.4. Constantes e variveis alfanumricas 2.4.5. Definindo variveis inteiras ou reais por Default ou explicitamente. 2.4.6. Mantendo as constantes consistentes em um programa

14
14 14 14 15 15 15 16 16 17 17 17 18 18

2.5 Declarao de atribuio e clculos aritmticos 2.5.1 Aritmtica inteira 2.5.2 Aritmtica real 2.5.3. Hierarquia das operaes 2.5.4 Aritmtica mista 2.5.5. Aritmtica mista e exponenciao 2.6 Declarao de atribuio e clculos lgicos 2.6.1 Operadores relacionais lgicos 2.6.2 Operadores combinatoriais lgicos 2.7 Declarao de atribuio e variveis do tipo caracter 2.7.1 Sub-especificao de variveis do tipo caracter 2.7.2 Operador de concatenao 2.7.3. Operadores relacionais lgicos com variveis do tipo caracter 2.8 Funes intrnsecas 2.9 Declaraes de entrada e sada de dados 2.10 Inicializao de variveis 2.11 Declarao IMPLICIT NONE

18 19 19 19 20 20 21 21 22 22 23 23 23 24 25 25 26

CAPTULO 3 - ESTRUTURAS DE CONTROLE


3.1 Estruturas de controle: bifurcaes 3.1.1. Estrutura do tipo bloco IF 3.1.2. Estrutura do tipo ELSE e ELSE IF 3.1.3. Estrutura do tipo IF, nomeado 3.1.4 Declarao IF lgico 3.1.5 A estrutura CASE 3.2 Estruturas de controle: laos 3.2.1. O lao com terminao indefinida 3.2.2. Outra implementao do lao com terminao indefinida 3.2.3. O lao com terminao bem definida 3.2.4. As declaraes CYCLE e EXIT

27
27 27 27 28 29 29 30 30 31 31 32

3.2.5 Laos nomeados 3.2.6. Ninho de laos

33 34

CAPTULO 4 - CONCEITOS BSICOS DE ENTRADA E SADA DE DADOS 35


4.1 Descritores de formato 4.1.1 Sada de dados inteiros: O descritor I 4.1.2 Sada de dados reais: O descritor F 4.1.3 Sada de dados reais: O descritor E 4.1.4 Sada de dados reais: O descritor ES 4.1.5 Sada de dados lgicos: O descritor L 4.1.6 Sada de dados caracter: O descritor A 4.1.7 Posicionamento horizontal: Os descritores X e T 4.1.8 Trocando as linhas de sada: O descritor slash (/) 4.1.9. Declaraes READ formatadas 4.2. Introduo a arquivos e processamento de arquivos 4.2.1 A declarao OPEN 4.2.2 A declarao CLOSE 4.2.3 READs e WRITEs a arquivos em disco 4.2.4 O uso da especificao IOSTAT= nas declaraes READ 4.2.5 Posicionamento em arquivos 35 35 36 36 37 37 38 38 39 40 40 41 42 42 42 43

CAPTULO 5 - ARRAYS
5.1 Declarando arrays 5.2 Usando elementos de array em declaraes Fortran 5.2.1 Usando elementos de array em declaraes Fortran 5.2.2 Inicializando elementos de array 5.2.3 Modificando a faixa de subscritos de um array 5.2.4 Subscritos de array fora da faixa de definio 5.2.5 O uso de constantes com declarao de arrays 5.3 Usando arrays completos e sub-conjuntos de arrays

44
44 45 45 45 46 46 47 47

5.3.1 Operaes com arrays completos 5.3.2 Definindo e operando com subconjuntos de arrays 5.3.3 Reformatando arrays 5.4 Usando funes intrnsecas Fortran com arrays 5.4.1 Funes intrnsecas elementares 5.4.2 Funes intrnsecas de questionamento 5.4.3 Funes intrnsecas de transformao 5.5 Atribuio de arrays com mscara: a estrutura WHERE 5.5.1 A estrutura WHERE do Fortran 90 5.5.2 A estrutura WHERE do Fortran 95 5.5.3 A declarao WHERE 5.6. A estrutura FORALL 5.6.1 A forma da estrutura FORALL 5.7. Arrays alocveis

47 49 50 51 51 51 52 53 53 53 54 54 54 54

CAPTULO 6 - FUNES E SUBROTINAS


6.1 Subrotinas 6.1.1 O atributo INTENT 6.1.2 Passando arrays para subrotinas 6.1.3 Passando variveis caracter para subrotinas 6.2 A declarao e o atributo SAVE 6.3 Arrays automticos 6.4 Compartilhando dados usando mdulos (modules) 6.5 Sub-programas mdulo 6.5.1 Usando mdulos para criar interfaces explicitas 6.5.2 Passando arrays para subrotinas usando mdulos e arrays de forma assumida. 6.6 Funes Fortran 90/95 6.7 Funes puras (pure functions) e funes elementares (elemental functions) 6.7.1 Funes puras (pure functions) 6.7.2 Funes elementares (elemental functions)

57
58 60 62 64 65 65 66 67 68 69 71 72 72 73

6.8 Passando subprogramas como argumentos para outros subprogramas 6.8.1 Passando funes como argumento

73 73

CAPTULO 7 - OUTROS TIPOS DE DADOS


7.1 Outros tamanhos de dados para variveis do tipo real 7.1.1 Tamanhos (kinds) de dados para variveis e constantes do tipo real 7.1.3 Definindo a preciso de uma varivel, independente do processador 7.1.4. Preciso dupla em funes intrnsecas 7.2 Outros tamanhos (kinds) de dados para variveis inteiras 7.3 O tipo de dados COMPLEX 7.3.1 Aritmtica mista com complexos 7.3.2 Utilizando operadores relacionais com complexos 7.4 Tipos de dados derivados 7.4.1 Acessando elementos de uma estrutura

75
75 75 77 78 78 78 79 79 79 80

CAPTULO 8 - ASPECTOS AVANADOS DE FUNES, SUBROTINAS E MDULOS. 82


8.1 Subprogramas internos 8.2 Escopo de variveis e constantes 8.3 Subprogramas recursivos 8.4 Argumentos com palavras-chave e argumentos opcionais. 8.5 Interfaces para subprogramas e blocos de interfaces 8.5.1. Criando blocos de interface 8.6 Subprogramas genricos 8.6.1 Subprogramas genricos definidas pelo programador 8.6.2 Interfaces genricas para subprogramas em mdulos 8.7 Estendendo o Fortran com declaraes e operadores definidos pelo programador 8.8 Restringindo acesso ao contedo de um mdulo 8.9 Opes avanadas da declarao USE 82 82 84 85 88 88 90 90 92 93 98 98

CAPTULO 9 - PONTEIROS E ESTRUTURAS DINMICAS DE DADOS 100


9.1 Ponteiros e alvos 100

9.1.1 Declarao de atribuio de ponteiro 9.1.2 Situao de um associao ponteiro-alvo 9.2 Usando ponteiros em declaraes de atribuio 9.3 Alocao dinmica de memria usando ponteiros 9.4 Usando ponteiros com estruturas derivadas de dados

101 101 102 103 104

CAPTULO 10 - PONTEIROS E ESTRUTURAS DINMICAS DE DADOS114


10.1 Ponteiros e alvos 10.1.1 Declarao de atribuio de ponteiro 10.1.2 Situao de um associao ponteiro-alvo 10.2 Usando ponteiros em declaraes de atribuio 10.3 Alocao dinmica de memria usando ponteiros 10.4 Usando ponteiros com estruturas derivadas de dados 114 115 115 116 117 118

REFERNCIAS

128

Captulo 1 - Introduo linguagem Fortran


O ser humano sempre esteve envolvido com as questes de contar, calcular, e armazenar informaes. Ao longo do tempo desenvolveu-se os mais variados objetos para atingir estes objetivos. At uns vinte anos atrs utilizava-se nos cursos de engenharia as rguas de clculo. Uma das mais importantes invenes deste sculo foi computador eletrnico que realiza estes objetivos com razovel preciso e com bastante velocidade. Um programa (ou conjunto de programas) armazenados na memria do computador diz a ele qual seqncia de clculo necessria e em quais dados executar as instrues. 1.1 - O computador Na Figura 1.1 mostra-se um diagrama de bloco de um computador tpico, constitudo pela Unidade de Processamento Central (CPU-Central Processing Unit), pela memria principal, pela memria secundria, e pelas unidades de entrada e sada de dados.
Unidade de processamento central

Unidades de entrada de dados

Memria interna (registros)

Unidades de sada de dados

Unidade de controle

Memria principal Unidade de lgica aritmtica

Memria secundria

Figura 1.1- Esquema de um computador. 1.1.1 - A CPU A parte principal do computador a Unidade de Processamento Central (CPU-Central Processing Unit). Ela constituda pela Unidade de Controle, que controle as outras partes do computador; pela Unidade de Lgica Aritmtica (ALU-Arithmetic Logic Unit); e pelas memrias internas (tambm conhecidas como registros). A CPU interpreta as instrues dos programas; pega os dados nas unidades de entrada de dados ou nas memrias principal e secundria e armazena-os nos registros; efetuas as instrues aritmticas na ALU e armazena o resultado nos registros. Depois

Forttran 90//95 For ran 90 95


que estes dados no so mais necessrios na CPU eles so armazenados na memria principal ou secundria. 1.1.2 Memria principal e memria secundria. Como dito anteriormente o computador tem dois tipos principais de memria: a principal e a secundria. A memria principal constituda em geral de chips semicondutores e em geral a mais rpida e a mais cara. Uma das caractersticas deste tipo de memria sua volatilidade, sendo usada para armazenar programas e dados a serem processados. As memrias secundrias so mais lentas e mais baratas, alguns exemplos so: disquetes, fitas magnticas e discos rgidos. So em geral no-volteis e podem armazenar, permanentemente, grandes massas de dados a um custo mais baixo que as memrias principais. 1.1.3 Unidades de entrada e sada de dados. Dados so introduzidos no computador via teclado, microfone, unidades de disco, unidades de fita, scanners, e placas de aquisio de dados. Os dados podem sair do computador via impressora, tela, plotter, e auto-falante. 1.2 - Representao de dados em um computador. A memria de um computador constituda de chaves que podem estar apenas nos estados ligado (on) e desligado (off). Cada chave ento representa apenas um dgito binrio, ligado ou desligado. Esta a menor informao que um computador representa, conhecida como bit. Se interpretarmos o estado ligado como sendo o nmero um e o estado desligado como sendo o nmero zero, podemos assim representar os nmeros zero e um. Da conclumos que a representao interna do computador binria. Em geral precisamos trabalhar com nmeros que no sejam apenas o zero e o um. Pode-se resolver esta questo juntando vrios bits, podendo representar nmeros maiores em um sistema binrio. O menor grupo de bits chamado de byte. Um byte um grupo de 8 bits agrupados em conjunto para representar um nmero binrio. O grupo de bits maior que o byte a palavra (word), que pode usar 2, 4, 8 ou mais bytes consecutivos para representar um nmero binrio. O tamanho das palavras variam de computador para computador. 1.2.1 - O sistema binrio de nmeros. Pode-se representar nmeros em diferentes bases, por exemplo o nmero 354 no sistema decimal pode ser escrito como segue na bases binria 35410 = 3 10 2 + 5 101 + 4 10 0 = = 1 2 8 + 0 2 7 + 1 2 6 + 1 2 5 + 0 2 4 + 0 2 3 + 0 2 2 + 1 21 + 0 2 0 O maior nmero que pode ser representado em um byte 11111111 que corresponde ao nmero 255 (28-1)na base 10, que em geral atravs de translaes leva a nmeros variando de 128 a +127. Da mesma forma com dois bytes pode-se

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

10

Forttran 90//95 For ran 90 95


representar nmeros de 32.768 a +32.767 (65.535 = 216-1). At o momento discutimos apenas o armazenamento na memria de nmeros inteiros, na prxima seo veremos outros tipo de dados. Quando entramos via teclado com um nmero decimal em um programa Fortran, este nmero ento convertido para o sistema de armazenamento natural do computador, o binrio. Quando o computador vai imprimir um nmero, ele converte o nmero que est no sistema binrio para decimal e imprime. Como o sistema decimal pode ser convertido em binrio, outros sistemas de nmeros tambm podem ser convertidos para binrios e vice-versa. Os outros sistemas de nmeros que o Fortran 90 suporta so o Octal (base 8) e o Hexadecimal (base 16). Desta forma o nmero 77 decimal pode ser representado da seguinte maneira nas diferentes bases

7710 = 10011012 = 1158 = 4 D16 .


1.2.2. - Tipos de dados que o Fortran 90 armazena na memria.

Manipulando-se a quantidade de bytes que se utiliza e o tipo de uso feito destes bytes em uma palavra pode-se armazenar na memria outros tipo de dados que no apenas inteiros. Os tipo comuns de dados que se pode armazenar so: inteiros, reais, alfanumricos, complexos e lgicos. No momento trataremos apenas dos inteiros, reais e alfanumricos. Existe tambm os tipos de dados derivados a partir destes cinco tipos fundamentais, mais adiante voltaremos aos tipos derivados ou especiais.
1.2.2.1. Dados inteiros

O tipo de dado inteiro consiste de uma quantidade de inteiros positivos, uma de inteiros negativos e o zero. O tamanho da palavra para armazenar inteiros varia de processador a processador (processador = computador + sistema operacional + compilador), mas os tamanho mais comuns so palavras de 1, 2, 4 e 8 bytes ou 8, 16, 32 e 64 bits. Em geral o maior e o menor inteiros representados so fornecidos por: maior inteiro = 2n-1-1, e menor inteiro = -2n-1, onde n a quantidade de bits da palavra.
1.2.2.2. Dados reais

Os nmeros inteiros no conseguem representar nmeros racionais ou irracionais. Tambm no representam nmeros muito grandes ou muito pequenos. Para contornar estas duas deficincias de representao de nmeros no inteiros na forma inteira, os processadores incluem o tipo de dado real ou floating-point. O tipo real armazena dados em um forma de notao cientfica. As duas partes de um nmero expresso em notao cientfica so chamados de mantissa e expoente. Por exemplo no nmero 5.7651010, a mantissa 5.765 e o expoente 8. No computador o sistema semelhante, apenas a base numrica binria ao invs de decimal. Os nmeros reais mais comuns nos processadores o real de 4 bytes ou 32 bits, dividido em duas partes, a mantissa com 24 bits e o expoente com 8 bits. A mantissa contm um nmero entre 1 e +1 e o expoente contm uma potncia de 2 suficientes para recuperar o valor do nmero representado, como segue

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

11

Forttran 90//95 For ran 90 95


valor do numero = mantissa 2 exp oente .

Os dados do tipo real so caracterizados por dois indicadores: a preciso e a faixa. Preciso a quantidade dgitos significativos que so preservados em um nmero, e faixa a diferena entre o maior e o menor nmero que pode ser representado. A preciso depende do nmero de bits na mantissa e a faixa depende do nmero de bits no expoente. Uma mantissa com 24 bits pode armazenas nmeros de -223 e +223, aproximadamente. Convertendo para a base decimal tem nmeros de 107 a +107, ou seja com este tipo de representao a preciso que se obtm de aproximadamente 7 dgitos decimais. Um expoente com 8 bits pode representar nmeros entre 2-128 e 2128, ou seja entre 10-38 e 10+38. Note que o tipo real de dados pode representar nmeros muito maiores ou muito menores que o tipo inteiro pode, porm no caso de usar 4 bytes tem-se apenas uma preciso de 7 dgitos decimais. Neste caso impossvel distinguir qualquer diferena entre os nmeros 1234567.1 e o 1234567.2. Isto ocorre porque quando um nmero com mais de 7 dgitos decimais armazenado em um tipo real com mantissa de 24 bits, o dgitos alm do stimo estaro perdidos para sempre. Este o erro de arredondamento (round-off error).
1.2.2.3. Dados alfanumricos

O tipo de dado alfanumrico (character) consiste de caracteres e smbolos. Um sistema tpico para representao de uma linguagem ocidental deve incluir os seguintes smbolos: 26 letras maisculas de A a Z; 26 letras minsculas de a a z; 10 nmeros 0 a 9; Smbolos variados, tais como: !@#$%^&*(){}[],<>?/~; Smbolos especiais requeridos pela linguagem, tais como: . Uma vez que o nmero de caracteres usando por uma linguagem ocidental no excede a 256 (28) pode-se utilizar um byte para armazenar qualquer caracter. Assim para armazenar 10 caracteres sero necessrio 10 bytes, e assim por diante. O conjunto de caracteres mais utilizado no mundo ocidental o ASCII (American Standard Code for Information Interchange). Existem outros sistemas semelhantes ao ASCII, tal como o BRASCII. Como algumas linguagens tal como o Japons e o Chins tm milhares de smbolos (aproximadamente 4000) faz-se necessrio um sistema com mais caracteres, assim surgiu o Unicode que armazena cada caracter em 2 bytes podendo assim abrigar 65536 (216) caracteres. Os primeiros 128 caracteres do Unicode so os mesmos do ASCII, ficando os blocos restantes designados para as diferentes linguagens: Japons, Chins, Portugus, Hebreu, rabe, etc.
1.3 Linguagens de computador.

Existem vrias camadas de linguagens entre o usurio e a mquina propriamente dita. Duas delas mais familiares ao usurio so o sistema operacional e o compilador. Vamos nos ater aqui ao compilador, mas no nos esqueamos que existem vrias outras camadas por trs at chegar mquina fsica propriamente dita. Isto necessrio porque a mquina s entende a linguagem dos fenmenos fsicos sobre os quais o computador baseia-se, enquanto os usurios humanos preferem uma
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
12

Forttran 90//95 For ran 90 95


linguagem a mais prxima possvel de sua maneira de se comunicar. Assim quase todas as linguagens de programao so de alto nvel. Em outras palavras, quase todas apresentam recursos de alto nvel e algumas apresentam alguns recursos de baixo nvel. A linguagem Fortran, consequentemente de alto nvel, com poucos recursos de baixo nvel. Existem trs famlias de linguagens computacionais: as objeto orientadas (SmallTalk), as rotina orientadas (Fortran, Pascal e C), e as mistas (C++). Hoje, as linguagens dentro de cada grupo so muito parecidas na sua concepo geral, diferindo em detalhes e na sintaxe. O Fortran existe a mais de 50 anos e uma linguagem projetada para cmputos cientficos, tendo assim uma larga tradio em resolver problemas de engenharia e cincias aplicadas em geral. Existem excelentes bibliotecas de rotinas cientficas escritas para esta linguagem.
1.4 - Histria do Fortran

O Fortran o av de todas as linguagens de computao. O nome FORTRAN derivado de FORmula TRANslation. O Fortran foi criado originalmente na IBM entre 1954 e 1957. Este primeiro Fortran foi o Fortran I. Logo aps em 1958 a IBM lanou o Fortran II. A evoluo do Fortran continuou rapidamente at o aparecimento do Fortran IV em 1962. Esta verso do Fortran tinha muitas melhorias e tornou-se o standard da linguagem por 15 anos. Em 1966 o Fortran IV foi a primeira linguagem a ser padronizada pela ANSI (American National Standard Institute), sendo ento renomeado para Fortran 66. Em 1977 o padro Fortran tem uma nova grande melhoria com sintaxe e comandos que facilitavam a produo de programas estruturados. Este padro foi um sucesso e passou a ser conhecido como Fortran 77, em uso corrente at o momento. Novamente em 1990 o Fortran passa por uma grande mudana. O Fortran 90 um superconjunto do Fortran 77 e estende a linguagens em novas direes. Entre as novidades includas no Fortran 90 esto: livre formato do cdigo fonte, novas estruturas de arrays; operao com arrays completos; tipos de dados derivados; e interfaces explicitas. O Fortran 90 j suportado pelos principais compiladores disponveis no mercado (Microsoft, Lahey e Digital). Outro grande aspecto do Fortran 90 suportar processamento paralelo SIMD (Single Instruction Multiple Data). Em 1995 surgiu uma pequena atualizao do Fortran 90, o Fortran 95. Ainda no popular no Brasil (1999). Vamos nos dedicar prioritariamente ao Fortran 90, porm mostrando os aditivos feitos no Fortran 95.

Obsoleto

A forma fixa do cdigo fonte foi declarada obsolescente no Fortran 95, significando que candidata a ser deletada do padro nas futuras verses do Fortran.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

13

Forttran 90//95 For ran 90 95


Captulo 2 - Elementos Bsicos do Fortran 90
Neste captulo apresenta-se os elementos bsicos do Fortran 90. Ao final ser possvel criar uma programa computacional para efetuar algum clculo simples.
2.1 O conjunto de caracteres do Fortran 90.

Cada linguagem tem um conjunto de caracteres bsicos. Tal como o Chins e o Portugus, o Fortran 90 possui um conjunto bem definido de smbolos que podem ser usados. Outros smbolos no so permitidos e causar erro de sintaxe no programa. O alfabeto do Fortran 90 (Fortran character set) consiste de 86 smbolos mostrados na Tabela 2.1. Tabela 2.1 Conjunto de smbolos do Fortran 90. Quantidade de smbolos Descrio Smbolo 26 letras maisculas A-Z 26 letras minsculas a-z 10 nmeros 0-9 1 underscore _ 5 smbolos aritmticos +-*/** 17 smbolos variados ( ) . = , $ : ! % & ; < > ? branco
2.2 A estrutura de uma declarao Fortran 90.

Um programa consiste de uma seqncia de declaraes destinadas a atingir um determinado objetivo. As declaraes podem ser executveis e no-executveis. Declaraes executveis descrevem aes que o programa deve realizar quando executado. As declaraes no executveis provm informaes necessrias ao correto funcionamento do programa.
2.2.1 Formato livre do cdigo fonte.

At o Fortran 77 o formato do cdigo fonte era fixo, e ditado pelo antigo formato dos cartes perfurados. Quem j utilizou cartes perfurados sabe que tinha 80 colunas, as declaraes podiam ter no mximo 72 colunas, as declaraes deviam comear na stima coluna, e assim por diante. No formato livre atual no mais necessrio atender a estas restries. As declaraes podem comear na primeira coluna e cada linha pode ter at 132 caracteres. Se a declarao muito longa ento pode colocar o smbolo (&) e continuar a declarao na prxima linha. Uma declarao pode usar at 40 linhas consecutivas. No padro do Fortran 77 era praticamente obrigado o uso de labels, no Fortran 90 eles continuam existindo mas so praticamente desnecessrios. Qualquer coisa que venha aps o smbolo ! ser interpretado como comentrio pelo compilador. Este smbolo pode aparecer em qualquer coluna do programa e no apenas na primeira coluna, como era no padro Fortran 77.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

14

Forttran 90//95 For ran 90 95

Boa Prtica Evite Usar

Sempre use o cdigo fonte no formato livre quando escrevendo programas em Fortran 90/95.

O formato fixo foi declarado obsolescente no Fortran 95, significando que candidato a ser deletado nas prximas verses do Fortran. Assim no use o formato fixo em qualquer programa novo.

2.3 A estrutura de um programa Fortran 90.

Como dito anteriormente um programa Fortran um conjunto de declaraes executveis e no executveis organizadas em uma ordem apropriada. Todo programa Fortran pode ser dividido em at trs sees: declarao, execuo e terminao. A seo de declarao consiste de um grupo de declaraes no-executveis, no comeo do programa, que define o nome do programa e as variveis, bem como seus tipos, que sero referenciados na seo de execuo. A seo de execuo consiste de um conjunto de declaraes executveis descrevendo as aes que o programa executar. A seo de terminao consiste de declaraes parando o processamento do programa e dizendo ao compilador onde o programa termina.
2.3.1 Estilo de programao

Talvez a principal conveno (no obrigatria) de estilo em Fortran 90 seja usar letras maisculas para as palavras-chave, tais como: PROGRAM, READ, WRITE; e minsculas para todo o restante. O Fortran case insensitive, isto , no diferencia entre letras maisculas e minsculas. Como os compiladores atuais mostram na tela as palavras-chave em cor diferenciado do restante, vou usar preferencialmente letras minsculas nos programas.

Boa Prtica

Adote um estilo de programao e siga-o em todos os programas.

2.3.2. Primeiro programa em Fortran 90

Na Figura 2.1 apresenta-se um primeiro programa em Fortran 90 que contm os principais elementos de todo programa Fortran 90.
!programador: joo batista aparecido !data: 03 de dezembro de 1999 program meu_primeiro_programa !Objetivo: !Mostrar alguns aspectos bsicos de um programa Fortran 90. !Somar dois nmeros reais x e y, colocar em uma varivel w, e imprimir. !declarao de variveis

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

15

Forttran 90//95 For ran 90 95


real:: x,y,w !atribui valores a x e y x=2.0 y=3.0 w=x+y !imprime x,y e w write(*,*) ' x = ',x,' y = ',y,' w = ',w !termina o programa stop end program meu_primeiro_programa !soma x e y

Figura 2.1 Um programa Fortran 90 simples.


2.4 Constantes e Variveis

Uma constante em Fortran um dado que definido antes do programa rodar, ou seja durante a compilao, e no ser alterado durante a execuo do programa. Uma varivel em Fortran um dado que podem mudar durante a execuo do programa. O valor de uma varivel Fortran pode ser ou no ser inicializado antes do programa ser executado. Quando o compilador Fortran encontra uma constante ou uma varivel ele reserva uma posio de memria para armazen-la. Uma varivel ou constante em Fortran possui nome nico e duas destas grandezas no podem ter o mesmo nome.

Boa Prtica Boa Prtica

Use nomes de variveis com algum significado sempre que possvel. Crie um dicionrio de dados (variveis e constantes) para cada programa, visando facilitar a manuteno.

2.4.1. Constantes e variveis inteiras

Conforme discutido anteriormente o tamanho (kind)de uma constante ou varivel inteira depende da quantidade de bits utilizado para armazenar a informao. Para declarar que uma varivel ou constante inteira numero_alunos procede-se da seguinte maneira
integer :: numero_alunos numero_alunos = 30

ou de uma forma mais econmica


integer :: numero_alunos = 30

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

16

Forttran 90//95 For ran 90 95


2.4.2. Constantes e variveis reais

Pode existir variveis e constantes de todos os tipos que o compilador suportar. Por exemplo, variveis do tipo real podem ter diferentes comprimentos (kinds), mostrados na Tabela 2.2. Tabela 2.2-Preciso e faixa de nmeros reais em vrios computadores. nmero de Computador nmero total nmero de preciso de bits bits na decimal bits do mantissa expoente 32 24 7 8 VAX 64 56 15 8 32 24 7 8 IBM PC 64 53 15 11 32 24 7 8 Sun Sparc 64 53 15 11 Cray 64 49 14 15 faixa expoente do

10-38 a 1038 10-38 a 1038 10-38 a 1038 10-38 a 1038 10-38 a 1038 10-308 a 10308 10-2465 a 102465

Para declarar uma constante ou varivel real procede-se da seguinte forma


real :: x x = 10.33

2.4.3. Constantes e variveis lgicas

O Fortran 90 suporta variveis lgicas, as quais s podem assumir dois valores TRUE ou FALSE. A maneira de declarar o tipo e atribuir valores a uma varivel lgica
logical :: w w = .true.

ou
logical :: w = .true.

2.4.4. Constantes e variveis alfanumricas

O tipo de dados character consiste de grupos de smbolos alfanumricos. Pode-se declara o tipo e atribuir valores a uma constante ou varivel caracter das seguintes maneiras
character (len = 14) :: x, y, w character :: r,s character(12) :: t,q x = Joseh da Silva ! variaveis com 14 caracteres ! variaveis com 1 caracter ! variaveis com 12 caracteres

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

17

Forttran 90//95 For ran 90 95


O termo len = 14 que aparece logo acima refere-se quantidade de caracteres na varivel ou constante. Da mesma forma o termo (12) refere-se quantidade de caracteres.
2.4.5. Definindo variveis inteiras ou reais por Default ou explicitamente.

Se no for definido o tipo de uma variveis, por default ela ser inteira se comear com I,J,K,L,M,N e real no restante dos casos. Este default pode ser alterado usando o comando IMPLICIT. O mais adequado usar nas declaraes iniciais de um programa a declarao implicit none que obrigar o programador a definir todas as variveis sendo utilizadas, caso contrrio o compilador acusar o erro.
2.4.6. Mantendo as constantes consistentes em um programa

Evite ficar definindo uma mesma constante em diferentes posies do programa com diferentes precises. Por exemplo, suponhamos que existe uma constante chamada um_terco e que vale um tero, assim evite num local definir um_terco = 0.33 e em outro um_terco = 0.33333.

Boa Prtica

Mantenha as constantes precisas e consistentes em todo o programa. Para melhorar a consistncia e o entendimento do cdigo, d um nome cada constante importante e as use pelo nome em todo o programa.

2.5 Declarao de atribuio e clculos aritmticos

Clculos so feitos em Fortran 90 utilizando-se declaraes de atribuio do tipo


nome_da_variavel = expresso_aritmtica,

por exemplo k = k + 1. Os operadores aritmticos do Fortran 90 so + * / ** adio subtrao multiplicao diviso exponenciao

Os operadores aritmticos mencionados acima so operadores binrios significando que devem aparecer entre duas variveis tal como
a + b, a b, a*b, a/b, a**b,

adicionalmente os smbolos + e podem ocorrer como operadores unrios, significando que podem ser aplicados a apenas uma varivel, como segue

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

18

Forttran 90//95 For ran 90 95


+a ou b.

2.5.1 Aritmtica inteira Aritmtica inteira aquela realizada apenas com nmeros inteiros. Aritmtica com inteiros sempre produz resultados inteiros. Esta regra importante principalmente quando existe diviso de dois inteiros, o resultados no poder ser fracionrio. Por exemplo
1/3 = 0, 2/3 = 0, 3/3 = 1, 4/3 = 1, 5/3 = 1, 6/3 = 2.

Cuidado
2.5.2 Aritmtica real

Preste ateno na aritmtica com inteiros. Diviso de inteiros sempre fornece resultados inesperados.

Aritmtica real ou aritmtica de ponto flutuante (floating-point arithmetic) aquela envolvendo constantes e variveis reais. A aritmtica real produz resultados reais que geralmente espera-se. Por exemplo
1./3. = 0.333333, 2./3. = 0.666666, 3./3. = 1., 4./3. = 1.333333.

Cuidado

Preste ateno na aritmtica com inteiros. Devido preciso limitada, duas expresses teoricamente idnticas podem freqentemente fornecer resultados ligeiramente diferentes.

2.5.3. Hierarquia das operaes

Freqentemente vrias operaes aritmticas ocorrem combinadas em apenas uma expresso. Para remover qualquer ambigidade que poderia ocorrer se a ordem dos operadores fosse alterada, Fortran 90 estabelece uma srie de regras que governam a ordem que as operaes so realizadas em uma expresso. Estas regras so semelhantes s da lgebra. As operaes so realizadas na seguinte ordem de prioridade 1) O contedo de todos os parnteses so calculados, partindo do mais interno para fora; 2) Todas as exponenciais so calculadas, operando da direita para a esquerda; 3) Todas as multiplicaes e divises so efetuadas, operando da esquerda para a direita; 4) Todas as adies e subtraes so avaliadas, operando da esquerda para a direita.

Boa Prtica

Use parnteses o suficiente fazer que as equaes fiquem claras e simples para entender.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

19

Forttran 90//95 For ran 90 95


2.5.4 Aritmtica mista

Quando operaes aritmticas so realizadas utilizando nmeros reais e nmeros inteiros, ento diz-se que uma aritmtica mista. Quando somamos dois nmeros inteiros o resultado ser tambm um inteiro. Quando somamos dois nmeros reais o resultado tambm ser um nmero real. Mas o que acontecer quando, por exemplo, somamos um real e um inteiro? Alm do mais existem muitas outras combinaes (em realidade infinitas) onde pode aparecer reais e inteiros juntos. No caso que propus acima o que vai acontecer que o programa vai perceber que se est tentando somar um inteiro e um real, ento no processo de soma o valor inteiro transformado para real (note a varivel continua inteiro) e somado com o outro real, resultando em um nmero real. Veja abaixo trs exemplos de diferentes tipos de operaes Tipo de expresso Operao Resultado Tipo de resultado Expresso inteira 5/2 2 inteiro Expresso real 5./2. 2.5 real Expresso mista 5./2 2.5 real necessrio muito cuidado em expresses mistas, principalmente onde exista diviso. Seja o seguinte exemplo
2.0 + 1/3 + 1/5 + 1/7 = 2.0

As divises na expresso acima tem prioridade sobre a soma, os termos de cada diviso tem apenas inteiros, ento uma aritmtica inteira, e o resultado das trs divises na expresso sero zero (inteiro), o qual ser ento transformado para zero (real) e adicionado com o 2.0, com resultado final igual a 2.0. Uma soluo para este problema transformar (durante a operao) todos os valores inteiros para reais. Veremos como fazer isto mais adiante.

Cuidado

Expresses do tipo misto so perigosas por que so difceis de entender e podem produzir resultados enganosos. Evite-as sempre que possvel.

2.5.5. Aritmtica mista e exponenciao

Existe uma situao onde a aritmtica mista desejvel, na exponenciao. Se temos uma expresso
w = y**n

onde y um real negativo e n um inteiro. Os compiladores atuais interpretam esta expresso como: tome o valor y e multiplique-o por si mesmo n vezes. Por outro lado se temos a seguinte expresso
w = y**x

onde y real negativo e x tambm real, esta expresso ser calculada internamente no computador da seguinte forma w = exp(x*lny),
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
20

Forttran 90//95 For ran 90 95


como y negativo o programa ser descontinuado devido ao erro fatal. Alguns compiladores (nem todos) tem alguma habilidade em verificar se o real x pode ser representado de forma exata por um inteiro. Caso a resposta seja positiva a exponenciao efetuada tal como no caso em que o expoente era inteiro.

Boa Prtica Cuidado

Use expoentes inteiros ao invs de expoentes reais, sempre que possvel.

Nunca eleve um nmero negativo a um expoente real.

2.6 Declarao de atribuio e clculos lgicos

Da mesma forma que clculos aritmticos, clculos lgicos tambm so efetuados com declaraes de atribuio, que em geral tem a forma nome_da_variavel_logica = expresso_logica A expresso lgica do lado direito da expresso acima pode ser qualquer combinao de variveis lgicas, constantes lgicas e operadores lgicos. Um operador lgico um operador sobre dados numricos, caracter ou lgico, fornecendo um resultado final do tipo lgico.
2.6.1 Operadores relacionais lgicos

Operadores relacionais lgicos so operadores com dois nmeros ou caracteres que fornecem um resultado do tipo lgico (logical). O resultado depende da relao entre os dois valores, por isto so chamados operadores relacionais. A forma geral do operador relacional v1 operador v2 onde v1 e v2 so expresses aritmticas, variveis, constantes ou caracteres, e operador um dos operadores relacionais mostrado na Tabela 2.3 Tabela 2.3-Operadores relacionais lgicos Operao Novo estilo Estilo antigo Significado == .eq. igual a /= .ne. diferente > .gt. maior que >= .ge. maior ou igual a < .lt. menor que <= .le. menor ou igual a Se a relao entre v1 e v2 verdadeira o operador retorna .TRUE. como resultado, caso contrrio retornar .FALSE. Por exemplo 3>5 = .false.; 3<5 = .true.; B<D = .true.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

21

Forttran 90//95 For ran 90 95


2.6.2 Operadores combinatoriais lgicos

Operadores combinatoriais lgicos so operadores com um ou dois operandos lgicos que fornecem um resultado lgico. Existem quatro operadores binrios: .and., .or., .eqv., e .neqv.; e um operador unrio, .not.. A forma geral de uma operao com combinatorial lgica com operador binrio w1.operador.w2 onde w1 e w2 so expresses lgicas, variveis ou constantes, e .operador. um dos operadores combinatoriais mostrados na Tabela 2.4. Tabela 2.4-Tabela da verdade para operadores combinatoriais lgicos. w1 w2 w1.and.w2 w1.or.w2 w1.eqv.w2 w1.neqv.w2 .false. .false. .false. .false. .true. .false. .false. .true. .false. .true. .false. .true. .true. .false. .false. .true. .false. .true. .true. .true. .true. .true. .true. .false. Na Tabela 2.5 apresenta-se a tabela da verdade para o operador unrio .not. Tabela 2.5- Tabela da verdade para o operador unrio .not. w1 .not.w1 .false. .true. .true. .false. Na hierarquia das operaes os operadores lgicos combinatoriais so calculados depois das operaes aritmticas e de todos os operadores lgicos relacionais. A ordem na qual os operadores em uma expresso so executados 1) Todos os operadores aritmticos so calculados de acordo com a seqncia apresentadas anteriormente; 2) Todos os operadores relacionais (==, /=, >, >=, <, <=) so calculados, operando da esquerda para a direita; 3) Todos os operadores .not. so executados; 4) Todos os operadores .and. so executados, operando da esquerda para a direita; 5) Todos os operadores .or. so executados, operando da esquerda para a direita; 6) Todos os operadores .eqv. e .neqv. so executados, operando da esquerda para a direita.
2.7 Declarao de atribuio e variveis do tipo caracter

Manipulaes de variveis do tipo caracter so efetuadas com a seguinte declarao de atribuio nome_da_variavel_caracter = expresso_com_variaveis_caracter. Um operador caracter (character operator) aquele que opera em dados que permitem um resultado do tipo caracter. Se o lado direito da expresso acima menor do que o da esquerda, algumas posies so ocupados com os caracteres atribudos e o restante preenchido com brancos. Porm se o lado direito maior do que o da esquerda, os caracteres em excesso sero descartados. Por exemplo, a declarao
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
22

Forttran 90//95 For ran 90 95


character(len = 5) :: x x = ijk

armazena o valor ijk na varivel x, enquanto que


character(len = 2) :: x x = ijk

armazena o valor ij na varivel x.


2.7.1 Sub-especificao de variveis do tipo caracter

Uma sub-especificao de uma varivel caracter seleciona uma parte da varivel e a trata como se fosse uma varivel independente. Uma sub-especificao definida colocando valores inteiros que representem o incio e o final dos caracteres selecionados, colocando-os entre parentes e separados por vrgula, logo aps o nome da varivel. Por exemplo se
a = abC4567 b = a(3:5)

ento b = C45.
2.7.2 Operador de concatenao

O operador (//) combina duas ou mais variveis caracter para formar ou outra. Por exemplo, a seguinte seqncia
a = abcdef b = 1234567 c = a(1:2)//b(3:4)//a(3:6)

ir produzir o resultado c = ab34cdef.


2.7.3. Operadores relacionais lgicos com variveis do tipo caracter

Como j se disse anteriormente, existem certos padres normalizados de dados do tipo caracter, por exemplo o ASCII. Se um caracter igual ou diferente de outro fcil. No entanto difcil dizer se um caracter , por exemplo, maior que outro. A seguinte expresso a<A verdadeira ou falsa? Dentro do padro ASCII a expresso falsa, por que o A o elemento de nmero 65, e a o de nmero 97. Por esta conveno a maior que A. Isto pode levar a resultados surpreendentes se o usurio mover o seu programa de um local com padro para outro com outro padro. A seqncia de caracteres pode variar de forma catastrfica. Uma Segunda pergunta interessante : Como comparar variveis do tipo caracter com vrios caracteres. Por exemplo: BBBCFR maior ou menor que BBC? O algoritmo usado no compilador Fortran 90 compara o primeiro caracter de cada varivel, no caso so iguais, a compara-se os segundos caracteres, tambm so iguais; ento compara-se os terceiros caracteres que so diferentes, o C maior que o B, ento BBC>BBBCFR.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

23

Forttran 90//95 For ran 90 95

Cuidado
2.8 Funes intrnsecas

Evite usar operadores lgicos relacionais operando em variveis do tipo caracter.

Existe um subconjunto das funes matemticas que associa a um ou mais argumentos um nico resultado. Nos clculos tcnicos e cientficos faz-se necessrio calcular diferentes tipos de funes. Estas funes podem muitas vezes ser escritas como combinao de outras funes mais bsicas. O compilador Fortran 90 apresenta um conjunto, aproximadamente 112, destas funes bsicas, chamadas funes intrnsecas (intrinsic functions). Caso o usurio no consiga reduzir sua funo de interesse a combinaes das funes intrnsecas, ser necessrio desenvolver uma nova, a partir dos fundamentos do clculo. Funes criadas pelo prprio usurio podem ser definidas como funes externas (external function)ou funes internas (internal function). Retornaremos a este assunto mais adiante. Abaixo, na Tabela 2.6, apresenta-se algumas das funes intrnsecas suportadas pelo Fortran 90/95. Tabela 2.6 Vinte funes intrnsecas suportadas pelo Fortran 90/95. Funo e Definio Tipo de Tipo de Comentrios argumentos matemtica argumento resultado sqrt(x) real real raiz quadrada de x x abs(x) |x| real/inteiro real/inteiro valor absoluto de x achar(i) inteiro char(1) retorna caracter ASCII sin(x) sin(x) real real seno de x cos(x) cos(x) real real co-seno de x tan(x) tan(x) real real tangente de x real real exponencial de x exp(x) ex real real logaritmo natural de x log(x) loge(x) real real logaritmo base 10 de x log10(x) log10(x) iachar(c) char(1) inteiro posio de c no ASCII int(x) real inteiro parte inteira de x nint(x) real inteiro inteiro prximo de x real(i) inteiro real converte inteiro para real mod(a,b) a-p*int(a/p) real/inteiro real/inteiro resto de a/b max(a,b) real/inteiro real/inteiro pega o maior de a e b min(a,b) real/inteiro real/inteiro pega o menor de a e b -1 real real inverso do seno de x asin(x) sin (x) real real inverso do co-seno de x acos(x) cos-1(x) real real inverso da tangente de x atan(x) tan-1(x) Note na tabela acima tem uma funo para converter inteiros em reais, evitando assim os inconvenientes da aritmtica mista. Esta funo a real(i). Assim a expresso
2.0 + 1/5 = 2.0

pode ser re-escrita como


2.0 + real(1)/real(5) = 2.2.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

24

Forttran 90//95 For ran 90 95


2.9 Declaraes de entrada e sada de dados

Uma declarao de entrada de dados l um ou mais dados de um equipamento de entrada de dados. Da mesma forma uma declarao de sada de dados escreve um ou mais dados em um equipamento de sada de dados. Uma forma simplificada de declarao de entrada de dados read(*,*) lista_de_variaveis. Analogamente, uma declarao de sada de dados write(*,*) lista_de_variaveis. Abaixo apresenta-se um exemplo que l duas variveis inteiras e imprime as duas inteiras e mais duas reais j definidas no programa
program entrada_saida integer :: i,j real :: p=1.0, q=7.0 write(*,*)'digite i e j' read(*,*)i,j write(*,*)i,j,p,q stop end entrada_saida

Boa Prtica
2.10 Inicializao de variveis

Imprima na tela, quando possvel, todas as variveis que so lidas por um programa, para certificar que foram digitadas e processadas corretamente.

Considere o seguinte programa


program inicializa integer :: m real :: x write(*,*) m,x end program inicializa

Uma vez que as variveis m e x no foram inicializadas, quando so impressas podem conter qualquer valor que represente o estado dos bits daquelas posies de memria que o compilador lhes atribuiu. Alguns compiladores zeram as variveis no inicializadas. Em todo o caso os resultados podem ser catastrficos.

Boa Prtica

Sempre inicialize todas as variveis de um programa antes de us-las.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

25

Forttran 90//95 For ran 90 95


2.11 Declarao IMPLICIT NONE

Uma declarao no executvel de muita utilidade a implicit none. Quando utiliza-se implicit none todos os defaults de digitao so desabilitados e o programador ter obrigatoriamente que definir todas as variveis utilizadas. Se algumas varivel foi digitadas errada, quando da compilao o compilador emitir um erro indicando que existe uma varivel no declarada. A declarao implicit none dever aparecer logo aps a declarao program e antes das declaraes de tipos de variveis.

Boa Prtica

Defina sempre e explicitamente todas as variveis do programa e use a declarao implicit none para ajudar a localizar variveis no declaradas e variveis digitadas incorretamente.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

26

Forttran 90//95 For ran 90 95


Captulo 3 - Estruturas de controle
Um programa Fortran pode ser apenas seqencial, todos os comandos vo sendo executados um aps o outro at chegar ao final do programa. Entretanto existem programas onde, dependendo dos dados, deseja-se executar apenas uma parte do programa deixando o restante sem executar. Para criar este efeito necessita-se de estruturas de programa mais complexas do que as vistas at o momento. Estas estruturas de controle podem ser classificadas em dois grupos: laos (ou ciclos, loops), onde o programa fica executando um conjunto de declaraes ciclicamente; e bifurcaes (branches), onde o programa chega a um determinado ponto da execuo e toma a deciso em dos qual dos ramos da bifurcao o processamento continuar. Este captulo trata basicamente das implementaes no compilador Fortran 90, destes dois conceitos: laos e bifurcaes.
3.1 Estruturas de controle: bifurcaes

Como estabelecido acima as bifurcaes selecionam pores de cdigo que sero executadas enquanto salta outras pores que no sero executadas. Basicamente tem-se algumas variantes da declarao IF, e a declarao SELECT CASE.
3.1.1. Estrutura do tipo bloco IF

A forma mais comum de declarao IF o bloco IF. Esta estrutura define que um determinado bloco de cdigo s ser executado se uma determinada expresso lgica for verdadeira. A forma do bloco IF
if (expressao_logica) then declaracoes_executaveis end if

Boa Prtica

Sempre indente o corpo de um bloco IF de alguns espaos para melhorar a leitura do cdigo.

3.1.2. Estrutura do tipo ELSE e ELSE IF

Na estrutura bloco IF mostrada acima uma poro de programa executada se uma expresso lgica verdadeira. Se for falsa o bloco de declaraes simplesmente pulado. No entanto existem situaes onde deseja-se executar um bloco de declaraes se a expresso lgica for verdadeira, e um outro bloco se a expresso lgica for falsa. Uma declarao ELSE seguida de nenhuma, uma ou mais declaraes ELSE IF podem ser adicionadas ao bloco IF, para satisfazer este tipo de necessidade. O bloco IF com declaraes ELSE e ELSE IF tem a seguinte forma
if (expressao_logica_1) then declaracoes_executaveis_1 else if (expressao_logica_2) then declaracoes_executaveis_2 else declaracoes_executaveis_3 end if

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

27

Forttran 90//95 For ran 90 95


Na estrutura mostrada acima se a expressao_logica_01 for verdadeira executa-se ento declaracoes_executaveis_1 e o estrutura completa sua tarefa. Se a expresso_logica_1 for falsa o programa verifica se a expressao_logica_2 verdadeira ou falsa. Se for verdadeira executa-se declaracoes_executaveis_2, se for falsa executase declaracoes_executaveis_3.
3.1.3. Estrutura do tipo IF, nomeado

Todas as formas de declarao IF podem ser nomeadas. Isto pode-se dar nomes a cada uma destas estruturas. A forma do bloco IF nomeada ser ento
{nome:} if (expressao_logica) then declaracoes_executaveis end if {nome}

O bloco IF com declaraes ELSE e ELSE IF nomeado tem a seguinte forma


{nome:} if (expressao_logica_1) then declaracoes_executaveis_1 else if (expressao_logica_2) then declaracoes_executaveis_2 else declaracoes_executaveis_3 end if {nome}

Quando tem-se estruturas com muitos laos e bifurcaes indentados uns dentro dos outros, caso ao digitar seja esquecido alguma declarao end, o compilador acusar um erro que muitas vezes difcil de encontrar. A nomeao destas estruturas opcional mas pode ser usada para evitar ambigidades e facilitar a leitura e a manuteno do cdigo.

Boa Prtica

Destine um nome a todas as estruturas grandes e complicadas de IFs do programa, para facilitar a leitura e a manureno.

Abaixo apresenta-se um programa exemplo deste tipo IF nomeado


program if_nomeado logical :: l1=.true., l2=.false., l3=.true. integer :: i externo: if(l1) then i=1 medio: if(l2) then i=2 interno: if(l3) then i=3 end if interno end if medio end if externo stop end program if_nomeado

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

28

Forttran 90//95 For ran 90 95


3.1.4 Declarao IF lgico

Quando a estrutura bloco IF tem apenas uma declarao executvel ento ela uma forma especial mais simplificada. Torna-se ento uma declarao de apenas uma linha, como segue
if(expressao_logica) declaracao_executavel

3.1.5 A estrutura CASE

A estrutura CASE uma forma de implementar mltiplas bifurcaes simultneas. Permite ao programador escolher um determinado bloco de cdigo a ser executado, entre vrios, baseando em uma expresso envolvendo dados dos tipos inteiro, caracter e lgico. A forma geral da estrutura CASE
{nome:} select case (expressao_case) case (seletor_case_1) {nome} declaracoes_executaveis_1 case (seletor_case_2) {nome} declaracoes_executaveis_2 case (selector_case_3) {nome} declaracoes_case_3 case default {nome} declaracoes_default end select {nome}

Se a expressao_case estiver de acordo com o seletor_case_1 as declaracaoes_executaveis_1 sero executadas e o restante ser pulado. O mesmo acontece se a expressao_case estiver de acordo com o seletor_case_2 as declaracaoes_executaveis_2 sero executadas e o restante ser pulado. E assim por diante. Se a expressao_case no estiver de acordo com nenhum dos seletor_case, ento as declaracoes_default sero executadas. O case default opcional. Caso no exista o case default e a expressao_case no atenda nenhum dos seletor_case, ento nenhum dos blocos ser executado. Abaixo apresenta-se um exemplo de utilizao da declarao select case
! Objetivo: Demonstrar a utilidade da declarao CASE ! Distingue entre as palavras faca, garfo, e colher. character(6) :: x write(*,*)'Digite o talher!' read(*,*)x select case(x) case ('faca') write(*,*)'O talher uma faca' case ('garfo') write(*,*)'O talher um garfo' case ('colher') write(*,*)'O talher uma colher'

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

29

Forttran 90//95 For ran 90 95


case default write(*,*)'No faca, nem colher, muito menos um garfo.' end select stop end program cap03_02

3.2 Estruturas de controle: laos

Laos so estruturas de controle que permitem a execuo de um bloco de cdigo vrias vezes em seqncia. As duas formas bsicas de laos so: laos com terminao indefinida, e laos com terminao bem definida. No existe um lao puro, uma vez que o mesmo se repetiria eternamente. Assim todo lao possui na sua estrutura (implcita ou explicitamente) uma bifurcao responsvel pela sada do lao. Nos laos de terminao indefinida, o lao continua indefinidamente at que uma da condio de controle seja atendida e completa-se a execuo do lao. Se no for bem projetado este tipo de lao pode continuar indefinidamente. Por outro lado o lao com terminao bem definida, executa uma quantidade finita de laos e termina sua execuo. Neste caso no h o risco da execuo continuar indefinidamente.
3.2.1. O lao com terminao indefinida

Como falado acima, este tipo de lao executa um bloco de declaraes indefinidamente at que uma determinada condio seja atingida. A forma geral desta declarao em Fortran 90/95
do declaracoes_executaveis_1 if(expressao_logica) exit declaracoes_executaveis_2 end do

Abaixo apresenta-se um exemplo deste tipo de lao


! Testando um lao indefinido program looping integer :: i=0 do i=i+1 if(i>=10) exit write(*,*) i end do stop end program looping

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

30

Forttran 90//95 For ran 90 95


Na estrutura acima o bloco de declaraes executveis entre do e end do ser executado indefinidamente at que a expressao_logica seja verdadeira. Existe a possibilidade de que este tipo de laos nunca atinja terminao.
3.2.2. Outra implementao do lao com terminao indefinida

O compilador do Fortran 90/95 tem uma forma alternativa, mais simples, de implementar a estrutura mostrada no item anterior. Este caso eqivale ao mostrado acima onde no se tenha nenhuma declarao no sub-bloco declaracoes_executaveis_1, assim esta implementao alternativa, chamada de do while tem a seguinte forma
do while (expressao_logica) declaracoes_executaveis end do

Nesta estrutura acima o bloco de declaracoes_executaveis ser executada indefinidamente, enquanto a expressao_logica for verdadeira. Neste tipo de estrutura tambm possvel que o lao no atinja terminao.
3.2.3. O lao com terminao bem definida

Este tipo de lao tem terminao garantida porque o lao ser executado uma quantidade bem definida de vez e ento terminar. s vezes este tipo de lao pode ser chamado tambm de lao iterativo ou lao com contagem. O lao com contagem construdo da seguinte forma
do index = i_start, i_end, i_incr declaracoes_executaveis end do

onde index uma varivel do tipo inteiro usada como um contador do lao. A variveis inteiras i_start, i_end e i_incr so parmetros do lao de contagem, elas controlam os valores assumidos por index durante a execuo do lao. O parmetro i_incr opcional e o seu valor default a unidade. Os valores assumidos por index comeam com o valor i_start, vai sendo acrescido de i_incr, at atingir o valor i_end, inclusive. O critrio de parada do lao em realidade index*i_incr i_end*i_incr, definido desta forma esta mais apto a tratar de casos patolgicos. Entretanto, testando o caso patolgico onde i_incr = 0, o compilador (Microsoft V4) no acusou erro e o programa quando rodou foi descontinuado por erro fatal. O nmero total de iteraes (i_iter) que ser executado em um lao de contagem pode ser obtido aproximadamente por i_iter = (i_end-i_start+i_incr)/i_incr. A varivel de controle index tem um escopo interno do lao, no devendo ser modificada (externamente) durante a execuo do lao. Pode ser inserido um comando IF (expressao_logica) EXIT no interior deste tipo de lao para provocar uma interrupo precoce quando a expressao_logica for verdadeira. Aps o trmino do lao DO o valor da varivel de controle index indefinido (dependendo de cada implementao de compilador). proibido fazer um desvio incondicional (go to) de fora para dentro de um lao DO. A seguir tem-se uma implementao deste tipo de lao

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

31

Forttran 90//95 For ran 90 95


! Testando um lao definido program looping_2 integer :: i=0, i_start=1, i_end=10, i_incr=1 do i=i_start,i_end,i_incr write(*,*) i end do stop end program looping_2

Cuidado Cuidado Deletado

Nunca use incremento nulo em laos de contagem.

Boa Prtica

Sempre indente o corpo de um bloco DO de alguns espaos para melhorar a leitura do cdigo.

Nunca modifique o valor da varivel de controle de lao DO enquanto o lao est sendo executado. O uso de varivel real como varivel de controle de lao de DO foi declarado obsolescente no Fortran 90 e foi deletado do Fortran 95. Nunca dependa do valor de uma varivel de controle de lao de DO depois que o lao termina.

Boa Prtica

3.2.4. As declaraes CYCLE e EXIT

Pode-se utilizar adicionalmente as declaraes cicle e exit para controlar as operaes enquanto executa-se algum tipo de lao. Se a declarao cicle for usada dentro de um lao a execuo ser parada e o controle retornar ao comeo do lao. A varivel de controle ento ser incrementada e o processamento continuar. Abaixo mostra-se um exemplo de tal aplicao
program cycling integer :: i, m=10 do i=1,m,1 if(i==5) cycle write(*,*) i end do

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

32

Forttran 90//95 For ran 90 95


stop end program cycling

Quando a declarao EXIT utilizada dentro de um lao de DO a execuo do lao ser interrompida e o controle da execuo ser passada para a primeira declarao executvel aps o lao. Adaptando-se o exemplo acima para testar o efeito da declarao EXIT tem-se
program exiting integer :: i, m=10 do i=1,m,1 if(i==5) exit write(*,*) i end do stop end program exiting 3.2.5 Laos nomeados

Da mesma maneira que as bifurcaes (IFs) podem ser nomeados, os laos (DOs) tambm podem. Os dois tipos principais de laos podem ser nomeados da seguinte maneira
{nome:} DO declaracoes_executaveis_1 if(expressao_logica_1) cycle {nome} declaracoes_executaveis_2 if(expressao_logica_2) exit {nome} declaracoes_executaveis_3 end do {nome} {nome:} DO index = i_start, i_end, i_incr declaracoes_executaveis_1 if(expressao_logica_1) cycle {nome} declaracoes_executaveis_2 if(expressao_logica_2) exit {nome} declaracoes_executaveis_3 end do {nome}

Boa Prtica

D nomes aos principais laos no programa para deixar claro quais declaraes executveis pertencem a um determinado lao.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

33

Forttran 90//95 For ran 90 95


3.2.6. Ninho de laos

Quando vrios laos esto um dentro do outro, diz-se que o laos esto aninhados ou que formam um ninho de laos. Abaixo tem-se um exemplo deste tipo de ninho de laos
{nome_2:} DO declaracoes_executaveis_1 {nome1:} DO declaracoes_executaveis_2 if(expressao_logica_1) cycle {nome_1} declaracoes_executaveis_3 if(expressao_logica_2) exit {nome_2} declaracoes_executaveis_4 end do {nome_1} declaracoes_executaveis_5 end do {nome_2}

As declaraes cycle e exit que aparecem acima tm que referir a qual lao a declarao se aplica quando existe mais de um lao. Se estas declaraes no tiverem referncias a qual lao se aplicam, ento se aplicaro ao lao externo mais imediato no sentido de dentro para fora.

Boa Prtica

Use declaraes CYCLE e EXIT nomeadas em ninho de laos para deixar claro que a declarao afete o lao correto.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

34

Forttran 90//95 For ran 90 95


Captulo 4 - Conceitos bsicos de entrada e sada de dados
No captulo anterior utilizou-se as declaraes read(*,*) e write(*,*). Este formato o formato default destas declaraes. As estrelas (*,*) que aparecem nas declaraes significam: no estou lhe passando nada, faa um procedimento padro. O compilador Fortran 90 suporta vrios tipos de formataes, que veremos neste captulo.
4.1 Descritores de formato

Na Tabela 4.1 tm-se os tipos mais comuns de descritores de formato usados, juntamente com seus significados. Tabela 4.1-Simbolos usados com os descritores de formato. Smbolo Significado c Nmero da coluna d Nmero de dgitos direita do ponto decimal m Nmero mnimo de dgitos a ser apresentado n Nmero de espaos para pular r Nmero de vezes o formato repetido w Nmero de caracteres a ser usado
4.1.1 Sada de dados inteiros: O descritor I

O descritor usado para formatar a apresentao de inteiros o descritor I. Sua forma geral
r(Iw) ou r(Iw.m)

onde r, w e m tem os significados mostrados na Tabela 4.1. Abaixo tem-se um pequeno exemplo do uso da formatao para inteiros.
program formato_inteiro integer :: i=0, j=17, k=254, l=7689 write(*,1)i, j, k, l 1 format(4(1x,i3.3)) stop end program formato_inteiro

O resultado da impresso acima ser algo do tipo


000 017 254 ***

As estrelas que aparecem acima significam que o formato foi insuficiente para imprimir o dado.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

35

Forttran 90//95 For ran 90 95


4.1.2 Sada de dados reais: O descritor F

Um dos descritores usados para formatar dados reais o F. Sua forma geral
r(Fw.d)

Abaixo apresenta-se um programa com algumas impresses usando o formato F.


program formato_real_F real :: i=1., j=1./3., k=21.123456789, l=1234.567 write(*,1)i,j,k,l 1 format(4(1x,F7.4)) stop end program formato_real_F

O resultado do programa acima ser 01.0000 .3333 21.1234 *******


4.1.3 Sada de dados reais: O descritor E

Muitas vezes dados do tipo real variam muito de faixa e torna-se difcil imprimi-los usando o descritor F. Um outro descritor para nmeros reais o descritor E, tambm conhecido como notao exponencial. Esta formatao consiste em imprimir um nmero normalizado entre 0 e 1, multiplicado por um potncia de 10. O formato geral do descritor E r(Ew.d) Alterando o programa anterior para usar a formatao E tem-se program formato_real_E real :: i=1.,j=1./3.,k=21.123456789,l=1234.567 write(*,1)i,j,k,l 1 format(4(1x,E14.7)) stop end program formato_real_E O resultado deste programa ser .1000000E+01 .3333333E+00 .2112346E+02 .1234567E+02

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

36

Forttran 90//95 For ran 90 95


Percebe-se que este formato mais adequado que o formato F quando deseja-se ler ou imprimir nmeros ou muito pequenos ou muito grandes. Para este tipo de formato necessrio que
w d+7 4.1.4 Sada de dados reais: O descritor ES

O descritor ES muito semelhante ao descritor E. Esta formatao consiste em imprimir um nmero normalizado entre 1 e 10 (lembre-se que no descritor E este nmero era entre 0 e 1), multiplicado por um potncia de 10. O formato geral do descritor ES r(ESw.d) Alterando o programa anterior para usar a formatao ES tem-se program formato_real_ES real :: i=1.,j=1./3.,k=21.123456789,l=1234.567 write(*,1)i,j,k,l 1 format(4(1x,ES14.7)) stop end program formato_real_ES O resultado deste programa ser 1.0000000E+00 3.33333330E-01 2.1123460E+01 1.2345670E+03

Boa Prtica

Quando imprimindo dados do tipo real muito grandes ou muito pequenos, utilize o descritor ES, porque idntico notao cientfica e os resultados podero ser entendidos mais facilmente pelo usurio.

4.1.5 Sada de dados lgicos: O descritor L

O descritor L usado para formatar dados do tipo lgico, e sua forma geral r(Lw) O programa abaixo
program formato_logico_L logical :: x=.true., y=.false.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

37

Forttran 90//95 For ran 90 95


write(*,1)x,y 1 format(2(1x,L1)) stop end program formato_logico_L

ir produzir o seguinte resultado


TF 4.1.6 Sada de dados caracter: O descritor A

Dados do tipo caracter so formatados com o descritor A, que a seguinte forma geral
r(A) ou r(Aw)

Abaixo apresenta-se um programa que explora o descritor A


program formato_caracter_A character(len=15) :: x = 'Como vo vocs?' write(*,1)x write(*,2)x write(*,3)x write(*,4)x write(*,5)x 1 format(1x,A) 2 format(1x,A1) 3 format(1x,A5) 4 format(1x,A15) 5 format(1x,A20) stop end program formato_caracter_A

Este programa quando executado fornece o seguinte resultado


Como vo vocs? C Como Como vo vocs? Como vo vocs? 4.1.7 Posicionamento horizontal: Os descritores X e T

Estes dois descritores operam quando os dados j esto no buffer. O descritor X insere brancos nos locais designados, e o T tabula a coluna para onde se deve ir. O descritor X tem o seguinte formato

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

38

Forttran 90//95 For ran 90 95


nX onde n a quantidade de branco que se quer inserir em um determinado local. Lembre-se que nos programas anteriores utilizou-se o descritor X, atravs de declaraes do tipo 1X, destinadas a inserir um branco na primeira coluna e assim informar ao compilador que se desejava espaamento simples. O descritor T tem a forma Tc onde c o nmero da coluna para onde deve-se ir. O efeito que o descritor T causa ficar saltando de um coluna para outra durante a impresso. Veja o exemplo abaixo
program formato_XT integer :: i=1,j=2,k=3 write(*,1)i,j,k 1 format(1x,4x,i1,t4,i1,t2,i1) stop end program formato_XT

cujo resultado final ser


321

Cuidado

Quando utilizando descritor T seja cuidadoso para no produzir resultados aparentemente falsos.

4.1.8 Trocando as linhas de sada: O descritor slash (/)

O descritor / (slash) quando encontrado em uma declarao format envia o contedo do buffer para a impressora e inicia um novo buffer, isto eqivale a dizer que o descritor / inicializa uma nova linha de impresso. Se colocarmos vrios slashes seguidos o efeito ser pular vrias linhas de impresso. No programa abaixo mostra-se o efeito causado pelo descritor slash
program formato_slash logical :: x=.true., y=.false. write(*,1)x,y 1 format(2(1x,L1//)) stop end program formato_slash

O resultado impresso deste programa


Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
39

Forttran 90//95 For ran 90 95


T F Esteja certo que exista uma relao um-para-um entre os tipos de dados em uma declarao write e os tipos de formato especificados na declarao format correspondente. Caso isto no ocorra o programa ir falhar durante a execuo.

Cuidado

4.1.9. Declaraes READ formatadas

As regras de formatao de declaraes READ so semelhantes s de declaraes WRITE.


4.2. Introduo a arquivos e processamento de arquivos

Em todos programas mostrados at o momento as entradas de dados foram pelo teclado e a sada na tela. A quantidade de dados de entrada e de sada eram pequenas. Freqentemente, necessita-se ler grandes arquivos armazenados em disco, e tambm gravar grandes arquivos em disco. Nos computadores esta entidade que armazena dados em disco (ou mesmo em fitas ou outros dispositivas de armazenamento de dados) conhecida como FILE. Todo file constitudo de um conjunto de linhas chamadas de RECORD. O Fortran capaz de ler ou escrever informaes em um file, um record por vez. Quando se l dados em um arquivo, um record de cada vez, em ordem consecutiva, diz-se que este o acesso seqencial. Este tipo de acesso mais usado em unidades de fita magntica. Quando salta-se livremente de um record para outro sem ordem especfica, diz-se que este um acesso direto. A maneira de direcionar uma declarao read ou write para um determinado dispositivo de entrada ou sada de dados feita colocando o nmero correspondente daquela unidade no lugar do primeiro asterisco que aparece nas declaraes read e write usadas nos programas mostrados anteriormente. Vrias declaraes do Fortran so destinadas a controle este processo de ler ou escrever dados em um arquivo (file), as discutidas neste captulo esto na Tabela 4.2. Tabela 4.2-Declaraes Fortran de entrada e sada de dados Declarao de entrada ou sada Funo Associa um arquivo em disco com um nmero de open entrada ou sada. Termina a associao iniciada com a declarao close open L dados de uma unidade de entrada de dados read especificada Escreve dados em uma unidade de sada de dados write especificada Move o ponteiro para o comeo do arquivo rewind Move o ponteiro um record em um arquivo backspace
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
40

Forttran 90//95 For ran 90 95


4.2.1 A declarao OPEN

A declarao open associa um arquivo (file) com um nmero dado de unidade de entrada ou sada de dados. Sua forma
open(lista_open)

onde lista_open contm uma srie de especificaes especificando a unidade de entrada ou sada de dados, o nome do arquivo, e informaes de como acessar o arquivo. As vrias especificaes dentro da lista_open so separadas por vrgulas. Veremos neste captulo algumas destas especificaes, outras apenas em outros captulos mais adiante. As cinco especificaes mais importantes so: 1) UNIT = expressao_inteira: Esta especificao indica qual o nmero inteiro que vai se usar para associar com arquivo. 2) FILE = expressao_caracter: Esta especificao estabelece o nome do arquivo a ser aberto. 3) STATUS = expressao_caracter: onde expressao_caracter pode assumir os seguintes valores: OLD, NEW, REPLACE, SCRATCH, e UNKNOWN. A expresso old refere-se a arquivos j existentes, new refere-se a arquivos novos, unknown refere-se tanto a arquivos antigos quanto novos, scratch refere-se a arquivos que sero deletados pelo sistema quando a execuo do programa terminar, e replace significa que um arquivo vai ser aberto para sada de dados, independente da existncia de outro arquivo com o mesmo nome. 4) ACTION = expressao_caracter: Esta especificao indica se o arquivo deve ser aberto apenas para leitura, apenas para escrita, ou para a leitura e escrita. Nestes trs casos a expressao_caracter assume os seguintes valores: READ, WRITE, e READWRITE. Os prprios ttulos so auto-explicativos. 5) IOSTAT = variavel_inteira: Esta especificao indica o nome de uma varivel inteira na qual o status da operao open ser retornado. Se a abertura do arquivo bem sucedida o valor zero (inteiro) ser retornado na variavel_inteira. Se o processo no for bem sucedido o sistema retornar em variavel_inteira um nmero positivo correspondente ao cdigo de mensagem de erro. Abaixo segue alguns fragmentos de cdigo mostrando alguns casos de declaraes open
integer :: i_error open (UNIT=8, FILE=exemplo.dat, STATUS=old, ACTION=read, IOSTAT=i_error)
integer :: i_error,unit character(len=5) :: file_name unit=25 file_name=saida open (UNIT=unit, FILE=file_name, STATUS=REPLACE,& ACTION=WRITE, IOSTAT=i_error)

Boa Prtica

Sempre tenha cuidado ao especificar os argumentos da declarao OPEN, dependendo se est lendo ou escrevendo no arquivo. Esta prtica vai prevenir erros tais como escrever em cima de um arquivo que deseja-se preservar.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

41

Forttran 90//95 For ran 90 95


4.2.2 A declarao CLOSE

A declarao CLOSE fecha o arquivo e libera o nmero de entrada e sada que estava associado com aquele arquivo. O formato desta declarao
close(lista_close)

onde a lista_close deve conter o nmero de entrada e sada associado ao arquivo que se deseja fechar. Pode existir nesta lista outros argumentos que sero discutidos em outro captulo. Um exemplo o que fecha o arquivo associado ao nmero de entrado e sada igual a oito, como segue
close(UNIT = 8) ou simplesmente close(8) 4.2.3 READs e WRITEs a arquivos em disco

Uma vez que um arquivo foi conectado a um nmero de entrada e sada de dados atravs de um declarao open ser possvel ler do arquivo ou escrever no arquivo utilizando as declaraes read e write j utilizadas anteriormente. Por exemplo o seguinte bloco de declaraes
real :: x,y,z open (UNIT=8, FILE=exemplo.dat, STATUS=OLD, IOSTAT=i_error) read (8,*) x,y,x

ir ler no arquivo exemplo.dat, j existente os dados reais x, y, z em formato livre.


4.2.4 O uso da especificao IOSTAT= nas declaraes READ

A especificao IOSTAT= pode ser usada juntamente com a declarao READ (ou mesmo com WRITE) quando se estiver operando com arquivos em disco. A declarao read ficar ento com o seguinte aspecto
integer :: i_err, i read(7,10,IOSTAT=i_err) i

Neste a declarao read tentar ler a varivel inteira i no arquivo que estiver vinculado ao nmero 7 de entrada e sada de dados. O nmero 10 o label da declarao format que especificar a formatao de leitura. Tudo isto j se fazia antes. Da forma anterior, quando a leitura era bem sucedida o programa continuava a execuo. Quando a leitura era malsucedida o programa descontinuava. Com a introduo deste novo argumento IOSTAT=i_err, quando a leitura bem sucedida o sistema ir retornar um valor zero para i_err, e o programa continuar. Se a leitura for malsucedida o programa no descontinuar e retornar um valor no nulo para i_err, utilizando um IF est situao pode ser detectada e alguma providncia tomada para resolver o problema ou fazer uma terminao elegante do programa ao invs das terminao selvagens que ocorre quando este recurso no usado.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

42

Forttran 90//95 For ran 90 95

Boa Prtica

Sempre inclua a especificao IOSTAT= quando lendo (ou mesmo escrevendo) um arquivo de disco. Isto prov um meio gracioso de detectar excepcionalidades em operaes com arquivos.

4.2.5 Posicionamento em arquivos

Conforme falado anteriormente um dos tipos de arquivos em Fortran so os seqenciais, que devem ser lidos ou escritos um record aps em uma ordem consecutiva. s vezes necessrio ler um mesmo dado duas vezes, ou escrever duas vezes na mesma posio. O Fortran apresenta duas declaraes para resolver este tipo de problema. O BACKSPACE que move o ponteiro um record para trs de cada vez, e o REWIND que reposiciona o ponteiro no incio do arquivo. As formas destas duas declaraes so como segue
backspace (UNIT=unit_number)

e
rewind (UNIT=unit_number)

onde unit_number o nmero da unidade de entrada e sada qual est associado o arquivo. Estas duas declaraes podem ainda estar munidas da especificao IOSTAT=, para detectar possveis erros no posicionamento do arquivo, sem causar a descontinuao.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

43

Forttran 90//95 For ran 90 95


Captulo 5 - ARRAYS
Para facilitar as operaes sobre os dados de um programa, em geral interessante ordenar estes dados de alguma forma especial e dar-lhes um nome de tal forma que possam ser referenciados facilmente. Da matemtica tem-se os conceitos de vetor, matriz e tensor. Um vetor (tensor de ordem 1) uma entidade composta de vrios escalares com um definida ordem. Os vetores tm, digamos, um aspecto unidimensional, os dados so colocados em linha. Uma matriz (tensor de ordem 2) tambm tem seus componentes bsicos organizados, porm esto organizados com um aspecto bidimensional, os dados esto dispostos como se estivessem em um plano retangular. Estes tipos de estruturas quando esto dispostos de uma forma em que as dimenses sejam maiores que 2 em geral so chamados de tensores. Existe um paralelismo entre estes conceitos da matemtica e algumas formas de organizar dados em computao. Para evitar a proliferao de nomes, vamos aqui chamar estas formas organizadas de dados simplesmente de arrays. Os vetores computacionais seriam arrays unidimensionais (ou arrays com rank 1), as matrizes seriam arrays bidimensionais (ou arrays com rank 2), e assim por diante at o conceito de arrays n-dimensionais (ou arrays com rank n). Os componentes de um array so os elementos do array. Cada array identificado por um nome, e seus elementos por ndices que indicam a sua posio dentro do array. Desta forma as expresses a(7), b(6,9) e c(1,9,100) referem-se ao stimo elemento do array a, ao elemento na posio (6,9) do array b, e ao elemento (1,9,100) do array c. Nos arrays com rank 1 os dados subsequentes so armazenados em posies subsequentes na memria do computador. Em arrays com rank 2 o armazenamento do array faz-se seqencialmente pela primeira coluna (como em um array de rank 1), depois empilha-se seqencialmente a segunda coluna, e assim por diante at a ltima coluna. Olhando de outra forma, congela-se o ndice direito do array e varia o da esquerda, depois incrementa-se o ndice da direita e varia o da esquerda mais rpido, e assim sucessivamente, produz-se ento o mesmo efeito de empilhar as colunas. Generalizando para arrays n-dimensionais, isto pode ser dito ainda da seguinte forma: se imaginarmos os ndices do array como componentes de um ninho de DOs, tudo se passa como se os ndices mais direita fossem colocados nos laos mais internos dos DOs, de tal forma que o ndice mais direita correspondesse ao lao mais interno e o ndice mais esquerda correspondesse ao lao mais externo. O conceito e o uso de arrays facilita tremendamente a execuo das operaes de lgebra linear sobre um dado conjunto de dados.
5.1 Declarando arrays

Antes de comear a usar um array necessrio declar-lo, quanto ao tipo e quantidade de elementos possui, para informar ao compilador que tipo de dado ser armazenado e quanto ser usado de memria. Abaixo apresenta-se algum declaraes de arrays:
real, dimension(16) :: x

nesta declarao tem-se um vetor x com 16 elementos do tipo real;


Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
44

Forttran 90//95 For ran 90 95


character(len=25), dimension(100) :: y

nesta tem-se um vetor y com 100 elementos do tipo character(25);


integer, dimension(10,20,30) :: i

aqui tem-se um array de rank 3 x com 102030 elementos do tipo integer; O nmero de elementos do array em uma dada direo chama-se extenso do array naquela direo. A forma de um array definida pelo seu rank e pelas extenses do array nas vrias direes. Dois arrays tem a mesma forma se possuem o mesmo rank e as mesmas extenses em cada direo. O tamanho de um array o total de elementos declarados naquele array. Pode-se definir arrays constantes. Um array constante aquele que possui todos seus elementos constantes. Inicializa-se um array constante usando um construtor de array. Abaixo apresenta-se um exemplo de uso do construtor de array
real, dimension(5) :: w w = (/1.,2.,3.,4.,5./)

as duas /s existentes no exemplo acima funcionam como o incio e o final dos dados do array sendo inicializado.
5.2 Usando elementos de array em declaraes Fortran

Aqui apresenta-se algumas informaes de como utilizar elementos de arrays em declaraes Fortran.
5.2.1 Usando elementos de array em declaraes Fortran

Cada elemento de um array uma variavel tal como todas as outras j at o momento nos captulos anteriores, e portanto podem ser inseridas em qualquer espresso aritmtica ou lgica.
5.2.2 Inicializando elementos de array

Os elementos de um array podem ser inicializados por declarao de atribuio. Abaixo apresenta-se alguns exemplos
integer, dimension(3) :: k k(1) = 5 k(2) = 4 k(3) = 1

neste caso o array k, com elementos inteiros, foi inicializado utilizando um declarao para cada elemento. Para arrays pequenos isto possvel, para arrays grandes melhor utilizar estruturas de laos para efetuar tal tarefa. Assim vejamos o seguinte exemplo
integer, dimension(1000) :: k

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

45

Forttran 90//95 For ran 90 95


integer :: i do i = 1, 1000 k(i) = i*i end do

Boa Prtica

Sempre inicialize os elementos de um array antes de utiliz-lo.

O bloco de programa acima tambm podem ter sido inicializado utilizando-se um lao implcito (uma forma especial de lao do tipo DO). Como segue
integer, dimension(1000) :: k = (/(i, i = 1,1000)/)

tambm possvel fazer operaes mais complexas, como


integer, dimension(10) :: k = (/(0, i = 1,5),(2*j,j = 1,5)/) 5.2.3 Modificando a faixa de subscritos de um array

Os elementos de um array rank 1 so endereados normalmente numerando o primeiro de 1, o segundo de 2, e o n-simo de n. Isto pode ser diferente. Imagine um array com 11 elementos, seus elementos seriam normalmente numerados de 1 a 11. No entanto tambm poderamos numer-los de 5 a 5, que a quantidade de elementos seria os mesmo 11. O Fortran suporta este tipo de nomenclatura que expressa da seguinte forma para um array rank 1
real, dimension (valor_baixo:valor_alto) :: x

ou para um array rank 2


real, dimension (valor_baixo_1:valor_alto_1, valor_baixo_2:valor_alto_2) :: x

para ranks maiores procede-se de maneira semelhante ao rank 2. A dimenso numa dada direo (i) obtida por
extensao_i = valor_alto_i valor_baixo_i + 1 5.2.4 Subscritos de array fora da faixa de definio

Para um array rank 1 com, digamos 10 elementos, ao se tentar acessar o suposto dcimo quinto elemento, o que acontecer? Isto depende de processador para processador. Em alguns o programa rodando verificar se todos os elementos usados esto na faixa adequada. Caso seja identificado casos fora da faixa, o programa emitir um mensagem e descontinuar a execuo. Entretanto este processo torna os programas mais lentos. Assim em geral esta capacidade de fazer a verificao de limites de array opcional nos compiladores. Desta forma aconselhvel que esta capacidade fique ligada quando o programa estiver sendo criado e compilador.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

46

Forttran 90//95 For ran 90 95


Quando o programa estiver na forma final desejvel que a verso final seja compilada com a verificao limites de array desligada.
Sempre ligue a verificao de limites de arrays do compilador Fortran quando o programa estiver sendo desenvolvido e debugado, para ajudar a encontrar erros produzir por referncias a elementos de arrays fora de seus limites de definio. Desligue ligue a verificao de limites de arrays do compilador Fortran quando o programa estiver pronto para, se necessrio, obter maior velocidade de processamento.

Boa Prtica

5.2.5 O uso de constantes com declarao de arrays

O uso de constantes pode ser utilizado para melhorar e tornar mais claro a declarao de arrays. Alm disto a definio constante protege o valor contra a corrupo de seu valor. Abaixo mostra-se alguns exemplos de tal uso
integer, parameter :: tamanho = 1000 real :: array1(tamanho) real :: array2(tamanho+5) real :: array3(tamanho*tamanho) real :: array4(tamanho,tamanho)

Boa Prtica

Sempre use constantes para declarar o tamanho de arrays em programas Fortran, visando torn-los fceis de ler, e de modificar.

5.3 Usando arrays completos e sub-conjuntos de arrays

Conforme falado anteriormente possvel fazer as operaes clssicas da lgebra sobre os elementos de um array, numa estratgia elemento-a-elemento. Vamos ver nesta seo como fazer operaes sobre arrays completos ou sobre subconjuntos de arrays.
5.3.1 Operaes com arrays completos

Sob certas circunstncias possvel efetuar operaes sobre arrays completos com uma notao similar quela usada para variveis escalares. Se dois arrays tem a mesma forma, eles podem ser usados em algumas operaes aritmticas como se fossem escalares. Algumas funes intrnsecas aplicam-se a arrays completos. Abaixo mostra-se dois programas, um escrito na forma tradicional, ou escrito usando operaes sobre arrays completos. Forma tradicional
! Este programa mostra operaes sobre arrays, escritas da forma tradicional

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

47

Forttran 90//95 For ran 90 95


! usando laos de DO ! program operacao_sobre_arrays implicit none ! declaraes de variveis integer, parameter :: i_max=100 integer :: i,j character(1) :: word real, dimension(i_max,i_max) :: array1,array2,array3 ! inicializao dos arrays do i=1,i_max do j=1,i_max array1(i,j) = 1.5 array2(i,j) = 2.5 array3(i,j) = 0.0 end do end do !somando o array1 com o array2 e colocando no array3 do i=1,i_max do j=1,i_max array3(i,j) = array1(i,j) + array2(i,j) end do end do !calculando a raiz quadrada de dos elementos de array3 do i=1,i_max do j=1,i_max array3(i,j) = sqrt(array3(i,j)) end do end do !imprimindo os resultados do i=1,i_max do j=1,i_max write(*,*)array3(i,j) end do end do !criando um efeito pause write(*,*)'Digite algo!' read(*,*)word !terminao do programa stop end program operacao_sobre_arrays

Forma usando operaes arrays completos


! Este programa mostra operaes sobre arrays, escritas da forma usando ! operaes sobre arrays completos !

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

48

Forttran 90//95 For ran 90 95


program operacao_sobre_arrays implicit none ! declaraes de variveis integer, parameter :: i_max=100 character(1) :: word real, dimension(i_max,i_max) :: array1,array2,array3 ! inicializao dos arrays array1 = 1.5 array2 = 2.5 array3 = 0.0 !somando o array1 com o array2 e colocando no array3 array3 = array1 + array2 !calculando a raiz quadrada de dos elementos de array3 array3 = sqrt(array3) !imprimindo os resultados write(*,*)array3 !criando um efeito pause write(*,*)'Digite algo!' read(*,*)word !terminao do programa stop end program operacao_sobre_arrays

Fica claro que todos os resultados impressos so iguais a 2.0. Na operao com arrays completos basta que os arrays envolvidos numa operao tenha a mesma forma, no importando se as faixas das extenses de cada array em cada direo usem notao diferenciada. Se dois arrays no tem a mesma forma ento podero executar operaes do tipo array completo.
5.3.2 Definindo e operando com subconjuntos de arrays

Para definir um subconjunto de um array ou seo de array (array section) fazse necessrio usar uma trinca de ndices que tem a forma geral
indice_1 : indice_2 : incr

onde indice_1 o primeiro subscrito a ser includo no subconjunto do array; indice_2, o ltimo subscrito a ser includo; e incr o incremento que se far ao longo do array completo. Esta definio pode ser aplicada para cada direo do array. Abaixo apresenta-se alguns exemplos de definio de subconjuntos de arrays. Sejam os seguintes arrays originais
integer, dimension(7) :: array1 = (/1,2,3,4,5,6,7/) integer, dimension(4,4) :: array2 = (/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16/)

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

49

Forttran 90//95 For ran 90 95


assim pode-se definir os seguintes exemplos de subconjuntos de arrays
integer, dimension(7) :: array1 = (/1,2,3,4,5,6,7/) integer, dimension(4) :: array3 = array1(1:7:2) integer, dimension(5) :: array4 = array1(1:5:1) integer, dimension(4,4) :: array2 = (/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16/) integer, dimension(2,2) :: array5 = array2(1:2:1,3:4:1) integer, dimension(3,2) :: array6 = array2(1:3:1,1:2:1)

Um ou todos os elementos da trinca podem ser default. Se indice_1 no aparece na trinca o seu default o primeiro ndice de elemento para aquela direo. Se indice_2 no aparece na trinca o seu default o ltimo ndice de elemento para aquela direo, e incr no aparece na trinca o seu default a unidade. Todas as combinaes abaixo so possveis e legais
indice_1 : indice_2 : incr indice_1 : indice_2 indice_1 : indice_1 :: incr : indice_2 : indice_2 : incr :: incr : 5.3.3 Reformatando arrays

Um array rank 1 com cem elementos pode ser reformatado para, por exemplo, para um array rank 2 (1010) ou mesmo um array rank 2 (520), mantendo os valores e o mesmo nome. Para tal utiliza-se a funo intrnseca RESHAPE cuja formato
array3 = reshape(array1, array2)

onde array1 contm os dados para reformatar e array2 um array rank 1 descrevendo a nova forma. O nmero de elementos em array2 igual ao nmero de dimenses no array de sada e os seus valores igual dimenso em cada direo. O nmero de elementos em array1 deve o mesmo nmero de elemento para forma especificada para array2, caso contrrio a declarao reshape ir falhar. O array3 o resultado da reformataro, quando bem sucedida. Abaixo apresenta-se um programa que reformata um vetor com cem elementos para uma matriz quadrada (1010).
! Este programa mostra a reformatao de arrays. ! program reformatacao_de_arrays implicit none

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

50

Forttran 90//95 For ran 90 95


! declaraes de variveis integer, parameter :: i_max=100,j_max =10 integer :: i real, dimension(i_max) :: array1 real, dimension(j_max,j_max) :: array3 integer, dimension(2) :: array2 = (/10,10/) ! inicializao do array1 do i=1,i_max array1(i)=i end do !reformatando o array1 para array3 array3 = reshape(array1,array2) stop end program reformatacao_de_arrays

Como o construtor de arrays s funciona com arrays rank 1, a declarao reshape serve para transformar arrays criados rank 1 em arrays que no sejam rank 1.
5.4 Usando funes intrnsecas Fortran com arrays

O Fortran 90/95 tm trs tipos de funes intrnsecas: funes elementares (elemental functions), funes de questionamento (inquiry functions), e funes de transformao (transformational functions). Algumas funes de cada um destes grupos so projetadas para operar com arrays.
5.4.1 Funes intrnsecas elementares

So funes que projetadas para operar com escalares trabalham tambm com arrays. Se o argumento de uma funo elementar um escalar o resultado ser um escalar, se o argumento um array o resultado ser tambm um array. Estes dois arrays devero ter o mesmo formato. A maioria das funes intrnsecas que aceitam escalares como argumentos tambm aceitam arrays. Alguns exemplos so
y = sin(x) y = cos(x) y = sqrt(x)

onde x e y ou so ambos escalares ou so dois arrays de mesmo formato.


5.4.2 Funes intrnsecas de questionamento

Funes intrnsecas de questionamento so funes cujo resultado depende do formato e da natureza do argumento. Por exemplo LBOUND(force) uma funo de questionamento que retorna o menor subscrito do array force. Na Tabela 5.1 apresenta-se algumas destas funes mais usadas.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

51

Forttran 90//95 For ran 90 95


Tabela 5.1-Algumas funes comuns de questionamento Funo e seus argumentos Utilidade Determina se um array alocvel est alocado ou no. allocated(array) Retorna todos os limites inferiores das dimenses de um lbound(array,dim) array se dim no est presente (o retorno um array). Se dim est presente retornar o menor dos limites inferiores (o retorno um escalar) Retorna a forma de um array shape(array) Retorna a dimenso do array em cada direo se dim size(array,dim) est presente. Caso contrrio retorna o tamanho do array Retorna todos os limites superiores das dimenses de um ubound(array,dim) array se dim no est presente (o retorno um array). Se dim est presente retornar o maior dos limites superiores (o retorno um escalar)
5.4.3 Funes intrnsecas de transformao

Funes intrnsecas de transformao so funes que tem um ou mais arrays como argumento ou um array como resultado. Este tipo de funes no operam sobre escalares, apenas sobre arrays completos. Freqentemente o resultado deste tipo de funo um array com formato diferente dos arrays de entrada. O Fortran 90/95 tm muitas funes intrnsecas de transformao, algumas so apresentadas na Tabela 5.2. Tabela 5.2-Algumas funes de transformao comuns. Funo e agumentos Utilidde all(mask) Funo lgica, retorna TRUE se todos os valores no array mask so TRUE any(mask) Funo lgica, retorna TRUE se qualquer dos valores no array mask TRUE count(mask) Retorna a quantidade de elementos TRUE em mask dot_product(vet_1,vet_2) Calcula o produto interno de dois arrays rank 1 de mesmo formato matmul(matriz_1,matriz_2) Multiplica duas matrizes conformveis maxloc(array,mask) Retorna o local onde ocorre o elemento de maior valore entre aqueles para os quais MASK TRUE. O resultado um array rank 1 contendo os subscritos da posio. minloc(array,mask) Retorna o local onde ocorre o elemento de menor valore entre aqueles para os quais MASK TRUE. O resultado um array rank 1 contendo os subscritos da posio. minval(array,mask) Retorna o menor valor no array entre aqueles para os quais MASK TRUE. (mask opcional) product(array,mask) Calcula o produto dos elementos de um array para os quais MASK TRUE. MASK opcional, se no estiver presente o produto feito com todos os elementos do array.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

52

Forttran 90//95 For ran 90 95


reshape(array1,array2) sum(array,mask) Constri um novo array a partir do array1 e das informaes do novo array contidas em array2 rank 1. Calcula a soma dos elementos de um array para os quais MASK TRUE. MASK opcional, se no estiver presente a soma feito com todos os elementos do array. retorna a transposta de um array rank 2. Retorna o maior valor no array entre aqueles para os quais MASK TRUE. (mask opcional)

transpose(matriz) maxval(array,mask)

5.5 Atribuio de arrays com mscara: a estrutura WHERE

Considere a necessidade de se fazer algum tipo de operao apenas sobre alguns elementos de um array e outro tipo sobre os restantes. O Fortran 90/95 suporta operao sobre array considerando uma mscara que informa sobre quais elementos deve-se fazer ama operao e quais devem ficar com a outra operao. Esta mscara em realidade um array lgico com o mesmo formato do array a ser operado. Deve-se colocar nesta mscara, nas posies correspondentes s quais deseja-se fazer a primeira operao no array, valores igual a TRUE, e no restante das posies coloca-se FALSE.
5.5.1 A estrutura WHERE do Fortran 90

A forma geral da estrutura WHERE do Fortran


{nome:} where (mask) declaracoes_com_array_1 elsewhere {nome} declaracoes_com_array_2 end where {nome}

No cdigo acima faz-se declaracoes_com_array_1 sobre todos os elementos do array para os quais os elementos correspondentes de mask TRUE. Para aquelas posies correspondentes aos elementos com valor FALSE em mask aplica-se declaracoes_com_array_2.

Boa Prtica

Use a estrutura WHERE para modificar somente os elementos de um array que passem em um determinado teste .

5.5.2 A estrutura WHERE do Fortran 95

O Fortran 95 apresenta uma forma mais complexa de estrutura WHERE, onde permite-se o uso de vrios ELSEWHERE com mscara e apenas um ELSEWHERE sem mscara. A forma geral da estrutura WHERE do Fortran 95
{nome:} where (mask_1) declaracoes_com_array_1 Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
53

Forttran 90//95 For ran 90 95


elsewhere (mask_2) {nome} declaracoes_com_array_2 elsewhere {nome} declaracoes_com_array_3 end where {nome} 5.5.3 A declarao WHERE

O Fortran 90/95 tambm tem uma estrutura WHERE com apenas um linha de declarao. Sua forma
where(mask) declaracoes_com_array

neste caso ser feito determinado conjunto de operaes apenas sobre os elementos do array que correspondem aos elementos com valores TRUE em mask.
5.6. A estrutura FORALL

O Fortran 95 apresenta um novo tipo de estrutura, o FORALL, que foi projetado para permitir um conjunto de operaes a serem aplicadas a um conjunto de elementos de um array. Os elementos a serem operados so definidos pelos ndices e tambm por uma expresso lgica. As operaes ocorrero apenas sobre os elementos do array que atendam as restries contidas nos ndices bem como aquela contida numa expresso lgica.
5.6.1 A forma da estrutura FORALL

A forma geral da estrutura FORALL


{nome:} forall (in_1 = trinca_1, in_2 = trinca_2,, expressao_logica) declaracoes_executaveis end forall {nome}

Cada ndice na declarao FORALL especificado por uma trinca, na forma


trinca_1 = indice_1 : indice_2 : incr

j definidos anteriormente quando tratamos de subconjuntos de arrays.


5.7. Arrays alocveis

Todos os arrays utilizados at o momento tiveram suas dimenses definidas durante o perodo de compilao e no podem ser alteradas durante a execuo do programa. Esta a chamada alocao esttica de memria, uma vez que o tamanho dos arrays nunca muda durante a execuo do programa. desejvel que o tamanho de um array varie durante a execuo para se moldar s necessidades de armazenamento, que variam de caso a caso sendo executado. Uma melhor soluo

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

54

Forttran 90//95 For ran 90 95


desenvolver um programa que use alocao dinmica de memria. Neste caso o programa define automaticamente o tamanho dos arrays de acordo com a necessidade e durante o perodo de execuo. Um array em Fortran que ter suas dimenses definidas dinamicamente, dever ser declarado alocvel usando o atributo ALLOCATABLE na declarao de tipo de varivel, e alocado usando a declarao executvel ALLOCATE. Quando um array alocvel no vai mais ser utilizado, deve-se liberar a memria que ele ocupava, utilizando a declarao executvel DEALLOCATE. Para dotar um array com o atributo de alocvel procede-se como segue
real, allocatable, dimension(:,:) :: array1

note que na declarao acima o tipo do array fica definido, mas no o seu tamanho. Para o programa definir durante a execuo o tamanho do array necessrio o uso da declarao executvel ALLOCATE que tem a seguinte forma
allocate (lista_de_arrays_para_alocar, STAT = status)

Um exemplo tpico
allocate(array1(50,-10:50), STAT = status)

que um array de 5061 elementos.


Sempre inclua a especificao STAT = em qualquer declarao ALLOCATE, e sempre verifique o valor retornado, de tal forma que o programa termine de maneira elegante sempre que a memria a alocar para os arrays seja insuficiente .

Boa Prtica

Caso se tenha dvida se um array alocvel j est alocado, pode-se descobrir esta informao usando a funo de questionamento ALLOCATED(array). Se array j estiver alocado o retorno ser TRUE, caso contrrio ser FALSE. Aps utilizar um array alocvel necessrio desaloc-lo para liberar memria til para outros propsitos. Isto pode ser feito usando a declarao executvel DEALLOCATE. A forma geral desta declarao
deallocate (lista_de_arrays_para_desalocar, STAT = status)

Um exemplo caracterstico
deallocate(array1, STAT = status)

onde a especificao STAT = tem o mesmo significado daquela usado na declarao ALLOCATE.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

55

Forttran 90//95 For ran 90 95

Boa Prtica

Sempre utilize desalocamento dinmico de arrays usando uma declarao DEALLOCATE, assim que os mesmos no for mais utilizado.

Abaixo apresenta-se um programa que faz a alocao dinmica de uma matriz.


!Programa para mostrar a alocacao dinmica de arrays. ! ! Programador: Joo Batista Aparecido ! Ilha Solteira, December 9, 1999. ! program alocacao_dinamica implicit none !declara variveis integer :: i, status real, allocatable, dimension(:,:) :: x !array alocvel !Entra com o inteiro i write(*,*)'Digite i (inteiro) = ' read(*,*)i !Faz a alocacao de memria para o array x allocate(x(i,i), stat=status) !Atribui valores ao elementos do array x x = 5.0 !Desaloca a memria ocupada por x deallocate(x,stat = status) !Terminao do programa stop end program alocacao_dinamica

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

56

Forttran 90//95 For ran 90 95


Captulo 6 - Funes e Subrotinas
No primeiro livro considerado de cincia poltica, escrito no entorno de 1500, o autor Nicolau Maquiavel, j ensinava: dividir para conquistar. Em mtodos numricos esta idia muito utilizada. Talvez seria melhor neste caso adapt-la para: dividir para calcular. E fazendo um paralelo na rea de programao computacional, poderamos tambm dizer: dividir para programar. A questo ento como dividir um programa? Em Fortran tem-se o programa principal (nico) e programas auxiliares, tambm chamados de sub-programas. Estes programas so usados da seguinte maneira: Quando o programa principal vai sendo executado e encontra uma referncia a um sub-programa, este invocado, o programa principal passa-lhe as variveis necessrias, o sub-programa recebe o controle da execuo, executa as declaraes, termina a execuo, e passa de volta o controle da execuo ao programa principal. E assim sucessivamente tantos quantos sub-programas sejam encontrados pelo programa principal durante a execuo. Existem dois tipos principais de sub-programas: as funes (functions) e as subrotinas (subroutines). As funes recebem um ou mais argumentos do programa e retornam apenas um argumento, podendo ser um array. As subrotinas recebem um ou mais argumentos do programa e retornam um ou mais argumentos para o programa. Pode-se ter subrotinas que no retornem nenhum argumento para o programa. Subrotinas e funes podem chamar outras subrotinas e funes. O uso de subrotinas e funes e vantajoso porque permite dividir um programa que seria muito grande em partes modulares. Estas partes podem ser desenvolvidas de um maneira razoavelmente independente, podendo ser editar e compiladas separadamente. O debugamento destas rotinas em separado torna-se mais fcil do que se estivessem em um programa nico. Uma outra vantagem que isto facilita o trabalho em equipe, divide-se tarefas para diferentes programadores e depois vai-se integrando o resultado final. Pode-se ter elementos de grupos de trabalhos mesmo em pases diferentes. Para estes grandes grupos naturalmente necessrio a existncia de um gerente de projeto. O uso de subrotinas e funes tambm facilitam a manuteno do programa. Se as subrotinas e funes estiverem bem desenvolvidas, robustas e precisas, podero ser reusadas em qualquer outro programa que seja necessrio, sem necessidade de desenvolv-las de novo. Devido ao fato de que as variveis de uma function ou subroutine so na sua maioria locais, isto produz um certo grau de encapsulamento dos dados, evitando em parte que a execuo de um subrotina ou funo corrompa os dados do programa principal ou mesmo de outras subrotinas ou funes. Para invocar functions dentro de um programa Fortran basta mencionar seu nome em uma expresso aritmtica ou lgica. Para invocar subroutines necessrio utilizar a declarao CALL antes do nome da subroutine.

Boa Prtica

Divida grandes programas em sub-programas para beneficiar-se das vantagens de teste independente, reuso do cdigo, e encapsulamento (parcial) de dados.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

57

Forttran 90//95 For ran 90 95


6.1 Subrotinas

Uma subrotina um sub-programa Fortran que invocado utilizando a declarao CALL antes do nome da subrotina, ela recebe alguns argumentos de entrada e retorna outros argumentos de sada. A forma geral de declarar uma subrotina
subroutine {nome} (lista_de_argumentos) declaracoes_no_executaveis declaracoes_executaveis return end subroutine {nome}

No bloco de cdigo acima a primeira linha marca o incio da subrotina e a ltima linha marca o final da subrotina. A declarao return retorna o controle da execuo ao programa principal, da mesma forma que um programa pode ter vrios STOPs, uma subrotina pode ter vrios RETURNs. O nome da subrotina pode ter at 31 caracters porm o primeiro deve ser alfabtico. Na lista _de_ argumentos tm-se as variveis e arrays que sero passadas pelo programa principal quando invocar a subrotina. Como uma subrotina (ou funo) um programa independente pode-se utilizar variveis com os mesmos nomes de variveis usadas no programa principal e outras subrotinas sem a possibilidade de conflito. Uma subrotina no pode chamar a si mesma, a no que tenha sido declarada recursiva (recursive). A forma de invocar uma subrotina como segue
call {nome} (lista_de_argumentos)

onde os argumentos na lista_de_argumentos deve ter uma relao biunvoca com os listados na declarao de argumentos da subrotinas, quanto quantidade, ao tipo e ao uso que se destina.

Cuidado

Confira que os argumentos listados no programa principal e em um subrotina estejam adequadamente representados em nmero, ordem e tipo.

Abaixo apresenta-se um programa que tem uma subrotina


program soma_dois_reais !O objetivo deste programa eh mostrar o uso de uma subrotina ! !Programmer: joao batista aparecido ilha solteira, 11 dezembro 1999 ! implicit none real :: x = 1.5, y = 2.5, z !invocando a subrotina call soma(x,y,z)

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

58

Forttran 90//95 For ran 90 95


stop end program soma_dois_reais subroutine soma(a,b,c) implicit none real :: a,b,c !soma dois reais c=a+b return end subroutine soma

Este programa quando executado a passagem de dados feita de uma forma apropriada e o resultado produzido o correto, z = 4.0. Quando um programa passa um argumento para uma subrotina, em realidade ele est passando um ponteiro que aponta para a posio de memria onde aquela varivel est, com isto a subrotina pode fazer uso do seu valor. Fica claro que a subrotina pode modificar o valor que est naquele endereo mediante uma simples operao de atribuio. Em realidade isto que ocorre com os chamados argumentos de retorno, a subrotina escreve algo naquela posio de memria que foi indicada pelo programa principal, a questo se de entrada ou de sada (at este ponto) est mais na cabea do programador. Percebe-se ento que possvel haver a corrupo dos dados do programa principal atravs de uma alterao indevida ocorrida em uma subrotina. A seguir apresenta-se um programa onde pode haver algum tipo de corrupo de dados
program corrupcao_de_dados !O objetivo deste programa eh mostrar a corrupcao de !dados quando se utiliza subrotinas ! !Programmer: joao batista aparecido ilha solteira, 11 dezembro 1999 ! implicit none real :: x = 1.5, y = 2.5, z !invocando a subrotina call soma(x,y,z) stop end program corrupcao_de_dados subroutine soma(a,b,c) implicit none real :: a,b,c !corrompendo o dado b. Isto pode ocorrer por digitacao distraida em um cdigo !grande, por fragmentos de codigo nao deletado e assim por diante. b=b*2 !soma dois reais c=a+b

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

59

Forttran 90//95 For ran 90 95


return end subroutine soma

Neste caso o resultado deveria ser z = 4.0 no entanto o resultado ser z = 6.5, isto ocorreu porque uma operao dentro da subrotina que alterou o valor que estava armazenado no endereo da varivel y, neste ficam os valores e y e z corrompidos. Dependendo da estrutura do programa a informao corrompida poder se propagar contaminado os resultados intermedirios e o resultado final. Agora apresenta-se outro caso de corrupo de dados, via definio incorreta dos tipos das variveis na subrotina
program corrupcao_de_dados_2 !O objetivo deste programa eh mostrar a corrupcao de dados !quando se utiliza subrotinas ! !Programmer: joao batista aparecido ilha solteira, 11 dezembro 1999 ! implicit none real :: x = 1.5, y = 2.5, z !invocando a subrotina call soma(x,y,z) stop end program corrupcao_de_dados_2 subroutine soma(a,b,c) implicit none !introduzindo a corrupo nos dados. Via uma declarao & !incorreta de tipo na subrotina integer :: a,b,c !soma dois reais c=a+b return end subroutine soma

A execuo deste programa no ir corromper o valor da varivel y mas ir produzir um resultado totalmente errado para a varivel z. Apresenta-se a seguir a soluo para os problemas de corrupo de dados mostrados no primeiro exemplo, posteriormente retornaremos aos apresentados no segundo exemplo.
6.1.1 O atributo INTENT

Percebe-se da anlise dos dois exemplos apresentados acima que duas formas de corrupo de dados so: via a modificao acidental do valor das variveis de entrada

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

60

Forttran 90//95 For ran 90 95


ou sada; ou a definio conflitante dos tipos das variveis no programa principal e na subrotina, neste caso haver a corrupo dos resultados gerados, ou seja os argumentos de sada. A declarao INTENT especifica em uma subrotina quais argumentos so de entrada, quais so de sada e quais so de entrada e sada. Com isto o programa quando estiver executando no permitir que um subrotina faa alteraes no valor de uma varivel de entrada. Este mecanismo refora o encapsulamento de dados que ocorre na execuo de uma subrotina. A declarao INTENT um atributo da varivel e deve ser declarado no local de declarao de variveis, ou seja no incio da subrotina. Este atributo pode tomar as seguintes trs formas intent(in) Usado apenas para passar dados de entrada para a subrotina intent(out) Usado apenas para passar dados de sada para o programa principal intent(inout) Usado nos dois casos, para receber e passar dados. O objetivo do atributo INTENT dizer ao compilador como o programador quer usar cada um dos argumentos listados. Isto ajudar a detectar erros ainda durante a fase de compilao. Apresenta-se abaixo o primeiro programa exemplo sob corrupo de dados, apresentado na seo anterior, agora escrito utilizando o atributo INTENT.
program eliminando_corrupcao_de_dados !O objetivo deste programa eh mostrar a corrupcao de dados quando !se utiliza subrotinas ! !Programmer: joao batista aparecido ilha solteira, 11 dezembro 1999 ! implicit none real :: x = 1.5, y = 2.5, z !invocando a subrotina call soma(x,y,z) stop end program eliminando_corrupcao_de_dados subroutine soma(a,b,c) implicit none real, intent(in) :: a,b real, intent(out) :: c !corrompendo o dado b. Isto pode ocorrer por digitacao distraida !em um cdigo grande, !por fragmentos de codigo nao deletado e assim por diante. b=b*2 !soma dois reais c=a+b return

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

61

Forttran 90//95 For ran 90 95


end subroutine soma

Quando da compilao, o compilador acusar de imediato o erro, e mostrar claramente qual o erro e onde encontra-se. Basta compilar para ver!

Boa Prtica

Sempre declare o atributo INTENT para todos os argumentos de uma subrotina.

6.1.2 Passando arrays para subrotinas

Como falado anteriormente, um escalar passando do programa principal para um subrotina simplesmente passando um ponteiro que aponta para a varivel. Se o argumento um array o programa principal passa um ponteiro que aponta para o primeiro elemento do array. A pergunta que surge : Como a subrotina fica sabendo do da forma e das extenses do array? O rank do array ser definido no atributo dimension colocado na subrotina, mas ainda fica faltando a informao sobre a extenso do array em cada direo. Existem trs formas de informar subrotina quais as extenses do array em cada direo. A primeira forma de se fazer isto passar as extenses do array via argumentos da prpria subrotina e ento utilizar esta informao em uma declarao de atributo dimension e definir por completo a forma do array. Os arrays passados desta forma so denominados de arrays com forma explicita, uma vez que sua forma passada explicitamente. A seguir apresenta-se um programa que utiliza este tipo de passagem de argumentos do tipo array
program passando_array_1 !Objetivo: Mostrar a passagem de um array do program principal !para um subrotina de forma explicita ! !programmer: joao batista aparecido ilha solteira, 11 dezembro 1999 ! implicit none integer, parameter :: i_max = 100, j_max = 120 real, dimension(i_max,j_max) :: array1, array2 !atribuio de valores aos arrays array1=5.0 !invocando a subrotina passa_array call passa_array(i_max, j_max, array1, array2) stop end subroutine passa_array(i,j,x,y) implicit none integer, intent(in) :: i, j Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
62

Forttran 90//95 For ran 90 95


real, intent(in), dimension(i,j) :: x real, intent(out), dimension(i,j) :: y y = sqrt(x) return end subroutine passa_array

Note que este tipo de passagem de array suporta operaes sobre arrays completos. Uma segunda maneira de se passar as informaes de um array para o compilador declarar na subrotina cada extenso do array com um asterisco. Neste caso o compilador no sabe nada sobre as dimenses do array, e as operaes de verificao de limites do array e operaes arrays inteiros no so permitidas. Os arrays passados desta forma so chamados de arrays de tamanho presumido. O programa mostrado acima adaptado para fazer passagem de array neste novo formato, a seguir o cdigo
program passando_array_2 !Objetivo: Mostrar a passagem de um array do program principal !para um subrotina de forma implicita ! !programmer: joao batista aparecido ilha solteira, 11 dezembro 1999 ! implicit none integer, parameter :: i_max = 100, j_max = 120 real, dimension(i_max,j_max) :: array1, array2 !atribuio de valores aos arrays array1=5.0 !invocando a subrotina passa_array call passa_array(i_max, j_max, array1, array2) stop end subroutine passa_array(i,j,x,y) implicit none integer, intent(in) :: i, j integer :: m !note a seguir os asteriscos nos atributos dimension real, intent(in), dimension(*) :: x real, intent(out), dimension(*) :: y do m=1,i*j y(m) = sqrt(x(m)) end do

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

63

Forttran 90//95 For ran 90 95


return end subroutine passa_array

Note que neste caso o programa no suporta a operao sobre array completo y = sqrt(x), intuitivo que no lugar desta expresso devssemos colocar dois laos de DO uma vez que no programa principal estes arrays so rank 2. No entanto isto no funcionou, porque como o compilador no sabe nada sobre o rank nem as extenses do array em cada direo, ele interpreta que trata-se um um array rank 1, no formato em que os dados so armazenados na memria. Ento ao invs de colocar dois DOs, para cobrir as duas extenses dos arrays rank 2, tive que colocar apenas um DO com lao igual ao tamanho (quantidade de elementos) do array. A ento funciona.

Cuidado

Nunca use passagem de arrays para subrotinas pelo mtodo do tamanho presumido..

Uma terceira forma de fazer a passagem de array utilizando arrays de forma presumida juntamente com uma interface explicita para a subrotina. Veremos mais adiante neste captulo como fazer isto.

Boa Prtica

Sempre use passagem de arrays pelo mtodo da forma explicita ou da forma assumida com interface explicita..

6.1.3 Passando variveis caracter para subrotinas

Quando uma varivel caracter passada para um subrotina no necessrio passar o tamanho da varivel, bastando colocar um asterisco no local onde apareceria o seu tamanho, no cdigo da subrotina. Se for necessrio saber o tamanho da varivel caracter pode-se usar a funo intrnseca LEN(variavel_caracter). Ver exemplo abaixo
program passando_caracter implicit none character(len = 5) :: word = 'manga' call caracter(word) stop end program passando_caracter subroutine caracter(x) implicit none character(len = *)x write(*,*)'O tamanho da varivel caracter ',x,' eh = ',len(x)

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

64

Forttran 90//95 For ran 90 95


return end subroutine caracter 6.2 A declarao e o atributo SAVE

Os valores de todas as variveis locais de um subrotinas tornam-se indefinidos toda vez que a subrotina termina de executar. Surge ento a seguinte pergunta: Quando a subrotina for acessada por um segunda vez qual ser o valor inicial assumido por uma determinada varivel local da subrotina? Pela afirmao na primeira linha deste pargrafo pode-se dizer que o valor ser indeterminado, uma vez que o programa poder estar destinando um novo endereo na memria para aquela varivel local. Pode acontecer tambm a posio de memria ocupada pela varivel local pela primeira vez tenha sido usada posteriormente para outra finalidade e o seu valor tenha mudado. De qualquer forma evite fiar-se em valores volteis armazenados na memria entre um chamada e outra da subrotina, embora algumas vezes estes valores parecem permanentes. O Fortran prov um mecanismos para tornar perene o valor de uma varivel local de uma subrotina, normalmente voltil entre duas chamadas. o atributo SAVE que perenizar o valor da varivel local entre chamadas consecutivas de uma subrotina. Sua declarao como segue
real, save :: lista_de variveis integer, save, dimension (5,6) :: x,y 6.3 Arrays automticos

Aprendeu-se no captulo anterior a alocar arrays alocveis em programas principais bem como a desaloc-los. Pode-se fazer o mesmo em subrotinas, no entanto existe uma forma mais fcil e prtica de conseguir o mesmo efeito. Considerando que toda varivel local de subrotina voltil, ou seja ela torna-se indefinida depois que a execuo da subrotina termina, pode-se criar arrays locais, passando apenas as extenses do array via argumentos da subrotina. Veja a seguinte subrotina criando um array rank 2 local chamado array1 com extenses p e q
subroutine array_automatico(p,q) implicit none real, dimension array1(p,q,) outras_declaracoes_executaveis return end subroutine array_automatico Use arrays automticos para criar locais temporrios de trabalho em subrotinas. Use arrays alocveis para criar locais de trabalho temporrios de trabalho no programa principal ou que vai ser criado em uma subrotina e destrudo em outra. Em

Boa Prtica

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

65

Forttran 90//95 For ran 90 95


Fortran 95 os arrays alocveis criados dentro de subrotinas so automaticamente destrudos assim que a execuo da rotina termina, exceto aqueles que receberam o atributo SAVE. 6.4 Compartilhando dados usando mdulos (modules)

Pelo visto at o momento o programa principal e as subrotinas so programas distintos que trocam informaes entre si, via uma lista de argumentos. O Fortran 90/95 possui uma nova forma de passar dados (e outras informaes tambm, como veremos mais adiante) que mais flexvel e segura. Assim programas Fortran, subrotinas e funes podem compartilhar informaes usando mdulos (modules). Mdulo uma unidade de programa compilada separadamente que as definies e valores iniciais dos dados que deseja-se compartilhar entre o programa e os sub-programas. Para uma unidade de programa acessar os dados no mdulo, basta mencionar o nome do mdulo logo aps a declarao USE. Esta declarao deve ir logo aps a declarao do nome do programa ou sub-programa. Para que os dados contidos no mdulo no fiquem volteis necessrio utilizar a declarao SAVE na seo de declarao de variveis. A forma geral de definir um mdulo
module nome_do_modulo declaracoes_nao_executaveis end module nome_do_modulo

A forma geral de invocar um mdulo usar a seguinte declarao


use nome_do_modulo

na primeira linha aps o cabealho do programa ou sub-programa. A seguir temos um exemplo de um mdulo para compartilhar constantes.
module constantes implicit none save !definindo as constantes real :: pi = 3.14159, e = 2.718281828 end module constantes program modulo !Objetivo: Mostrar o uso de modulos para compartilhar dados entre programas. ! !programmer: joao batista aparecido ilha solteira, 11 de dezembro 1999 ! use constantes implicit none integer, parameter :: i_max = 10 real, dimension(i_max) :: x,y,z integer :: i do i=1,i_max x(i) = sqrt(real(i)/i_max) y(i) = cos(real(i)*pi*x(i))

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

66

Forttran 90//95 For ran 90 95


end do call sub_seno(i_max,x,z) stop end program modulo subroutine sub_seno(m,t,s) use constantes implicit none integer :: i,m real, dimension(m) :: t,s do i=1,m s(i) = sin(real(i)*pi*t(i)) end do end subroutine sub_seno

6.5 Sub-programas mdulo

Alm de dados os mdulos podem conter subrotinas e funes, que so chamadas ento subprogramas-mdulo. Estes subprogramas-mdulo so compilados como componentes do mdulo e so disponibilizados para as unidades de programa atravs do uso da declarao USE. Os subprogramas inclusos em um mdulo devem vir depois da declarao de dados compartilhados e devem ser precedidos pela declarao CONTAINS. Esta declarao diz ao compilador que o restante de cdigo so subprogramas inclusos. Assim um mdulo pode conter uma coleo de subrotinas, como segue
module banco implicit none save real :: pi = 3.14159, e = 2.718281828 contains subroutine sub1(x,y) implicit none real, intent(in) :: x real, intent(out) :: y y=sin(pi*x) return end subroutine sub1 subroutine sub2(m,x,y) implicit none real, intent(in) :: x real, intent(out) :: y integer, intent(in) :: m y = exp(real(m)*x)/e

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

67

Forttran 90//95 For ran 90 95


return end subroutine sub2 end module banco program banco_de_rotinas use banco implicit none real :: a = 2.0, b, c integer :: j = 5 call sub1(a,b) call sub2(j,a,c) stop end program banco_de_rotinas

6.5.1 Usando mdulos para criar interfaces explicitas

Sabe-se que possvel criar e compilar subrotinas separadamente e depois coloc-las juntas usando o linker. Ento porque ter o trabalho extra de colocar estas subrotinas em um mdulo, compilar e depois lincar de novo? A razo que o mdulo prov uma maneira superior de compartilhamento de dados, de tal forma que mais informaes sobre as subrotinas estaro disponveis ao compilador, que poder ento fazer um melhor controle do programa. Quando o programa (usando mdulo) compilado, o compilador verifica o nmero de argumentos, os tipos dos argumentos, se o argumento um array ou no, os atributos INTENT de cada argumento, e assim por diante. Um sub-programa compilado dentro de um mdulo e acessado por uma declarao USE tm uma interface explicita. Ento o compilador do Fortran automaticamente sabe explicitamente todos os detalhes sobre cada argumento no subprograma sempre que este usado e verifica a interface para confirmar que est sendo usada adequadamente. Quando os subprogramas no esto dentro de um mdulo o compilador tm pouca informao sobre os subprogramas. Estes subprogramas tm um interface implcita. Um exemplo disto o programa mostrado acima chamado corrupcao_de_dados_2 onde os argumentos da subrotina esto declarados de forma errada. Enquanto deveriam ser reais, esto declarados como inteiros. Mais adiante mostraremos a soluo para evitar este tipo de corrupo de dados utilizando o conceito de mdulo. Quando compilando um programa deste tipo, o compilador apenas assume que tudo transcorra bem, os seja que a quantidade e os tipos dos argumentos esto corretos, que esto na ordem correta, e que o tamanho dos arrays esto corretos. Se algo estiver errado o programa falhar totalmente. Mostra-se abaixo o programa corrupcao_de_dados_3 que o corrupcao_de_dados_2 com a subrotina escrita dentro de um mdulo.
module auxiliar contains subroutine soma(a,b,c) implicit none !introduzindo a corrupo nos dados. Via uma declarao&

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

68

Forttran 90//95 For ran 90 95


!incorreta de tipo na subrotina integer :: a,b,c !soma dois reais c=a+b return end subroutine soma end module auxiliar program corrupcao_de_dados_3 !O objetivo deste programa eh mostrar a corrupcao de dados quando !se utiliza subrotinas ! !Programmer: joao batista aparecido ilha solteira, 11 dezembro 1999 ! use auxiliar implicit none real :: x = 1.5, y = 2.5, z !invocando a subrotina call soma(x,y,z) stop end program corrupcao_de_dados_3

Aps a compilao deste programa (usando o Compildor da Microsoft V4.0) percebe-se que o resultado no to bom quanto esperado. O compilador, dependendo das diretivas internas (trs: no warning, normal warning, and warning treated as error) pode acontecer trs coisas: 1) na opo no warning, no emite nada, ignorando o erro; 2) na opo normal warning, identifica claramente o erro mas emite mensagem apenas de warning; e 3) na opo warning treated as error o compilador emitiu os mesmos warnings do item 2, mas identificou que tinha um no global um erro. digno de nota que o resultado apresentado quando compilando os programas corrupcao_de_dados_3 e corrupcao_de_dados_2 foram praticamente identicos, ou seja este compilador est implicitamente instrudos a fazer a verificao dos argumentos de um subrotina mesmo quando no so uma interface explicita, a literatura mostra que nem todos compiladores fazem isto.

Boa Prtica

Quando ainda estiver desenvolvendo um programa, sempre use as opes de emisso de warnings da forma mais restritiva possvel, para evitar deixar escapar erros importantes disfarados de simples warnings.

6.5.2 Passando arrays para subrotinas usando mdulos e arrays de forma assumida.

Como dito anteriormente, quando uma subrotina tem uma interface explicita, toda a informao necessria sobre os argumentos estaro disponveis para o

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

69

Forttran 90//95 For ran 90 95


compilador. Assim podemos ento desenvolver a terceira forma de passar arrays para uma subrotina, mencionada anteriormente neste captulo. Vimos anteriormente duas formas de passagem: A explicita onde as informaes sobre os arrays so passadas via argumento da subrotina e ento usados por ela. Outra forma era a do tamanho assumido do array, onde a subrotina apenas assume que o array rank 1 e que tem um determinado tamanho. Veremos agora a terceira forma onde as informaes sobre o rank e as extenses do array so passadas via uma interface explicita usando mdulo. Abaixo apresenta-se uma nova verso do program passando_array_1, a passando_array_3,
module passando_array contains subroutine passa_array(x,y) implicit none real, intent(in), dimension(:,:) :: x real, intent(out), dimension(:,:) :: y integer :: i1,i2,j1,j2,m1,m2,n1,n2 !Se necessario as dimensoes dos arrays podem ser recuperadas !diretamente, como abaixo i1 = lbound(x,1) i2 = ubound(x,1) j1 = lbound(x,2) j2 = ubound(x,2) m1 = lbound(y,1) m2 = ubound(y,1) n1 = lbound(y,2) n2 = ubound(y,2) y = sqrt(x) return end subroutine passa_array end module passando_array program passando_array_3 !Objetivo: Mostrar a passagem de um array do program principal !para um subrotina usando array de forma assumida e mdulo ! !programmer: joao batista aparecido ilha solteira, 12 dezembro 1999 ! use passando_array implicit none integer, parameter :: i_max = 10, j_max = 12 real, dimension(i_max,j_max) :: array1, array2 !atribuio de valores aos arrays array1=5.0 !invocando a subrotina passa_array call passa_array(array1, array2) stop

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

70

Forttran 90//95 For ran 90 95


end program passando_array_3

interessante notar que neste mtodo de passagem de arrays no necessrio passar explicitamente a extenso do array em cada direo, estes valores podem ser obtidos na prpria subrotina usando as declaraes LBOUND e UBOUND. Os arrays passando assim suportam operaes sobre array completo.
Para passagem de arrays entre unidades de um programa d preferencia a passagem explicita ou a passagem de array na forma assumida. Estas duas tcnicas so mais seguras e ambas suportam operaes sobre arrays completos. Evite usar a tcnica do array com tamanho assumido que menos segura e no suporta operaes sobre arrays completos.

Boa Prtica
6.6 Funes Fortran 90/95

Uma funo um subprograma Fortran que produz apenas um resultado de sada, embora podendo ser um array. O nome da funo pode ser usado no lado direito de uma funo aritmtica como uma varivel qualquer. O Fortran tm suas prprias funes intrnsecas (j falado anteriormente) e suporta tambm funes definidas pelo programador. O uso das funes definidas pelo usurio similar aquele das funes intrnsecas. A forma geral de uma funo
function nome_da_funcao (lista_de_argumentos) declaracaoes_no_executaveis declaracoes_executaveis nome_da_funcao = expresao return end function nome_da_funcao

A funo deve comear com a declarao FUNCTION e terminar com a declarao END FUNCTION. O nome da funo pode conter at 31 caracteres sendo que o primeiro deve ser alfabtico. Uma funo invocada quando seu nome mencionado em uma expresso aritmtica. Quando isto ocorre o programa para a execuo e passa o controle da execuo para a primeira linha da funo que efetua sua execuo, quando esta termina, o controle retorna ao programa que invocou a funo. Quando a funo retorna o seu valor usado para continuar o processamento onde ocorria seu nome na expresso aritmtica. Uma funo pode ser vazia de argumentos, neste caso deve-se colocar os dois parnteses sem nada entre. Como o nome da funo retorna um valor, ento este nome deve ser declarado na seo de declarao de variveis, tanto no programa que invoca quanto na prpria funo. A passagem de argumentos nas funes ocorre da mesma forma que nas subrotinas.
program funcao !Objetivo: Mostrar a tcnica de uso de uma function, calculando !o valor assumido por uma parabola, sendo conhecido os coeficientes

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

71

Forttran 90//95 For ran 90 95


!a, b, c e a coordenada x. ! !programmer: joao batista aparecido ilha solteira, 12 de dezembro 1999 ! implicit none real :: a = 2.0,b = 3.5 ,c = -5.0 ,x = 1.0 , y, parabola y = parabola(a,b,c,x) stop end program funcao function parabola(r,s,t,w) implicit none real :: r,s,t,w,parabola parabola = (r*w+s)*w+t return end function parabola

6.7 Funes puras (pure functions) e funes elementares (elemental functions)

Duas novas classes de funes disponveis em Fortran 90/95, destinadas principalmente (porm no somente) ao processamento paralelo do tipo SIMD (single instruction multiple data) so as funes puras (pure functions) e as funes elementares (elemental function).
6.7.1 Funes puras (pure functions) Funes puras so aquelas que no tm efeitos colaterais, isto os seus argumentos no podem ser modificados, nem outros dados podem ser modificados tais como aqueles contidos em mdulos. Adicionalmente, variveis locais no podem ter o atributo SAVE, e tambm no podem ser inicializadas em declaraes de tipo (uma vez que isto implicitamente implicaria em SAVE). Qualquer funo invocada por uma funo pura tambm dever ser pura. Todos os argumentos de uma funo pura devem ter a declarao de atributo INTENT(IN), alm dos mais no devem fazer nenhuma operao de entrada e sada de dados. Uma vez que funes puras no tem efeitos colaterais, elas so ideais para se utilizar junto com a declarao FORALL, onde podem ser executadas em (paralelo) qualquer ordem. Funes puras propriamente ditas s so definidas no Fortran 95, embora seja possvel criar algo parece em Fortran 90 declarando todos os argumentos da funo com o atributo INTENT(IN). Para declarar um funo pura basta acrescentar o prefixo PURE antes da palavra FUNCTION como segue
pure function nome_da_funcao (lista_de_argumentos)

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

72

Forttran 90//95 For ran 90 95


6.7.2 Funes elementares (elemental functions) Funes elementares so aquelas que operando com escalares como argumento tambm operam com arrays ou sees de arrays como argumento. O resultado que sai de uma funo elementar tem a mesma forma do argumento de entrada. Isto , se o argumento um escalar o resultado ser um escalar, se o argumento um array o resultado ser um array com a mesma forma do array de entrada. Funes intrnsecas elementares so disponveis em Fortran 90 e Fortran 95, mas funes elementares definidas pelo programador so suportadas apenas no Fortran 95. Funes elementares definidas pelo programador devem ser tambm funes puras e atender tambm as seguintes restries: 1) Todos os argumentos devem ser escalares e no devem ter o atributo POINTER (voltaremos ao pointer em outro captulo); 2) A funo deve ser escalar e no deve ter o atributo POINTER; 3) Argumentos da funo no podem ser usados em declaraes de tipo. Isto probe o uso de arrays autmticos. Funes elementares so especificadas para escalares mas que podem tambm ser usadas com argumentos do tipo array.

Para declarar um funo elementar basta acrescentar o prefixo ELEMENTAL antes da palavra FUNCTION como segue
elemental function nome_da_funcao (lista_de_argumentos)

6.8 Passando subprogramas como argumentos para outros subprogramas

Quando um subprograma invocado, a lista de argumentos passada como um conjunto de ponteiros que apontam para as posies de memria ocupadas por aqueles argumentos. Como cada posio de memria interpretada pelo subprograma depende do tipo e tamanho declarado no subprograma. Esta passagem por referncia pode ser estendida para permitir passar tambm ponteiros que apontem para outros subprogramas. Em Fortran funes e subrotinas podem ser passadas como argumento para outras funes ou subrotinas.
6.8.1 Passando funes como argumento

Se o nome de uma funo mencionado na lista de argumentos de um subprograma, ento um ponteiro passado para o subprograma. Se o argumento correspondente funo for mencionado internamente no subprograma, quando o subprograma executado, ento a funo receber o comando da execuo. Todo funo usada como argumento dever ter o atributo EXTERNAL. Abaixo apresenta-se um exemplo de uma subrotina que extrai valores de uma funo em certas posies definidas pela subrotina.
program funcao_como_argumento !Objectivo: Mostrar o uso de uma funo Fortran como argumento de !um outro subprograma: funo ou subrotina. ! !programmer: joao batista aparecido ilha solteira, 12 dezembro 1999

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

73

Forttran 90//95 For ran 90 95


! !Declarando as variveis do programa principal. !Note que a funo f aparece aqui com os atributos real e external implicit none integer :: i integer, parameter :: i_max = 100 real, external :: f real, dimension(i_max) :: x,y !Lao de atribuio de valores a x do i = 1,i_max x(i) = real(i)/10.0 end do !Rotina para extrair os valores de uma funo f, definida pelo usurio call extrai_valor(f,i_max,x,y) !terminao do programa stop end program funcao_como_argumento subroutine extrai_valor(g,m,t,s) !Subrotina que extrai os valores da funcao nas posioes definidas !no programa principal !declarao de variveis. Note que aqui chamei localmente a funcao de g implicit none real :: g integer :: m,j real, dimension(m) :: t,s !Lao onde a funcao invocada para os vrios valores do argumento. do j = 1,m s(j) = g(t(j)) ! Aqui a funcao sendo invocada end do !Finalizando a execuo da subrotina return end subroutine extrai_valor function f(x) !Funo que recebe um escalar e retorno o valor da funo para aquele argumento !Declaracao de variveis implicit none real :: x,f f = sin(x)+x ! A funcao sendo avaliada para o argumento x. !Completando a execuo da funo. return end function f

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

74

Forttran 90//95 For ran 90 95


Captulo 7 - Outros Tipos de Dados
Neste captulo vamos apresentar nmeros reais e inteiros com comprimentos (kind) diferentes daqueles apresentados at o momento. Apresenta-se tambm o tipo de dados COMPLEX, e o tipo de dados derivado, a ser definido pelo programador.
7.1 Outros tamanhos de dados para variveis do tipo real

Como j visto anteriormente o tipo real de dados usado para representar nmeros com ponto flutuante. Na maioria dos computadores hoje operando o tamanho default da varivel real, tambm chamado de preciso simples (single precision) usualmente 4 bytes, ou seja 32 bits. Conforme explicado em captulo anterior estes 32 bits so divididos em dois grupos, um para representar a mantissa e o outro para representar o expoente. A implementao padro de tipos de dados reais com preciso simples em geral tero uma preciso de 7 casas decimais e os nmeros estaro em uma faixa de 10-38 a 10+38. Caso seja necessrio ou desejvel representar nmeros reais com mais de 7 casas decimais e com uma faixa maior que de 10-38 a 10+38, ento ser necessrio utilizar variveis reais com tamanhos maiores que 32 bits. O Fortran possui tipo de dados com tamanho maior para representao de dados com maior preciso e maior faixa. Tipo de dados reais com maior tamanho tambm so de dupla preciso (double precision). Uma varivel real com dupla preciso tem usualmente 8 bytes ou 64 bits, nestes casos a implementao mais comum usar 53 bits para a mantissa e 11 bits para o expoente. Os 53 bits da mantissa so suficientes para representar aproximadamente 16 casas decimais, e os 11 bits do expoente so suficientes para representar nmeros em uma faixa de 10-308 a 10+308.
7.1.1 Tamanhos (kinds) de dados para variveis e constantes do tipo real

Como o Fortran prov para tipos de dados reais mais de um tamanho (kind) necessria a existncia de uma maneira de declar-los. Para tal existe o parmetro de tamanho (kind type parameter) para declarar os diferentes tamanhos. A seguir tem-se algumas exemplos de como efetuar esta declarao de tamanho de variveis ou constantes reais
real(kind = 4) :: r,s ou real(4) :: r,s real(kind = 8) :: w ou real(8)

Cada fabricante de compilador livre para definir o tamanho da palavra para cada representao de dados reais. Assim kind = 4 pode na maioria dos computadores pode significar uma palavra com 32 bits, mas em outros pode significar uma palavra com 64 bits. Assim o fragmento de cdigo escrito acima torna-se dependente de cada processador. Na Tabela 7.1 apresenta-se o tamanhos e os tipos de nmeros reais de alguns compiladores Fortran 90. Uma maneira mais elegante, mais ainda dependente do processador, de escrever o trecho de cdigo acima
integer, parameter :: single = 4, double = 8 real(kind = single) :: r,s ou real(single) :: r,s real(kind = double) :: w ou real(double)

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

75

Forttran 90//95 For ran 90 95


Tabela 7.1-Parmetros de tamanho (kind) e tamanho de variveis reais em compiladores Fortran 90 Computador/Compilador real de 32 bits real de 64 bits real de 128 bits Cray T90 Supercomputer/CF90 NA 4,8* 16 DEC/DEC Fortran 90 4* 8 NA PC/Lahey Fortran 90 4* 8 NA PC/Microsoft Powerstation 4 4* 8 NA PC/NAGWare FTN90 1* 2 NA * Parmetro default possvel declarar o tamanho de um dado real colocando um apndice na atribuio do dado, como segue
x = 37.5 y = 37.5_4 z = 37.5E0 w = 37.5D0 t = 37.5_double

Nas atribuies acima, a primeira atribuio interpreta 37.5 como sendo um dado do tipo preciso simples (32 bits); na segunda 37.5 representado por um tamanho que corresponde ao parmetro de tamanho igual a 4, nestes casos em geral mas nem sempre (ver tabela acima) corresponde tambm a 32 bits; a terceira corresponde exatamente primeira; na quarta 37.5 representado por em preciso dupla em geral correspondendo a 64 bits; a quinta corresponde exatamente quarta.
Sempre defina um parmetro de tamanho em uma constante, e ento use esta definio como atributo em declaraes de variveis e constantes. Esta prtica ajudar portar mais facilmente o programa em outras plataformas. Pode-se tambm colocar as constantes que definem os parmetro de tamanho em um nico mdulo e ento referenciar o mdulo em todas as unidades do programa.

Boa Prtica

7.1.2 Determinando o tamanho (kind) de uma varivel

O Fortran 90/95 prov uma funo intrnseca KIND para determinar o parmetro de tamanho de um varivel. O programa abaixo mostra como usar a funo KIND
program usando_kind implicit none integer, parameter :: double = 8 write(*,*)kind(37.5), kind(37.5_4), kind(37.5e0), kind(37.5d0), & kind(37.5_double) stop end program usando_kind

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

76

Forttran 90//95 For ran 90 95


7.1.3 Definindo a preciso de uma varivel, independente do processador

As expresses preciso simples e preciso dupla no so nicas em Fortran, uma vez que em um processador a preciso simples pode ser usando 64 bits e a preciso 128 bits. No entanto outros computadores utilizam 64 bits para dupla preciso e 32 bits para preciso simples. Desta forma dependendo do processador que se usa a interpretao do que simples e dupla preciso poder variar, implicando muitas vezes em resultados totalmente inesperados. interessante utilizarmos alguma maneira de definir a preciso de um varivel de uma forma o mais independente possvel do processador. O Fortran 90/95 apresenta uma funo intrnseca que auxilia nesta tarefa. A funo chamada SELECTED_REAL_KIND, quando executada retorna o parmetro de tamanho necessrio a uma varivel real, que corresponde a uma preciso e faixa especificadas, para aquele processador especfico. A forma geral desta funo
parametro_de_tamanho = selected_real_kind(p,f)

onde p a preciso em casas decimais desejada e f a faixa do expoente requerido em potncia de dez. A funo retorna o menor parmetro de tamanho que satisfaz as condies especificadas. Se esta funo retorna 1 significa que nenhum parmetro de tamanho satisfaz a preciso especificada; se retorna 2 significa que nenhum parmetro de tamanho satisfaz a faixa especificada; e se retorna 3 porque nenhum parmetro de tamanho satisfaz a preciso e a faixa especificadas. A seguir tem-se alguns exemplos de uso desta funo intrnseca
program usando_kind implicit none integer :: valor_kind valor_kind = selected_real_kind(p=2,r=10) valor_kind = selected_real_kind(p=5,r=35) valor_kind = selected_real_kind(p=7) valor_kind = selected_real_kind(r =30) valor_kind = selected_real_kind(p=20,r=350) stop end program usando_kind

No programa acima o primeiro valor_kind ser 4, porque em um PC (onde estou testando os programas) os dados reais em preciso simples usa 4 bytes ou 32 bits; o segundo valor de valor_kind ser tambm 4; o terceiro ser 8 porque uma varivel de 4 bytes prov uma preciso de apenas 6.8 casas decimais, ou seja abaixo de 7; no quarto caso ser 4 porque a faixa que uma variveis com 4 bytes estar na faixa de at 38; e por fim o ltimo valor ser 3 indicando que nenhum parmetro de tamanho atende aquela especificao.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

77

Forttran 90//95 For ran 90 95

Boa Prtica

Use a funo intrnseca SELECTED_REAL_KIND para determinar o parmetro de tamanho da varivel real desejada. A funo retornar o valor mais adequado para atender a necessidades, e tornar o programa mais portvel.

Existem ainda outras funes intrnsecas relacionadas ao parmetro de tamanho (kind), mostradas na Tabela 7.2 Tabela 7.2-Funes intrnsecas comuns relacionadas ao parmetro de tamanho (kind) Funo intrnseca Descrio selected_real_kind(p,r) Retorna o menor valor do parmetro de tamanho com um mnimo de p casas decimais e uma faixa 10r selected_int_kind(r) Retorna o menor valor do parmetro de tamanho com um range 10r kind(x) Retorna o parmetro de tamanho da varivel x, onde x uma constante ou varivel de qualquer tipo. precision(x) Retorna a preciso decimal de x, onde x real ou complexo. range(x) Retorna o expoente decimal de x, onde inteiro, real ou complexo
7.1.4. Preciso dupla em funes intrnsecas

Todas as funes genricas do Fortran 90/95 que operam com preciso simples, suportam tambm valores reais com preciso dupla. Se o argumento for preciso simples o retorno ser preciso simples, se o argumento for preciso dupla a resposta ser tambm preciso dupla.
7.2 Outros tamanhos (kinds) de dados para variveis inteiras

No caso de dados do tipo inteiro a idia geral totalmente semelhante quela dos dados do tipo real. No caso dos inteiros mais simples, pois necessita-se especificar apenas a faixa que o nmero deve satisfazer. Na Tabela 7.3 tm-se os parmetros de tamanho para dados do tipo inteiro para alguns processadores. Tabela 7.3-Parmetros de tamanho (kind) e tamanho de variveis inteiras em compiladores Fortran 90 Computador/Compilador int8 int16 int32 int48 int64 Cray T90 Supercomputer/CF90 NA NA 1,2,4 6* 8 DEC/DEC Fortran90 1 2 4* NA 8 PC/Lahey Fortran 90 1 2 4* NA NA PC/Microsoft Powerstation 4.0 1 2 4* NA NA PC/NAGWare FTN90 1 2 4* NA NA *Valores default
7.3 O tipo de dados COMPLEX

Praticamente tudo que se disse at o momento sobre reais aplica-se tambm aos do tipo complexo. Um complexo em realidade um dupla de dados reais que
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
78

Forttran 90//95 For ran 90 95


satisfazem um lgebra especial. Assim tem-se algumas funes implicitas que aplicam-se para complexos. Para declarar variveis e constantes do tipo complexo procede-se da seguinte maneira
complex, parameter :: x = (1.0,2.0) complex, dimension(55) :: y complex :: z = (-1.5,5.5)

onde na dupla (a,b) o real da esquerda a componente real do dado complexo e b a parte imaginria.
7.3.1 Aritmtica mista com complexos

Quando um complexo vai sofrer uma operao com um nmero no complexo o programa converte o valor do dado no complexo para complexo e executa a operao.
7.3.2 Utilizando operadores relacionais com complexos

possvel comparar dois dados complexos usando o operador relacional == e pode-se tambm verificar se so diferentes usando o operador /=. Os outros operadores relacionais >, <, >= e <= no podem ser usados com complexos.
7.4 Tipos de dados derivados

Os tipos de dados bsicos do Fortran so: inteiros, reais, caracteres, lgicos e complexos. O Fortran permite ao usurio definir os seus prprios tipos de dados, so os chamados tipos derivados de dados. Eles so derivados porque devem ser construdos utilizando aqueles tipos bsicos mencionados acima e eventualmente outros tipos de dados derivados j definidos. Uma das vantagens dos dados derivados o encapsulamento, isto os componentes internos do dados derivado devem ser acessado explecitamente. Um dado do tipo derivado uma maneira conveniente de agrupar um conjunto de informaes, de uma maneira semelhante definio de um array como um conjunto de elementos. Porm a sintaxe diferente, e cada elemento referenciado por um nome ao invs de por um nmero. Um dado derivado definido como uma seqncia de declaraes precedidas da declarao TYPE e completado com a declarao END TYPE. A forma geral de declarao de um dado derivado
type :: nome_do_tipo componentes_da_declaracao end type nome_do_tipo

abaixo apresenta-se um caso concreto de um dado derivado destinado a descrever livros


type :: frutos_no_cesto integer :: numero_bananas

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

79

Forttran 90//95 For ran 90 95


integer :: numero_laranjas integer :: numero_cajus integer :: numero_mangas integer :: numero_goiabas end type frutos_no_cesto

Uma vez que uma determinado dado derivado esteja definido possvel utiliz-lo e produzir as seguintes definies
type (frutos_no_cesto) :: cesto1, cesto2, cesto_na_prateleira type (frutos_no_cesto), dimension(20) :: cesto_grande, cesto_pequeno

Um dado derivado tambm chamado de estrutura. Uma estrutura pode ser inicializada utilizando um construtor de estruturas, de um forma semelhante ao construtor de arrays, como segue
cesto1 = cesto_de_frutas(12,9,0,2,5) cesto2 = cesto_de_frutas(0,24,3,0,6) Na primeira linha de cdigo logo acima, o construtor da estrutura cesto1 ir atribuir 12 ao numero_bananas, 9 ao numero_laranjas, 0 ao numero_cajus, 2 ao numero_mangas, e 5 ao numero_goiabas. Uma estrutura pode ser usada como elemento em outra estrutura. 7.4.1 Acessando elementos de uma estrutura

Os componentes numricos de uma estrutura podem ser usados de acordo com as operaes cada tipo suporta. O mesmo vale os componentes do tipo caracter. Como estes elementos esto encapsulados dentro da estrutura, para acess-los ser necessrio usar o seletor de componentes (component selector) No exemplo dos cestos de frutos mostrados acima pode-se atribuir valor a um componente da estrutura da seguinte maneira
cesto1%numero_bananas = 13

ou
cesto1.numero_bananas = 13

estas duas formas acima, usando % ou usando . so equivalentes. Para somar o nmero de goiabas no cesto1 e no cesto2, o procedimento faz-se
quantidade_goiabas = cesto1.goiabas + cesto2.goiabas

Os tipos de dados derivados podem ser definidos dentro de mdulos, evitando ter que defini-los em todas as unidades de programa. Abaixo apresenta-se a definio de uma estrutura chamada caixa que tem trs componentes: largura, profundidade e altura.
program estrutura_caixa

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

80

Forttran 90//95 For ran 90 95


implicit none type :: caixa real :: largura real :: profundidade real :: comprimento end type caixa type (caixa) :: caixa1, caixa2 caixa1.largura = 1.5 caixa2%profundidade = 3.5 stop end program estrutura_caixa

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

81

Forttran 90//95 For ran 90 95


Captulo 8 - Aspectos Avanados de Funes, Subrotinas e Mdulos.
Neste captulo apresenta-se alguns aspectos mais avanados de funes, subrotinas e mdulos, visando ter um controle mais seguro da transferncia de dado entre estas unidades de programa. Estas caractersticas permitem definir subrotinas com argumentos opcionais, com argumentos que variam de tipo, e definir operaes sobre tipos derivados de dados.
8.1 Subprogramas internos

At o momento todos os subprogramas que definimos, funes, subrotinas e mdulos, eram do tipo externo. Vamos ver agora como construir os chamados subprogramas internos. O Fortran 90/95 suporta os subprogramas internos, que so funes e subrotinas que ficam dentro do programa principal, ou de algum outro subprograma. Os subprogramas internos so compilados juntamente com a compilao do programa hospedeiro, e podem ser invocadas apenas por ele. O local no programa em que fica um subprograma interno logo aps a ltima declarao executvel do programa, e antes das declaraes STOP e END PROGRAM. Separando os subprogramas internos do restante do cdigo deve-se usar apenas a palavra CONTAINS. De outra forma, todos os subprogramas internos devero estar entre a declarao CONTAINS e a finalizao do programa. O nome de um subprograma interno no pode ser passado como argumento de linha de comando a outro subprograma. Um subprograma interno recebe todas as variveis e constantes definidas no seu hospedeiro, em outras palavras todas as variveis e constantes do programa hospedeiro so globais para os seus subprogramas internos. Quando define-se uma varivel no subprograma interno, que tenha o mesmo nome de uma varivel do programa hospedeiro, ento esta varivel passa a ter um carter local, e por conseguinte no consegue acessar o valor contido na varivel do mesmo nome no programa hospedeiro.
8.2 Escopo de variveis e constantes

Os escopos possveis de todos os tipos de objetos (inteiro, real, lgico, complexo, caracter, derivado) so: global, local e declarao. Objetos globais so aqueles cuja definio atinge todas as instncias do programa (programa principal e subprogramas), consequentemente os nomes destes objetos devem ser nicos em todas as unidades do programa. Os nicos objetos que gozam desta propriedade so o nome do programa principal, os nomes das funes, os das subrotinas, e os dos mdulos. Utilizando-se mdulos tambm pode-se produzir variveis que tenham um escopo global, quando todas as unidades de programa referenciarem um determinado mdulos de compartilhamento de dados. Objetos locais so definidos apenas dentro de algumas unidades do programa, tambm chamadas de unidades de escopo. Alguns exemplos so: programas, funes, subrotinas, e mdulos. O nome de um objeto de ser nico dentro de sua unidade de escopo, porm o mesmo nome pode ser usado para outra varivel em outra unidade de escopo. Um objeto tambm pode ter como escopo declaraes do tipo DO implcito em construtores de arrays, abaixo tem-se um exemplo de onde pode ocorrer este tipo de escopo de variveis
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
82

Forttran 90//95 For ran 90 95


array1 = (/i**2, i = 1,100/)

Abaixo mostra-se um programa exemplo que apresenta todos os tipos de escopo: global, local e de declaraes. module global implicit none save integer, parameter :: j=5 end module global
program escopo !Objetivo: Demonstrar os tipos de escopo: global, local e declarao ! !programmer: joao batista aparecido ilha solteira, 14 dezembro 19999 ! !Declarando variveis use global implicit none integer, parameter :: k = 6 integer :: i real, dimension(k) :: a do i=1,k a = (/(i*i, i =1,k)/) end do call sub(k,a) stop end program escopo subroutine sub(k,a) use global implicit none integer :: k, i real, dimension(k) :: a do i = 1, j a(i) = sin(a(i)) end do return end subroutine sub

No programa acima alm do nome do programa, da subrotina e do mdulo, tambm o inteiro j definido no mdulo tm escopo global. No programa principal tem-se um lao de DO em que o ndice (i) vai de 1 a 6 (k), este lao inicializa o vetor a seis vezes, e dentro do lao tem o lao de DO implcito, cuja varivel de controle tambm (i) que vai atribuir valores a cada posio do array, note que no ocorre erro de compilao, nem erro de link, nem erro de execuo, isto porque a varivel (i) do lao interno tem escopo apenas dentro do
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
83

Forttran 90//95 For ran 90 95


lao implicito, deixando de existir assim que o lao completa-se, este o escopo de declarao. Para evitar problemas de interpretao ao se ler o cdigo procure escrever o nome das variveis de controle dos DOs implcitos diferentes das dos DOs explcitos que envolvem em seu lao os DOs implcitos. Quando o lao de DO do programa termina o valor da varivel i = 7, ento a subrotina sub invocada, e l dentro dela existe outro lao de DO no qual a varivel de controle tambm (i). O valor de i dentro da subrotina vai variar de 1 a 5 e quando a subrotina completar o valor de i ser i= 6, ento subrotina passa o controle da execuo ao programa principal e o valor armazenado na varivel i ainda i = 7. Isto ocorre porque a varivel i da subrotina tem escopo apenas local na subrotina, no podendo alterar o valor da varivel i que encontra-se tambm no programa principal.
8.3 Subprogramas recursivos

As funes e subrotinas comuns do Fortran 90/95 no podem invocar a si mesmas, quer seja diretamente ou indiretamente. Significa dizer que os subprogramas comuns do Fortran no so recursivos. Alguns tipos de problemas podem ser resolvidos mais facilmente se determinada funo ou subrotina recursiva. O Fortran 90/95 suporta funes e subrotinas recursivas desde que tenham o atributo RECURSIVE, colocado antes da palavra FUNCTION ou SUBROUTINE. Um exemplo clssico de uso de subrotina recursiva para calcular o fatorial de um inteiro n. Abaixo apresenta-se um programa que faz o cmputo do fatorial de um inteiro n, utilizando-se de um subrotina recursiva.
program recursivo implicit none integer :: n=5, valor call fatorial(n,valor) stop end program recursivo recursive subroutine fatorial(i,x) implicit none integer :: i, x, aux if (i>=1) then call fatorial(i-1,aux) x = i*aux else x=1 end if return end subroutine fatorial

No caso de definio de funes recursivas existe um complicao extra. Como j vimos anteriormente para invocar uma funo necessrio que ela esteja em uma

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

84

Forttran 90//95 For ran 90 95


expresso aritmtica, isto do lado direito da declarao de atribuio (=). Por outro lado para receber o valor que retorna o nome da funo teria que estar do lado esquerdo do (=). Este problema foi resolvido da seguinte forma: atribuindo uma especificao RESULT(varivel_de_retorno) aps a lista de variveis da funo, de tal forma que o resultado retorne na varivel_de_retorno, ao invs de retornar no nome da funo, podendo ser utilizado de um forma semelhante das subrotinas. Abaixo apresenta-se uma declarao de um funo recursiva func que usa o argumento arg para retornar o valor
recursive function func(lista_de_variaveis) result(arg)

Abaixo tem-se uma nova verso do programa para calcular o fatorial de n, agora utilizando uma funo recursiva
program recursivo_2 implicit none integer :: n=5, valor, fatorial valor = fatorial(n) stop end program recursivo_2 recursive function fatorial(i) result(retorno) implicit none integer :: i, retorno if (i>=1) then retorno = i*fatorial(i-1) else retorno = 1 end if return end function fatorial

8.4 Argumentos com palavras-chave e argumentos opcionais.

J foi dito vrias vezes que a quantidade, o tipo e o tamanho dos argumentos de uma funo ou subrotina devem coincidir onde ela invocada e com sua definio. Caso contrrio haver algum tipo de erro, no de compilao, na lincagem ou na execuo. Existe situaes nas quais possvel modificar a ordem em que os argumentos aparecem na chamada e no subprograma, e mesmo assim o programa operar corretamente. Esta tcnica a ser descrita s funcionar se o subprograma tiver uma interface explicita. Como j foi dito anteriormente uma das maneiras de prover uma interface explicita colocar o subprograma dentro de um mdulo. A outra forma, que veremos mais adiantes, consiste em prover explicitamente blocos de interface. Se o subprograma possuir uma interface explicita ento possvel usar argumentos nomeados, no programa que est invocando o subprograma, de tal forma que seja possvel ao programa saber onde est cada argumento da lista de argumentos e fazer a
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
85

Forttran 90//95 For ran 90 95


associao correta, mesmo com o argumentos embaralhados na lista. Um argumento nomeado um argumento com a seguinte forma
palavra_chave = argumento_verdadeiro

onde palavra_chave o nome do argumento que est sendo associado com o argumento verdadeiro. Se a invocao do subprograma usa palavras-chave, ento os argumentos na chamada podem ser arranjados em qualquer ordem porque as palavras-chave auxiliam o compilador a saber qual argumento corresponde a qual. Vamos ilustrar estas idias construindo um programa que chama uma subrotina duas vezes. Na primeira chamada os argumentos esto colocados na lista de argumentos sem usar palavras-chave, e na Segunda vez est com os argumentos embaralhados porm usando palavras-chave. O resultando em os ambos os casos ser o mesmo.
module sub contains subroutine sub1(r,s,t,u) implicit none real :: r,s,t,u u=r+s+t return end subroutine sub1 end module sub program argumento_embaralhado use sub implicit none real :: a =1.0, b =2.0, c=3.0, d call sub1(a,b,c,d) write(*,*) d call sub1(u=d, s=b, r=a, t=c) write(*,*) d stop end program argumento_embaralhado

Esta prtica um pouco mais segura porm necessrio um gasto maior de tempo escrevendo os argumentos da chamada do subprograma em uma forma mais complexa. A principal conseqncia desta tcnica no entanto outra. Com este tipo de declarao ser possvel ter argumentos opcionais. Argumentos opcionais so aqueles que podem ou no aparecer na lista de argumentos e o subprograma no acusar erro de compilao e a execuo continuar, alm do mais o compilador ter condies de

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

86

Forttran 90//95 For ran 90 95


saber qual o argumento que no est presente. Obviamente argumentos opcionais s sero possveis em subprogramas com interface explicita. Estes argumentos opcionais devero ser declarados com o atributo OPTIONAL. Uma rotina que tem argumentos do tipo OPTIONAL pode usar a funo intrnseca PRESENT para verificar se um dado argumento est presente. Se o argumento estiver presente, PRESENT retornar TRUE, se estiver ausente PRESENT retornar FALSE. Abaixo apresenta-se o programa anterior modificado para suportar a falta dos argumentos de entrada.
module sub contains subroutine sub1(r,s,t,u) implicit none real, optional, intent(in) :: r,s,t real, intent(out) :: u logical :: i_r, i_s, i_t i_r = present(r) i_s = present(s) i_t = present(t) if(i_r.and.i_s.and.i_t) u = r + s + t if(i_r.and.i_s.and.(.not.i_t)) u = r + s if(i_r.and.(.not.i_s).and.i_t) u = r + t if((.not.i_r).and.i_s.and.i_t) u = s + t if((.not.i_r).and.(.not.i_s).and.i_t) u = t if((.not.i_r).and.i_s.and.(.not.i_t)) u = s if(i_r.and.(.not.i_s).and.(.not.i_t)) u = r if((.not.i_r).and.(.not.i_s).and.(.not.i_t)) u = 0.d0 return end subroutine sub1 end module sub program argumento_opcional use sub implicit none real :: a =1.0, b =2.0, c=3.0, d call sub1(a,b,c,d) call sub1(u=d, r=a, t=c) call sub1(u=d, s=b, t=c) call sub1(u=d, s=b) call sub1(u=d) call sub1(u=d, r=a, t=c, s=b) stop end program argumento_opcional

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

87

Forttran 90//95 For ran 90 95


O programa acima capaz de suportar e identificar os argumentos fora de ordem, se existe algum argumento ausente, e se existe algum, consegue identificar qual. Evidentemente, se existe algum argumento faltando necessrio executar procedimentos compatveis. Assim implementou-se algumas somas que levaram em considerao apenas os argumentos presentes.
8.5 Interfaces para subprogramas e blocos de interfaces

Conforme j foi dito anteriormente desejvel que o programador coloque as subrotinas e funes dentro de um mdulo, de tal forma que tenham automaticamente uma interface explicita. No entanto nem sempre possvel colocar-se estas informaes dentro de um mdulo. So vrias as situaes: imagine um conjunto de rotinas que no se tm o cdigo fonte para fazer as modificaes; ou uma biblioteca antiga e muito grande que ficaria muito cara re-escreve-la novamente; ou ainda uma biblioteca escrita em outra linguagem. Desta forma til alguma ferramenta que permita a construo de interfaces explicitas sem necessariamente ter que mexer no cdigo original. A maneira de se fazer isto utilizando blocos de interface.
8.5.1. Criando blocos de interface

Os blocos de interface so criados no programa que invoca os subprogramas, colocados na seo de declarao de variveis. Uma interface criada duplicando as informaes das declaraes de atributo de todas as variveis da lista de argumentos, e colocadas entre as declaraes INTERFACE e END INTERFACE. A forma geral para criar um bloco de interface
interface subroutine nome_da_subrotina (lista_argumentos) declaracoes_de_atributos_dos_argumentos end subroutine nome_da_subroutine function nome_da_function (lista_argumentos) declaracoes_de_atributos_dos_argumentos end function nome_da_function end interface

A forma mostra acima apresenta apenas um funo e uma subrotina, mas podem ser empilhadas tantas quantas forem necessrias. Estas informaes contidas na interface so suficientes para que o compilador reconhea a quantidade, tipo e tamanho dos argumentos.
program interface_explicita implicit none !Definindo o bloco de interface interface subroutine sub1(r,s,t,u) implicit none

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

88

Forttran 90//95 For ran 90 95


real, optional, intent(in) :: r,s,t real, intent(out) :: u end subroutine sub1 end interface real :: a =1.0, b =2.0, c=3.0, d call sub1(a,b,c,d) call sub1(u=d, r=a, t=c) call sub1(u=d, s=b, t=c) call sub1(u=d, s=b) call sub1(u=d) call sub1(u=d, r=a, t=c, s=b) stop end program interface_explicita subroutine sub1(r,s,t,u) implicit none real, optional, intent(in) :: r,s,t real, intent(out) :: u logical :: i_r, i_s, i_t i_r = present(r) i_s = present(s) i_t = present(t) if(i_r.and.i_s.and.i_t) u = r + s + t if(i_r.and.i_s.and.(.not.i_t)) u = r + s if(i_r.and.(.not.i_s).and.i_t) u = r + t if((.not.i_r).and.i_s.and.i_t) u = s + t if((.not.i_r).and.(.not.i_s).and.i_t) u = t if((.not.i_r).and.i_s.and.(.not.i_t)) u = s if(i_r.and.(.not.i_s).and.(.not.i_t)) u = r if((.not.i_r).and.(.not.i_s).and.(.not.i_t)) u = 0.d0 return end subroutine sub1

Estes programa usando uma interface explicita produzida via um bloco de interface produz exatamente o mesmo resultado que outro mostrado mais acima que tinha uma interface explicita produzida pelo fato da subrotina estar imersa em um mdulo. Os parmetros listados em um bloco de interface deve coincidir em quantidade, tipo, ordem e tamanho com aqueles do subprograma que se est interfaceando, mas no necessrio que o nome seja o mesmo.

Boa Prtica

Sempre que possvel coloque as subrotinas e funes dentro de um mdulo de tal forma que tenham um interface explicita, sem utilizar blocos de interface.
89

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

Forttran 90//95 For ran 90 95

Boa Prtica
8.6 Subprogramas genricos

Se for necessrio criar vrios blocos de interface, ento coloque os dentro de mdulo e as acesse via uma declarao USE.

O Fortran 90/95 inclui entre suas funes intrnsecas, funes de uso genrico e funes de uso especfico. As genricas operam sobre dados de diferentes naturezas, por exemplo: inteiros, reais e arrays de inteiros e reais. As especficas operam apenas sobre um determinado tipo de dado.
8.6.1 Subprogramas genricos definidas pelo programador

Viemos at o momento tratando com subprogramas (definidos pelo programador) apenas do tipo especfico onde fixa-se o tipo de varivel, e o subprograma s funciona para aquele tipo. O Fortran 90/95 suporta funes genricas definidas pelo programador. Suponha que temos vrias rotinas capazes de somar dois dados de diferentes tipos e tamanhos, e queremos produzir apenas um rotina que seja capaz de operar sobre todos os tipos e retornar um resultado compatvel. Se definirmos um nome genrico para a interface, diferente do nome dos subprogramas dentro da interface, ento o compilador Fortran 90/95 ser capaz de entender que todos os subprogramas contidos dentro daquela interface so verses especiais do subprograma genrico que se quer construir. A seguir apresenta-se um programa que adiciona dois nmeros reais ou inteiros e de comprimentos diferentes.
module tipo_dado !Mdulo para compartilhar os parmetros de tamanhos das variveis save integer, parameter :: curto = 2, longo = 4, simples = 4, duplo =8 end module tipo_dado program soma_generica !Objetivo: Mostrar como se define uma funo genrica ! !programador: joao batista aparecido, ilha solteira, 15 de dezembro 1999 ! use tipo_dado implicit none !Definio da interface genrica (soma) interface soma subroutine soma1(a,b,c) !Interface da subrotina que soma inteiros curtos use tipo_dado implicit none integer(curto), intent(in) :: a,b integer(curto), intent(out) :: c end subroutine soma1 subroutine soma2(a,b,c) !Interface da subrotina que soma inteiros longos use tipo_dado implicit none integer(longo), intent(in) :: a,b

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

90

Forttran 90//95 For ran 90 95


integer(longo), intent(out) :: c end subroutine soma2 subroutine soma3(a,b,c) !Interface da subrotina que soma reais simples use tipo_dado implicit none real(simples), intent(in) :: a,b real(simples), intent(out) :: c end subroutine soma3 subroutine soma4(a,b,c) use tipo_dado implicit none real(duplo), intent(in) :: a,b real(duplo), intent(out) :: c end subroutine soma4 end interface !Interface da subrotina que soma reais duplos

integer(curto) :: i=1,j=5,k integer(longo) :: m=2,n=9,o real(simples) :: x=1.e0, y=-1.e0/3.e0, z real(duplo) :: r=1.d0, s=-1.d0/3.d0, t call soma(i,j,k) call soma(m,n,o) call soma(x,y,z) call soma(r,s,t) stop end program soma_generica subroutine soma1(a,b,c) !Subrotina que soma dois inteiros curtos use tipo_dado implicit none integer(curto), intent(in) :: a,b integer(curto), intent(out) :: c c = a+ b return end subroutine soma1 subroutine soma2(a,b,c) !Subrotina que soma dois inteiros longos use tipo_dado implicit none integer(longo), intent(in) :: a,b integer(longo), intent(out) :: c c = a+ b return end subroutine soma2 subroutine soma3(a,b,c) !Subrotina que soma dois reais simples use tipo_dado

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

91

Forttran 90//95 For ran 90 95


implicit none real(simples), intent(in) :: a,b real(simples), intent(out) :: c c = a+ b return end subroutine soma3 subroutine soma4(a,b,c) !Subrotina que soma dois reais duplos use tipo_dado implicit none real(duplo), intent(in) :: a,b real(duplo), intent(out) :: c c = a+ b return end subroutine soma4

Boa Prtica

Use blocos de interface genricos para definir subprogramas que podem operam com tipos de dados variveis. Subprogramas genricos facilitam operaes com diferentes tipos de dados.

8.6.2 Interfaces genricas para subprogramas em mdulos

No item anterior mostrou-se como criar um subprograma genrico usando interfaces explicitas via bloco de interface. Agora mostra-se como fazer a mesma coisa porm usando mdulo. A dificuldade em se fazer isto em um mdulo que se os subprogramas j est no mdulo, eles automaticamente j tem cada um uma interface, ento como definir um interface mais genrica se o conceito de mdulo probe a definio de uma interface explicitamente dentro dele uma vez que ela j gerada automaticamente. O Fortran apresenta uma declarao especial para efetuar esta tarefa a declarao MODULE PROCEDURE, que pode ser usada em bloco de interface genrica. A forma desta declarao
module procedure lista_nome_procedure

onde procedure lista_nome_procedure a lista de subprogramas que compe funo genrica. Para o caso mostrado no programa acima, a interface genrica para a subrotina soma deve ser da seguinte forma
interface soma module procedure soma1 module procedure soma2 module procedure soma3 module procedure soma4 end interface

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

92

Forttran 90//95 For ran 90 95


O bloco de interface deve ser colocado no mesmo mdulo onde esto os subprogramas componentes do subprograma genrico.
8.7 Estendendo o Fortran com declaraes e operadores definidos pelo programador

Quando definimos no captulo anterior os tipos de dados derivados conseguimos apenas atribuir valores s componentes dos dados derivados. Porm no foi possvel fazer operaes sobre as variveis como um todo. As operaes bsicas dos operadores aritmticos binrios e unrios no funcionam para dados derivados. Isto porque o programador deve fornecer informaes suficientes, ao compilador, para se construir uma lgebra prpria de cada tipo especfico de dado derivado. O Fortran 90/95 uma linguagem extensvel de tal forma que o programador pode ir adicionando novas aspectos para acomodar tipos especiais de dados. J vimos como definir tipos novos de dados, vamos ver agora como definir novos operadores. Ou de outra forma como definir operadores para estes novos tipos de dados. Assim, como a devida imaginao possvel dar novos sentidos para adio, subtrao, multiplicao, diviso e comparaes de dados derivados. Como isto pode ser feito? O primeiro passo construir um subprograma que execute o desejado e coloc-lo em um mdulo. O segundo passo associar este subprograma com um operador (smbolo) usando um bloco de interface de operador. A forma desta interface
interface operator (simbolo_do_operador) module procedure nome_da_function end interface

onde simbolo_do_operador qualquer um dos operadores intrnsecos (+,-,*,/,>,< etc.) ou qualquer smbolo definido pelo programador. Um operador definido pelo programador pode ter at 31 caracteres cercado por um ponto antes e um ponto depois, tal como .DIVIDIR. Mais de um tipo de subprograma pode estar associado a um dado operador, mas ser necessrio que estes subprogramas possam ser distinguidos uns dos outros atravs da anlise dos tipos e tamanhos de seus argumentos. Quando o compilador encontra um operador, ento procura pelo correspondente subprograma. Se o operador tem dois argumentos ento um operador binrio, se tem apenas um unrio. Uma vez definido o operador ser tratado como uma referncia para a funo. Se o operador sendo definido pela interface utiliza os smbolos dos operadores intrnsecos (+,-,*,/, etc.) ento tem-se alguns restries adicionais: 1) No se pode mudar o sentido dos operadores intrnsecos quando estes operam sobre os tipos bsicos de dados (inteiro, real, lgico, complexo e caracter). No entanto estes operadores podem ser estendidos em outros sentidos para quando estiverem operando com dados derivados; 2) O nmero de argumentos em uma funo deve ser consistente com suas natureza, ou seja em uma adio s se pode somar dois nmeros de cada vez. Qualquer extenso do sentido da soma deve atender a este requisito.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

93

Forttran 90//95 For ran 90 95


Pode-se estender tambm o sentido da declarao de atribuio (=) de uma maneira similar. Para se fazer isto utiliza-se o bloco interface de atribuio, que tem a seguinte forma
interface assignment (=) module procedure nome_subroutine end interface

Para o operador de atribuio a interface deve ter uma subrotina ao invs de uma function. A subrotina dever ter dois argumentos O primeiro o argumento de sada e deve ter o atributo intent(out). O segundo o argumento de entrada e deve ter o atributo intent(in). O argumento da esquerda corresponde ao lado esquerdo da atribuio e o direito ao da direita.
Use blocos de interface de operadores e de atribuio para criar novos operadores ou estender o significado dos j existentes para trabalhar com tipos de dados derivados. Uma vez tendo definido estes operadores fica muito mais fcil operar com este tipo de dados..

Boa Prtica

Abaixo apresenta-se uma aplicao executar operaes sobre um tipo de dado derivado que um vetor com trs componentes.
MODULE vectors !Purpose: !To define a derived data type called vector and the !operations that can be performed on it. The module !defines eight operations that can be performed on vectors: ! ! 1.Creation from a real array = ! 2.Conversion to a real array = ! 3.Vector addition + ! 4.Vector subtraction ! 5.Vector-scalar multiplication (4 cases) * ! 6.Vector-scalar division (2 cases) / ! 7.Dot product .dot. ! 8.Cross product * ! !IL contains a total of 12 procedures to implement those !operations: array_to_vector, vector_to_array, vector_add, !vector_subtract, vector_times_real, real_times_vector, !vector_times_int, int_times_vector, vector_div_real, !vector_div_int, dot _product, and cross _product. ! IMPLICIT NONE ! Declare vector data types: TYPE :: vector REAL :: x REAL :: y REAL :: z END TYPE

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

94

Forttran 90//95 For ran 90 95


! Declare interface operators: INTERFACE ASSIGNMENT (=) MODULE PROCEDURE array_to_vector MODULE PROCEDURE vector_to_array END INTERFACE INTERFACE OPERATOR (+) MODULE PROCEDURE vector_add END INTERFACE INTERFACE OPERATOR (-) MODULE PROCEDURE vector_subtract END INTERFACE INTERFACE OPERATOR (*) MODULE PROCEDURE vector_times_real MODULE PROCEDURE real_times_vector MODULE PROCEDURE vector_times_int MODULE PROCEDURE int_times_vector MODULE PROCEDURE cross_product END INTERFACE INTERFACE OPERATOR (/) MODULE PROCEDURE vector_div_real MODULE PROCEDURE vector_div_int END INTERFACE INTERFACE OPERATOR (.DOT.) MODULE PROCEDURE dot_product END INTERFACE ! Now define the implementing functions. CONTAINS SUBROUTINE array_to_vector(vec_result, array) TYPE (vector), INTENT(OUT) :: vec_result REAL, DIMENSION(3), INTENT(IN) :: array vec_result%x = array(1) vec_result%y = array(2) vec_result%z = array(3) END SUBROUTINE array_to_vector SUBROUTINE vector_to_array(array_result, vec_1) REAL, DIMENSION(3), INTENT(OUT) :: array_result TYPE (vector), INTENT(IN) :: vec_1 array_result(1) = vec_1%x array_result(2) = vec_1%y array_result(3) = vec_1%z END SUBROUTINE vector_to_array FUNCTION vector_add(vec_1, vec_2) TYPE (vector) :: vector_add TYPE (vector), INTENT(IN) :: vec_1, vec_2 vector_add%x = vec_1%x + vec_2%x vector_add%y = vec_1%y + vec_2%y vector_add%z = vec_1%z + vec_2%z END FUNCTION vector_add FUNCTION vector_subtract(vec_1, vec_2) TYPE (vector) :: vector_subtract TYPE (vector), INTENT(IN) :: vec_1, vec_2

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

95

Forttran 90//95 For ran 90 95


vector_subtract%x = vec_1%x - vec_2%x vector_subtract%y = vec_1%y - vec_2%y vector_subtract%z = vec_1%z - vec_2%z END FUNCTION vector_subtract FUNCTION vector_times_real(vec_1, real_2) TYPE (vector) :: vector_times_real TYPE (vector), INTENT(IN) :: vec_1 REAL, INTENT(IN) :: real_2 vector_times_real%x = vec_1%x * real_2 vector_times_real%y = vec_1%y * real_2 vector_times_real%z = vec_1%z * real_2 END FUNCTION vector_times_real FUNCTION real_times_vector(real_1, vec_2) TYPE (vector) :: real_times_vector REAL, INTENT(IN) :: real_1 TYPE (vector), INTENT(IN) :: vec_2 real_times_vector%x = real_1 * vec_2%x real_times_vector%y = real_1 * vec_2%y real_times_vector%z = real_1 * vec_2%z END FUNCTION real_times_vector FUNCTION vector_times_int(vec_1, int_2) TYPE (vector) :: vector_times_int TYPE (vector), INTENT(IN) :: vec_1 INTEGER, INTENT(IN) :: int_2 vector_times_int%x = vec_1%x * REAL(int_2) vector_times_int%y = vec_1%y * REAL(int_2) vector_times_int%z = vec_1%z * REAL(int_2) END FUNCTION vector_times_int FUNCTION int_times_vector(int_1, vec_2) TYPE (vector) :: int_times_vector INTEGER, INTENT(IN) :: int_1 TYPE (vector), INTENT(IN) :: vec_2 int_times_vector%x = REAL(int_1) * vec_2%x int_times_vector%y = REAL(int_1) * vec_2%y int_times_vector%z = REAL(int_1) * vec_2%z END FUNCTION int_times_vector FUNCTION vector_div_real(vec_1, real_2) TYPE (vector) :: vector_div_real TYPE (vector), INTENT(IN) :: vec_1 REAL, INTENT(IN) :: real_2 vector_div_real%x = vec_1%x / real_2 vector_div_real%y = vec_1%y / real_2 vector_div_real%z = vec_1%z / real_2 END FUNCTION vector_div_real FUNCTION vector_div_int (vec_1, int_2) TYPE (vector) :: vector_div_int TYPE (vector), INTENT(IN) :: vec_1 INTEGER, INTENT(IN) :: int_2 vector_div_int%x = vec_1%x / REAL(int_2) vector_div_int%y = vec_1%y / REAL(int_2) vector_div_int%z = vec_1%z / REAL(int_2) END FUNCTION vector_div_int

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

96

Forttran 90//95 For ran 90 95


FUNCTION dot_product (vec_1, vec_2) REAL :: dot_product TYPE (vector), INTENT(IN) :: vec_1, vec_2 dot_product = vec_1%x*vec_2%x + vec_1%y*vec_2%y & + vec_1%z*vec_2%z END FUNCTION dot_product FUNCTION cross_product (vec_1, vec_2) TYPE (vector) :: cross_product TYPE (vector), INTENT(IN) :: vec_1, vec_2 cross_product%x = vec_1%y*vec_2%z - vec_1%z*vec_2%y cross_product%y = vec_1%z*vec_2%x - vec_1%x*vec_2%z cross_product%z = vec_1%x*vec_2%y - vec_1%y*vec_2%x END FUNCTION cross_product END MODULE vectors PROGRAM test_vectors !Purpose: !To test the definitions, operations, and assignments !associated with the vector data type. ! USE vectors IMPLICIT NONE ! List of variables: REAL, DIMENSION(3) :: array_out TYPE (vector) :: vec_1, vec_2 ! Output array ! Test vectors ! Test assignments by assigning an array to vec_1 and ! assigning vec_1 to array_out. vec_1 = (/ 1., 2., 3. /) array_out = vec_1 WRITE (*,*) vec_1, array_out ! Test addition and subtraction. vec_1 = (/10., 20., 30. /) vec_2 = (/1., 2., 3. /) WRITE (*,*) vec_1, vec_2, vec_1 + vec_2, vec_1-vec_2 ! Test multiplication by a scalar. vec_1 = (/1., 2., 3. /) WRITE (*,*) vec_1, 2.*vec_1, vec_1*2., 2*vec_1, vec_1*2 ! Test division by a scalar. vec_1 = (/10., 20., 30. /) WRITE (*,*) vec_1, vec_1/5., vec_1/5 ! Test dot product. vec_1 = (/ 1., 2., 3. /) vec_2 = (/1., 2., 3. /) WRITE (*,*) vec_1, vec_2, vec_1 .DOT. vec_2 ! Test cross product. vec_1 = (/ 1., -1., 1. /)

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

97

Forttran 90//95 For ran 90 95


vec_2 = (/ -1., 1., 1. /) WRITE (*,*) vec_1, vec_2, vec_1*vec_2 END PROGRAM test_vectors

8.8 Restringindo acesso ao contedo de um mdulo

At o momento todos os dados que foram colocados nos mdulos estavam totalmente disponveis e acessveis. No entanto as vezes no h interesse que determinados dados dos interior de um mdulo esteja disponvel fora do mdulo. Ento a questo : Como esconder alguns dados do interior de um mdulo? O Fortran dois atributos para tratar esta questo, o PUBLIC e o PRIVATE. Se declararmos uma varivel interna de um mdulo de PRIVATE, ela ficar totalmente indisponvel fora do ambiente do mdulo. Por outro lado ela continuar totalmente disponvel no interior do mdulo. O atributo PUBLIC o default e torna uma varivel totalmente disponvel, quer seja internamente ao mdulo quer seja externamente. Abaixo tem-se alguns exemplos de como efetuar esta declarao
integer, private :: conta real, public :: tension type (caixa), private :: caixa_sapato aconselhvel esconder qualquer dado ou subprograma de um mdulo que no precisem ser acessados de fora. A melhor maneira para ser fazer isto incluir o atribute PRIVATE em cada mdulo, e listar os itens especficos que se quer deixar visveis em uma declarao especfica com o atributo PUBLIC.

Boa Prtica

8.9 Opes avanadas da declarao USE

Quando um programa acessa um mdulo todas as informaes ali contidas ficam disponveis para os programa que est acessando, exceto aquelas protegidas com o atributo PRIVATE. No entanto nem todas as informaes disponveis sero usadas, assim interessante que se acesse apenas as que forem necessrias. Para restringir este acesso pode-se adicionar a especificao ONLY declarao USE. A forma de se fazer
use nome_do_modulo, only : lista_only

onde nome_do_modulo o nome do mdulo e lista_only a lista dos itens do mdulo a serem usadas. Nesta lista o itens so separados por vrgula. Tambm possvel renomear dados ou subprogramas de um mdulo quando usando a declarao USE. Uma das razes para renomear um item a existncia em algum local do programa de uma outra varivel com o mesmo nome. Fazendo esta renomeao no vai haver conflito de nomes. Outra razo surge quando um dado que usado com muita freqncia e tem um nome muito grande, pode-se ento renome-lo com um nome pequeno e economizar em tempo de digitao.
Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
98

Forttran 90//95 For ran 90 95


As formas de usar a declarao USE para permitir ao programador renomear algum dado ou subprograma so
use nome_do_modulo, lista_para_renomear use nome_do_modulo, only: lista_para_renomear

onde lista_para_renomear toma a forma


nome_no_modulo => nome_local

Na declarao abaixo
use data_fit, only : sp_real_least_square_fit => lsqfit

est utilizando-se um mdulo chamado data_fit. De todos os dados e subprogramas que este mdulo tem, utiliza-se apenas o chamado sp_real_least_square_fit que est sendo renomeado, localmente, para lsqfit.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

99

Forttran 90//95 For ran 90 95


Captulo 9 - Ponteiros e Estruturas Dinmicas de Dados
Ponteiros so variveis que no armazenam seu valor prprio, mas armazenam o endereo de uma outra varivel. Por isto o nome, ponteiro, aquele que aponta para uma outra varivel. O conceito de ponteiro introduz muita flexibilidade na construo de muitos algoritmos. Um exemplo simblico do poder dos ponteiros : Se voc diz que Jos da Silva mora na cidade de So Paulo, tm um significado; mas se voc diz Jos da Silva mora em So Paulo, na Rua Fantasia, 40, Apartamento 105, o significado ser muito mais claro, e a localizao de Jos da Silva ser muito mais rpida. Quase todas as variveis dos tipos bsicos do Fortran so estticas. Isto , so criadas no incio da execuo do programa e permanecem da mesma forma e na mesma posio de memria at o final da execuo. Isto tambm ocorre com variveis do tipo derivado. As excees a esta realidade so os arrays alocveis e os arrays automticos (em subprogramas). Neste captulo apresenta-se uma outra maneira de definir estruturas de dados que podem ser dinamicamente criadas e podem ter o tamanho varivel ao longo da execuo. Os ponteiros so fundamentais para viabilizar a existncia de tais estruturas. Imagine um programa que manipule a base de dados dos CPFs dos brasileiros, alm das atribuies de verificao de dados, certamente existir a necessidade de introduzir ou retirar nmeros da base de dados. Considere que vamos aumentar um nmero de CPF na lista, o array que guarda estas informaes enquanto na memria principal precisar ser incrementado de uma unidade com o programa j executando e com os dados j existentes j alocados na memria. Como fazer isto? Veremos neste captulo.
9.1 Ponteiros e alvos

Uma varivel ponteiro (pointer) quando possui o atributo POINTER na sua declarao de varivel, ou atravs de um declarao POINTER. Adiante apresenta-se alguns exemplos da sintaxe para definir ponteiros
real, pointer :: p_x pointer :: p_x

Como foi dito anteriormente um ponteiro no tm valor prprio mas guarda o endereo de memria de uma outra varivel. Uma varivel ponteiro dever ter o mesmo tipo declarado da varivel para qual aponta. De outra forma, um ponteiro s poder apontar para uma varivel que seja do seu mesmo tipo. Pode existir ponteiros apontando para variveis do tipo derivado. Ponteiros tambm podem apontar para arrays, neste caso na declarao do ponteiro estar explicito o rank do array mas no a sua extenso, de qualquer forma o ponteiro aponta para o primeiro elemento do array. Algumas maneiras de definir ponteiros para arrays so
integer, dimension(:), pointer :: p_y real, pointer, dimension(:,:) :: p_w pointer, real, dimension(:,:,:) :: p_z Os ponteiros acima apontam para arrays rank 1, rank 2 e rank 3, respectivamente. Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
100

Forttran 90//95 For ran 90 95


Para que seja possvel que um ponteiro aponte para uma varivel necessrio que a mesma seja um alvo. Em outras palavras, um alvo uma varivel que foi disponibilizada para o uso com ponteiros. Para uma varivel tornar-se um alvo necessrio que a mesma tenha o atributo TARGET ou atravs de uma declarao TARGET. A seguir alguns exemplos de declarao de variveis como alvo
real, target :: x integer, dimension(5), target :: y real, target, dimension(5,10) :: w 9.1.1 Declarao de atribuio de ponteiro

Um ponteiro pode ser associado a uma varivel alvo atravs de uma declarao de atribuio de ponteiro. Esta declarao tem o seguinte formato
ponteiro => alvo

onde ponteiro o nome da varivel ponteiro que ir conter o endereo do alvo, e alvo a varivel apontada pelo ponteiro. Depois que um ponteiro foi associado a uma varivel alvo, qualquer referncia ao ponteiro em realidade uma referncia varivel alvo. Se um ponteiro est associado a uma varivel alvo e ento novamente associado a uma segunda varivel alvo, a primeira associao ser desfeita. Em outras palavras um ponteiro no pode apontar para dois alvos simultaneamente, no entanto um alvo pode ser apontado por um, dois ou mais ponteiros. Quando um alvo est sendo apontado por dois ponteiros e um dos ponteiros movido, o outro ponteiro no ser modificado. Um valor que est armazenado em uma varivel alvo pode ser obtido referenciado a varivel alvo ou ento referenciado o ponteiro que est apontado para ela. Um ponteiro pode apontar para outro ponteiro, neste caso em realidade os dois ponteiros estaro apontando para o mesmo alvo.

Boa Prtica

Uma boa conduta na definio de variveis do tipo ponteiro colocar um p no incio do nome da varivel para se ter uma distino clara quando se l um programa de Quais variveis so ponteiros e quais no so.

9.1.2 Situao de um associao ponteiro-alvo

A situao da associao de um ponteiro e um alvo indica se um dado ponteiro est associado a um dado alvo. possvel trs situaes: associado (associated), indefinido (undefined) e desassociado (disassociated). Quando um ponteiro criado em uma declarao de tipo sua situao indefinido, quando associado a um alvo a situao ento associado, e quando desassociado do alvo sua situao torna-se desassociado. J vimos como declarar um ponteiro e como associar um ponteiro a um alvo, resta vermos como desassociar um ponteiro de seu alvo. Para tal necessrio apenas executar uma declarao NULLIFY com o seguinte formato
nullify(lista_de_ponteiros)

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

101

Forttran 90//95 For ran 90 95


onde nullify ir desassociar todos os ponteiros lista na lista_de_ponteiros de todos os seus alvos. A situao de todos os ponteiros da lista passar ento para desassociados. Um ponteiro ter sua situao indefinida desde que definido at o ponto que sofre uma atribuio. Como a situao indefinida ambgua importante que todos os ponteiros sejam nulificados usando nullify logo aps sua definio. A partir de ento a situao daquele ponteiro ser associado ou desassociado. Um ponteiro s pode referenciar uma varivel se estiver a ela associado. Para descobrir se um dado ponteiro est associado, ou associado a um dado alvo basta usar a funo lgica ASSOCIATED, a qual retornar um valor TRUE se estiver associado e um valor FALSE se no estiver associado. Esta funo apresenta caractersticas que quantidade varivel de argumentos, podendo ter um ou dois argumentos. No caso de possuir apenas um argumento, sua forma geral de uso
variavel_logica = associated(ponteiro)

que responder se ponteiro j est associado ou no. A Segunda forma de uso desta funo com dois argumentos
variavel_logica = associated(ponteiro, alvo)

a qual retornar a situao da associao de ponteiro e alvo.


9.2 Usando ponteiros em declaraes de atribuio

Quando um ponteiro aparece em uma expresso aritmtica, no lugar de um valor numrico ento o valor da varivel para a qual ele est apontando usado. Quando se escreve
p_x = p_y

o valor que est na varivel alvo apontada por p_y atribuda posio de memria da varivel alvo apontada por p_x. Adicionalmente, se escrevemos
p_x => p_y

significa que estamos apontando o ponteiro p_x para o ponteiro p_y, assim p_x ficar apontando para a mesma posio de memria que p_y, ou seja a varivel alvo de p_y. Abaixo apresenta-se um programa que utiliza ponteiros para trocar a posio de duas matrizes
program usando_pointer !Objetivo: Mostrar o uso de ponteiros ! !programador: joo batista aparecido ilha solteira, 16 de dezembro, 1999 ! implicit none integer, parameter :: i_max = 100, j_max =100 integer :: i,j real :: aux

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

102

Forttran 90//95 For ran 90 95


real, target, dimension(i_max,i_max) :: x,y !Definindo os alvos real, pointer, dimension(:,:) :: p_x, p_y, p_aux !Definindo os ponteiros nullify(p_x,p_y,p_aux) !Nulificando os ponteiros p_x =>x !Atribuindo o ponteiro p_x p_y =>y !Atribuindo o ponteiro p_y x = 1.0 y = 5.0 !Inicializando a matriz, x !Inicializando a matriz, y

!Fazendo o swap das matrizes da forma clssica do i = 1, i_max do j = 1, j_max aux = x(i,j) x(i,j) = y(i,j) y(i,j) = aux end do end do !Fazendo o swap das matrizes usando ponteiros p_aux => p_x p_x => p_y p_y => p_aux stop end program usando_pointer

9.3 Alocao dinmica de memria usando ponteiros

Um dos aspectos mais poderosos do uso de ponteiros em alocao dinmica que ele permite no s a criao de espaos na memria como tambm depois de criados aumentar ou diminuir estes espaos sem destruir os dados j armazenados. A maneira de se fazer isto parecida com aquela j vista usando arrays alocveis. Espaos em memria so alocados usando a declarao ALLOCATE, e desalocados usando DEALLOCATE. Neste a declarao ALLOCATE fica da seguinte forma
allocate(ponteiro_1(forma_do_ponteiro_1),,stat = status)

onde ponteiro_1 o nome do primeiro ponteiro a ser alocado, e assim por diante, forma_do_ponteiro_1 a forma, ou seja o rank e a extenso em cada direo que possui a estrutura para a qual o ponteiro vai apontar, e status o resultado para operao, se a alocao for bem sucedida seu valor zero, caso contrrio seu valor ser dependente de zero e depender da implementao de cada compilador, sendo necessrio consultar o manual do fabricante. A operao descrita acima criar um ponteiro que apontar para um conjunto de dados sem nome. Devido ao fato deste objeto no ter nome ele poder ser acessado apenas via ponteiro. Depois que a rotina acima executada com sucesso a situao do ponteiro ser associado. Se o ponteiro (ou ponteiros), que est associado a este objeto sem nome, for nulificado ou redirecionado para outro objeto, aquele objeto estar perdido para sempre. Porm ainda ocupando as posies de memria, criando o

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

103

Forttran 90//95 For ran 90 95


chamado vazamento de memria. Por isto importante que memria que foi alocada usando ALLOCATE seja desalocada usando DEALLOCATE, da seguinte maneira
deallocate(ponteiro, stat = status)

Quando um espao de memria desalocado usando DEALLOCATE automaticamente o ponteiro que apontava para aquele objeto nulificado. Logo abaixo apresenta-se a verso do programa anterior, agora usando alocao dinmica de memria
program usando_pointer !Objetivo: Mostrar alocao dinmica usando ponteiros ! !programador: joo batista aparecido ilha solteira, 16 de dezembro, 1999 ! implicit none integer, parameter :: i_max = 100, j_max =100 integer :: status real, pointer, dimension(:,:) :: p_x, p_y, p_aux !Definindo os ponteiros nullify(p_x,p_y,p_aux) !Nulificando os ponteiros allocate(p_x(i_max,j_max),p_y(i_max,j_max),stat = status) !Alocando memria p_x = 1.0 p_y = 5.0 !Inicializando o array para onde aponta p_x !Inicializando o array para onde aponta p_y

!Fazendo o swap das matrizes usando ponteiros p_aux => p_x p_x => p_y p_y => p_aux deallocate(p_x, stat=status) !Desalocando memria deallocate(p_y, stat=status) !Desalocando memria deallocate(p_aux, stat=status)!Desalocando memria stop end program usando_pointer

9.4 Usando ponteiros com estruturas derivadas de dados

Pode-se colocar ponteiros dentro de estruturas derivadas de dados. Assim seja a seguinte estrutura derivada de dados
type :: real_value real :: value type(real_value), pointer :: p end type

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

104

Forttran 90//95 For ran 90 95


Esta estrutura de dados acima toda vez que definida armazena um valor real, e um ponteiro que aponta para outra estrutura do mesmo tipo, e assim por diante. Estes elementos podem ser alocados dinamicamente e esta pilha pode ir crescendo o quanto se desejar. Para parar o processo basta nulificar a ponta do ultimo ponteiro, e para regredir o processo basta ir nulificando os ponteiros que foram abertos, dos ltimos para os primeiros, at o primeiro. Desta forma cria-se uma estrutura capaz de armazenar reais seqencialmente, como se fosse um array, mas cujo tamanho pode crescer ou decrescer continuamente em tempo de execuo. Abaixo apresenta-se um exemplo de utilizao deste tipo de dados onde um arquivo com nmeros reais, de tamanho desconhecido, lido, seqencialmente, at o final. O programa capaz de dinamicamente ir aumentando o tamanho da estrutura geral de dados para abrigar novas informaes.
PROGRAM linked_list ! ! ! ! ! ! ! ! ! Purpose: To read in a series of real values from an input data file and store them in a linked list. After the list is read in, it will be written back to the standard output device. Record of revisions: Date Programmer ==== ========== 01/28/95 S. J. Chapman

! Description of change ! ===================== ! original code IMPLICIT NONE ! Derived data type to store real values in TYPE :: real_value REAL :: value TYPE (real_value), POINTER :: p END TYPE ! List of variables: TYPE (real_value), POINTER :: head ! Pointer to head of list CHARACTER(len=20) :: filename ! Input data file name INTEGER :: nvals = 0 ! Number of data read TYPE (real_value), POINTER :: ptr ! Temporary pointer TYPE (real_value), POINTER :: tail ! Pointer to tail of list INTEGER :: istat ! status: 0 for success REAL :: temp, aux ! Temporary variable ! Get the name of the file containing the input data. WRITE (*,*) 'Enter the file name with the data to be read: ' READ (*,'(A20)') filename ! open input data file. OPEN ( UNIT=9, FILE=filename, STATUS='OLD', ACTION='READ',IOSTAT = istat )

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

105

Forttran 90//95 For ran 90 95


! Was the OPEN successful? fileopen: IF ( ISTAT == 0 ) THEN ! open successful ! The file was opened successfully, so read the data from ! it, and store it in the linked list. input: Do READ (9, *, IOSTAT=istat) temp ! Get value IF ( istat /= 0 ) EXIT ! Exit on end of data nvals = nvals + 1 ! Bump count IF (.NOT. ASSOCIATED(head)) THEN ! No values in list ALLOCATE (head,STAT=istat) ! Allocate new value tail => head ! Tail pts to new value NULLIFY (tail%p) ! Nullify p in new value tail%value = temp ! Store number ELSE ! Values already in list ALLOCATE (tail%p,STAT=istat) ! Allocate new value tail => tail%p ! Tail pts to new value NULLIFY (tail%p) ! Nullify p in new value tail%value = temp ! Store number END IF END Do input ! Now, write out the data. ptr => head output: DO IF (.NOT.ASSOCIATED(ptr)) EXIT WRITE (*,*) ptr%value ptr => ptr%p END DO output ELSE fileopen ! Else file open failed. Tell user. WRITE (*,*) 'File open failed--status = ', istat END IF fileopen END PROGRAM

! Pointer valid? ! Yes: Write value ! Get next pointer

A seguir um exemplo de arquivo de dados para se testar o programa acima


1.0 -2.0 3.0 -4.0 5.0 -6.0 7.0 -8.0 9.0

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

106

Forttran 90//95 For ran 90 95


-10.000

A estrutura derivada de dados utilizada acima pode ser estendida para conter no seu interior vrios valores reais, inteiros, lgicos, caracteres, complexos, assim uma extenso nesta direo seria, por exemplo, o que segue
type :: various_values complex :: c1 integer :: int1, int2 real :: value1, value2, value3 character(20) :: nome1, nome2 logical :: logic1, logic2, logic3 type(various_values), pointer :: p end type

Um outro tipo de extenso, na estrutura de dados acima, incorporar mais de uma ponteiro. Um exemplo bem conhecido na literatura a arvore binria que acrescenta mais um ponteiro ao tipo derivado de dados. Assim cada elemento na estrutura global estar apontando para nenhum, um ou dois outros elementos, formando assim uma arvore binria. Uma estrutura razoavelmente genrica de uma rvore binria seria
type :: various_values_and_two_pointers complex :: c1 integer :: int1, int2 real :: value1, value2, value3 character(20) :: nome1, nome2 logical :: logic1, logic2, logic3 type(various_values_and_two_pointers), pointer :: p1, p2 end type

Imagine-se uma arvore binria para armazenar e disponibilizar os primeiro, segundo e terceiro nomes de uma pessoa bem como o seu respectivo telefone. A seguir o programa que usa esta estrutura de dados
MODULE btree ! ! Purpose: ! To define the derived data type used as a node in the ! binary tree, and to define the operations >, <. and == ! for this data type. This module also contains the ! subroutines to add a node to the tree, write out the ! values in the tree, and find a value in the tree. ! ! Record of revisions: ! Date Programmer Description of change ! ==== ========== ===================== ! 02/04/96 S. J. Chapman Original code ! IMPLICIT NONE ! Restrict access to module contents. PRIVATE

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

107

Forttran 90//95 For ran 90 95


PUBLIC :: node, OPERATOR(>), OPERATOR(<), OPERATOR(==) PUBLIC :: add_node, write_node, find_node ! Declare type for a node of the binary tree. TYPE :: node CHARACTER(len=10) :: last CHARACTER(len=10) :: first CHARACTER :: mi CHARACTER(len=16) :: phone TYPE (node), POINTER :: before TYPE (node), POINTER :: after END TYPE INTERFACE OPERATOR (>) MODULE PROCEDURE greater_than END INTERFACE INTERFACE OPERATOR (<) MODULE PROCEDURE less_than END INTERFACE INTERFACE OPERATOR (==) MODULE PROCEDURE equal_to END INTERFACE CONTAINS RECURSIVE SUBROUTINE add_node (ptr, new_node) ! ! Purpose: ! To add a new node to the binary tree structure. ! TYPE (node), POINTER :: ptr ! Pointer to current pos. in tree TYPE (node), POINTER :: new_node ! Pointer to new node IF ( .NOT. ASSOCIATED(ptr) ) THEN ! There is no tree yet. Add the node right here. ptr => new_node ELSE IF ( new_node < ptr ) THEN IF ( ASSOCIATED(ptr%before) ) THEN CALL add_node ( ptr%before, new_node ) ELSE ptr%before => new_node END IF ELSE IF ( ASSOCIATED(ptr%after) ) THEN CALL add_node ( ptr%after, new_node ) ELSE ptr%after => new_node END IF END IF END SUBROUTINE add_node RECURSIVE SUBROUTINE write_node (ptr) ! ! Purpose: ! To write out the contents of the binary tree ! structure in order. ! TYPE (node), POINTER :: ptr ! Pointer to current pos. in tree

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

108

Forttran 90//95 For ran 90 95


! Write contents of previous node. IF ( ASSOCIATED(ptr%before) ) THEN CALL write_node ( ptr%before ) END IF ! Write contents of current node. WRITE (*,"(1X,A,', ',A,1X,A)") ptr%last, ptr%first, ptr%mi ! Write contents of next node. IF ( ASSOCIATED(ptr%after) ) THEN CALL write_node ( ptr%after ) END IF END SUBROUTINE write_node RECURSIVE SUBROUTINE find_node (ptr, search, error) ! ! Purpose: ! To find a particular node in the binary tree structure. ! "Search" is a pointer to the name to find, and will ! also contain the results when the subroutine finishes ! if the node is found. ! TYPE (node), POINTER :: ptr ! Pointer to curr pos. in tree TYPE (node), POINTER :: search ! Pointer to value to find. INTEGER :: error ! Error: 0 = ok, 1 = not found IF ( search < ptr ) THEN IF ( ASSOCIATED(ptr%before) ) THEN CALL find_node (ptr%before, search, error) ELSE error = 1 END IF ELSE IF ( search == ptr ) THEN search = ptr error = 0 ELSE IF ( ASSOCIATED(ptr%after) ) THEN CALL find_node (ptr%after, search, error) ELSE error = 1 END IF END IF END SUBROUTINE find_node LOGICAL FUNCTION greater_than (op1, op2) ! ! Purpose: ! To test to see if operand 1 is > operand 2 ! in alphabetical order. ! TYPE (node), INTENT(IN) :: op1, op2 CHARACTER(len=10) :: last1, last2, first1, first2 CHARACTER :: mi1, mi2 CALL ushift (op1, last1, first1, mi1 ) CALL ushift (op2, last2, first2, mi2 ) IF (last1 > last2) THEN greater_than = .TRUE. ELSE IF (last1 < last2) THEN

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

109

Forttran 90//95 For ran 90 95


greater_than = .FALSE. ELSE ! Last names match IF (first1 > first2) THEN greater_than = .TRUE. ELSE IF (first1 < first2) THEN greater_than = .FALSE. ELSE ! First names match IF (mi1 > mi2) THEN greater_than = .TRUE. ELSE greater_than = .FALSE. END IF END IF END IF END FUNCTION greater_than LOGICAL FUNCTION less_than (op1, op2) ! ! Purpose: ! To test to see if operand 1 is < operand 2 ! in alphabetical order. ! TYPE (node), INTENT(IN) :: op1, op2 CHARACTER(len=10) :: last1, last2, first1, first2 CHARACTER :: mi1, mi2 CALL ushift (op1, last1, first1, mi1 ) CALL ushift (op2, last2, first2, mi2 ) IF (last1 < last2) THEN less_than = .TRUE. ELSE IF (last1 > last2) THEN less_than = .FALSE. ELSE ! Last names match IF (first1 < first2) THEN less_than = .TRUE. ELSE IF (first1 > first2) THEN less_than = .FALSE. ELSE ! First names match IF (mi1 < mi2) THEN less_than = .TRUE. ELSE less_than = .FALSE. END IF END IF END IF END FUNCTION less_than LOGICAL FUNCTION equal_to (op1, op2) ! ! Purpose: ! To test to see if operand 1 is equal to operand 2 ! alphabetically. ! TYPE (node), INTENT(IN) :: op1, op2 CHARACTER(len=10) :: last1, last2, first1, first2 CHARACTER :: mi1, mi2 CALL ushift (op1, last1, first1, mi1 )

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

110

Forttran 90//95 For ran 90 95


CALL ushift (op2, last2, first2, mi2 ) IF ( (last1 == last2) .AND. (first1 == first2) .AND. & (mi1 == mi2 ) ) THEN equal_to = .TRUE. ELSE equal_to = .FALSE. END IF END FUNCTION equal_to SUBROUTINE ushift( op, last, first, mi ) ! ! Purpose: ! To create upshifted versions of all strings for ! comparison. ! TYPE (node), INTENT(IN) :: op CHARACTER(len=10), INTENT(INOUT) :: last, first CHARACTER, INTENT(INOUT) :: mi last = op%last first = op%first mi = op%mi CALL ucase (last) CALL ucase (first) CALL ucase (mi) END SUBROUTINE ushift SUBROUTINE ucase ( string ) ! ! Purpose: ! To shift a character string to UPPER case. ! ! Record of revisions: ! Date Programmer Description of change ! ==== ========== ===================== ! 11/25/95 S. J. Chapman Original code ! 1. 11/25/95 S. J. Chapman Modified for any collating seq ! IMPLICIT NONE ! Declare calling parameters: CHARACTER(len=*), INTENT(INOUT) :: string ! Declare local variables: INTEGER :: i ! Loop index INTEGER :: length ! Length of input string ! Get length of string length = LEN ( string ) ! Now shift lower case letters to upper case. DO i = 1, length IF ( ( string(i:i) >= 'a' ) .AND. & ( string(i:i) <= 'z' ) ) THEN string(i:i) = ACHAR ( IACHAR ( string(i:i) ) - 32 ) END IF END DO END SUBROUTINE ucase

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

111

Forttran 90//95 For ran 90 95


END MODULE btree PROGRAM binary_tree ! ! Purpose: ! To read in a series of random names and phone numbers ! and store them in a binary tree. After the values are ! stored, they are written out in sorted order. Then the ! user is prompted for a name to retrieve, and the program ! recovers the data associated with that name. ! ! Record of revisions: ! Date Programmer Description of change ! ==== ========== ===================== ! 02/06/95 S. J. Chapman Original code ! USE btree IMPLICIT NONE ! List of variables: INTEGER :: error ! Error flag: 0=success CHARACTER(len=20) :: filename ! Input data file name INTEGER :: istat ! Status: 0 for success TYPE (node), POINTER :: root ! Pointer to root node TYPE (node), POINTER :: temp ! Temp pointer to node ! Nullify new pointers NULLIFY ( root, temp ) ! Get the name of the file containing the input data. WRITE (*,*) 'Enter the file name with the input data: ' READ (*,'(A20)') filename ! Open input data file. Status is OLD because the input data must ! already exist. OPEN ( UNIT=9, FILE=filename, STATUS='OLD', ACTION='READ', & IOSTAT=istat ) ! Was the OPEN successful? fileopen: IF ( istat == 0 ) THEN ! Open successful

! The file was opened successfully, allocate space for each ! node, read the data into that node, and insert it into the ! binary tree. input: DO ALLOCATE (temp,STAT=istat) ! Allocate node NULLIFY ( temp%before, temp%after) ! Nullify pointers READ (9, 100, IOSTAT=istat) temp%last, temp%first, & temp%mi, temp%phone ! Read data 100 FORMAT (A10,1X,A10,1X,A1,1X,A16) IF ( istat /= 0 ) EXIT input ! Exit on end of data CALL add_node(root, temp) ! Add to binary tree END DO input ! Now, write out the sorted data. WRITE (*,'(/,1X,A)') 'The sorted data list is: ' CALL write_node(root)

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

112

Forttran 90//95 For ran 90 95


! Prompt for a name to search for in the tree. WRITE (*,'(/,1X,A)') 'Enter name to recover from tree:' WRITE (*,'(1X,A)',ADVANCE='NO') 'Last Name: ' READ (*,'(A)') temp%last WRITE (*,'(1X,A)',ADVANCE='NO') 'First Name: ' READ (*,'(A)') temp%first WRITE (*,'(1X,A)',ADVANCE='NO') 'Middle Initial: ' READ (*,'(A)') temp%mi ! Locate record CALL find_node ( root, temp, error ) check: IF ( error == 0 ) THEN WRITE (*,'(/,1X,A)') 'The record is:' WRITE (*,'(1X,7A)') temp%last, ', ', temp%first, ' ', & temp%mi, ' ', temp%phone ELSE WRITE (*,'(/,1X,A)') 'Specified node not found!' END IF check ELSE fileopen ! Else file open failed. Tell user. WRITE (*,'(1X,A,I6)') 'File open failed--status = ', istat END IF fileopen END PROGRAM

A seguir um arquivo de dados para se testar este programa


Carneiro Silva Forte Ricieri Alves Honda Park Smith Chung Ling Paulo Joo Ronaldo Maria Paula Eiko Sum John Chang Ding S M P R B H L W S P (602) (309) (987) (276) (145) (849) (743) (478) (586) (667) 534-2398 234-8534 854-4590 794-8520 639-7856 466-8866 136-3484 375-3678 975-8766 889-6544

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

113

Forttran 90//95 For ran 90 95


Captulo 10 - Ponteiros e Estruturas Dinmicas de Dados
Ponteiros so variveis que no armazenam seu valor prprio, mas armazenam o endereo de uma outra varivel. Por isto o nome, ponteiro, aquele que aponta para uma outra varivel. O conceito de ponteiro introduz muita flexibilidade na construo de muitos algoritmos. Um exemplo simblico do poder dos ponteiros : Se voc diz que Jos da Silva mora na cidade de So Paulo, tm um significado; mas se voc diz Jos da Silva mora em So Paulo, na Rua Fantasia, 40, Apartamento 105, o significado ser muito mais claro, e a localizao de Jos da Silva ser muito mais rpida. Quase todas as variveis dos tipos bsicos do Fortran so estticas. Isto , so criadas no incio da execuo do programa e permanecem da mesma forma e na mesma posio de memria at o final da execuo. Isto tambm ocorre com variveis do tipo derivado. As excees a esta realidade so os arrays alocveis e os arrays automticos (em subprogramas). Neste captulo apresenta-se uma outra maneira de definir estruturas de dados que podem ser dinamicamente criadas e podem ter o tamanho varivel ao longo da execuo. Os ponteiros so fundamentais para viabilizar a existncia de tais estruturas. Imagine um programa que manipule a base de dados dos CPFs dos brasileiros, alm das atribuies de verificao de dados, certamente existir a necessidade de introduzir ou retirar nmeros da base de dados. Considere que vamos aumentar um nmero de CPF na lista, o array que guarda estas informaes enquanto na memria principal precisar ser incrementado de uma unidade com o programa j executando e com os dados j existentes j alocados na memria. Como fazer isto? Veremos neste captulo.
10.1 Ponteiros e alvos

Uma varivel ponteiro (pointer) quando possui o atributo POINTER na sua declarao de varivel, ou atravs de um declarao POINTER. Adiante apresenta-se alguns exemplos da sintaxe para definir ponteiros
real, pointer :: p_x pointer :: p_x

Como foi dito anteriormente um ponteiro no tm valor prprio mas guarda o endereo de memria de uma outra varivel. Uma varivel ponteiro dever ter o mesmo tipo declarado da varivel para qual aponta. De outra forma, um ponteiro s poder apontar para uma varivel que seja do seu mesmo tipo. Pode existir ponteiros apontando para variveis do tipo derivado. Ponteiros tambm podem apontar para arrays, neste caso na declarao do ponteiro estar explicito o rank do array mas no a sua extenso, de qualquer forma o ponteiro aponta para o primeiro elemento do array. Algumas maneiras de definir ponteiros para arrays so
integer, dimension(:), pointer :: p_y real, pointer, dimension(:,:) :: p_w pointer, real, dimension(:,:,:) :: p_z Os ponteiros acima apontam para arrays rank 1, rank 2 e rank 3, respectivamente. Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05
114

Forttran 90//95 For ran 90 95


Para que seja possvel que um ponteiro aponte para uma varivel necessrio que a mesma seja um alvo. Em outras palavras, um alvo uma varivel que foi disponibilizada para o uso com ponteiros. Para uma varivel tornar-se um alvo necessrio que a mesma tenha o atributo TARGET ou atravs de uma declarao TARGET. A seguir alguns exemplos de declarao de variveis como alvo
real, target :: x integer, dimension(5), target :: y real, target, dimension(5,10) :: w 10.1.1 Declarao de atribuio de ponteiro

Um ponteiro pode ser associado a uma varivel alvo atravs de uma declarao de atribuio de ponteiro. Esta declarao tem o seguinte formato
ponteiro => alvo

onde ponteiro o nome da varivel ponteiro que ir conter o endereo do alvo, e alvo a varivel apontada pelo ponteiro. Depois que um ponteiro foi associado a uma varivel alvo, qualquer referncia ao ponteiro em realidade uma referncia varivel alvo. Se um ponteiro est associado a uma varivel alvo e ento novamente associado a uma segunda varivel alvo, a primeira associao ser desfeita. Em outras palavras um ponteiro no pode apontar para dois alvos simultaneamente, no entanto um alvo pode ser apontado por um, dois ou mais ponteiros. Quando um alvo est sendo apontado por dois ponteiros e um dos ponteiros movido, o outro ponteiro no ser modificado. Um valor que est armazenado em uma varivel alvo pode ser obtido referenciado a varivel alvo ou ento referenciado o ponteiro que est apontado para ela. Um ponteiro pode apontar para outro ponteiro, neste caso em realidade os dois ponteiros estaro apontando para o mesmo alvo.

Boa Prtica

Uma boa conduta na definio de variveis do tipo ponteiro colocar um p no incio do nome da varivel para se ter uma distino clara quando se l um programa de Quais variveis so ponteiros e quais no so.

10.1.2 Situao de um associao ponteiro-alvo

A situao da associao de um ponteiro e um alvo indica se um dado ponteiro est associado a um dado alvo. possvel trs situaes: associado (associated), indefinido (undefined) e desassociado (disassociated). Quando um ponteiro criado em uma declarao de tipo sua situao indefinido, quando associado a um alvo a situao ento associado, e quando desassociado do alvo sua situao torna-se desassociado. J vimos como declarar um ponteiro e como associar um ponteiro a um alvo, resta vermos como desassociar um ponteiro de seu alvo. Para tal necessrio apenas executar uma declarao NULLIFY com o seguinte formato
nullify(lista_de_ponteiros)

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

115

Forttran 90//95 For ran 90 95


onde nullify ir desassociar todos os ponteiros lista na lista_de_ponteiros de todos os seus alvos. A situao de todos os ponteiros da lista passar ento para desassociados. Um ponteiro ter sua situao indefinida desde que definido at o ponto que sofre uma atribuio. Como a situao indefinida ambgua importante que todos os ponteiros sejam nulificados usando nullify logo aps sua definio. A partir de ento a situao daquele ponteiro ser associado ou desassociado. Um ponteiro s pode referenciar uma varivel se estiver a ela associado. Para descobrir se um dado ponteiro est associado, ou associado a um dado alvo basta usar a funo lgica ASSOCIATED, a qual retornar um valor TRUE se estiver associado e um valor FALSE se no estiver associado. Esta funo apresenta caractersticas que quantidade varivel de argumentos, podendo ter um ou dois argumentos. No caso de possuir apenas um argumento, sua forma geral de uso
variavel_logica = associated(ponteiro)

que responder se ponteiro j est associado ou no. A Segunda forma de uso desta funo com dois argumentos
variavel_logica = associated(ponteiro, alvo)

a qual retornar a situao da associao de ponteiro e alvo.


10.2 Usando ponteiros em declaraes de atribuio

Quando um ponteiro aparece em uma expresso aritmtica, no lugar de um valor numrico ento o valor da varivel para a qual ele est apontando usado. Quando se escreve
p_x = p_y

o valor que est na varivel alvo apontada por p_y atribuda posio de memria da varivel alvo apontada por p_x. Adicionalmente, se escrevemos
p_x => p_y

significa que estamos apontando o ponteiro p_x para o ponteiro p_y, assim p_x ficar apontando para a mesma posio de memria que p_y, ou seja a varivel alvo de p_y. Abaixo apresenta-se um programa que utiliza ponteiros para trocar a posio de duas matrizes
program usando_pointer !Objetivo: Mostrar o uso de ponteiros ! !programador: joo batista aparecido ilha solteira, 16 de dezembro, 1999 ! implicit none integer, parameter :: i_max = 100, j_max =100 integer :: i,j real :: aux real, target, dimension(i_max,i_max) :: x,y !Definindo os alvos

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

116

Forttran 90//95 For ran 90 95


real, pointer, dimension(:,:) :: p_x, p_y, p_aux !Definindo os ponteiros nullify(p_x,p_y,p_aux) !Nulificando os ponteiros p_x =>x !Atribuindo o ponteiro p_x p_y =>y !Atribuindo o ponteiro p_y x = 1.0 y = 5.0 !Inicializando a matriz, x !Inicializando a matriz, y

!Fazendo o swap das matrizes da forma clssica do i = 1, i_max do j = 1, j_max aux = x(i,j) x(i,j) = y(i,j) y(i,j) = aux end do end do !Fazendo o swap das matrizes usando ponteiros p_aux => p_x p_x => p_y p_y => p_aux stop end program usando_pointer

10.3 Alocao dinmica de memria usando ponteiros

Um dos aspectos mais poderosos do uso de ponteiros em alocao dinmica que ele permite no s a criao de espaos na memria como tambm depois de criados aumentar ou diminuir estes espaos sem destruir os dados j armazenados. A maneira de se fazer isto parecida com aquela j vista usando arrays alocveis. Espaos em memria so alocados usando a declarao ALLOCATE, e desalocados usando DEALLOCATE. Neste a declarao ALLOCATE fica da seguinte forma
allocate(ponteiro_1(forma_do_ponteiro_1),,stat = status)

onde ponteiro_1 o nome do primeiro ponteiro a ser alocado, e assim por diante, forma_do_ponteiro_1 a forma, ou seja o rank e a extenso em cada direo que possui a estrutura para a qual o ponteiro vai apontar, e status o resultado para operao, se a alocao for bem sucedida seu valor zero, caso contrrio seu valor ser dependente de zero e depender da implementao de cada compilador, sendo necessrio consultar o manual do fabricante. A operao descrita acima criar um ponteiro que apontar para um conjunto de dados sem nome. Devido ao fato deste objeto no ter nome ele poder ser acessado apenas via ponteiro. Depois que a rotina acima executada com sucesso a situao do ponteiro ser associado. Se o ponteiro (ou ponteiros), que est associado a este objeto sem nome, for nulificado ou redirecionado para outro objeto, aquele objeto estar perdido para sempre. Porm ainda ocupando as posies de memria, criando o

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

117

Forttran 90//95 For ran 90 95


chamado vazamento de memria. Por isto importante que memria que foi alocada usando ALLOCATE seja desalocada usando DEALLOCATE, da seguinte maneira
deallocate(ponteiro, stat = status)

Quando um espao de memria desalocado usando DEALLOCATE automaticamente o ponteiro que apontava para aquele objeto nulificado. Logo abaixo apresenta-se a verso do programa anterior, agora usando alocao dinmica de memria
program usando_pointer !Objetivo: Mostrar alocao dinmica usando ponteiros ! !programador: joo batista aparecido ilha solteira, 16 de dezembro, 1999 ! implicit none integer, parameter :: i_max = 100, j_max =100 integer :: status real, pointer, dimension(:,:) :: p_x, p_y, p_aux !Definindo os ponteiros nullify(p_x,p_y,p_aux) !Nulificando os ponteiros allocate(p_x(i_max,j_max),p_y(i_max,j_max),stat = status) !Alocando memria p_x = 1.0 p_y = 5.0 !Inicializando o array para onde aponta p_x !Inicializando o array para onde aponta p_y

!Fazendo o swap das matrizes usando ponteiros p_aux => p_x p_x => p_y p_y => p_aux deallocate(p_x, stat=status) !Desalocando memria deallocate(p_y, stat=status) !Desalocando memria deallocate(p_aux, stat=status)!Desalocando memria stop end program usando_pointer

10.4 Usando ponteiros com estruturas derivadas de dados

Pode-se colocar ponteiros dentro de estruturas derivadas de dados. Assim seja a seguinte estrutura derivada de dados
type :: real_value real :: value type(real_value), pointer :: p end type

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

118

Forttran 90//95 For ran 90 95


Esta estrutura de dados acima toda vez que definida armazena um valor real, e um ponteiro que aponta para outra estrutura do mesmo tipo, e assim por diante. Estes elementos podem ser alocados dinamicamente e esta pilha pode ir crescendo o quanto se desejar. Para parar o processo basta nulificar a ponta do ultimo ponteiro, e para regredir o processo basta ir nulificando os ponteiros que foram abertos, dos ltimos para os primeiros, at o primeiro. Desta forma cria-se uma estrutura capaz de armazenar reais seqencialmente, como se fosse um array, mas cujo tamanho pode crescer ou decrescer continuamente em tempo de execuo. Abaixo apresenta-se um exemplo de utilizao deste tipo de dados onde um arquivo com nmeros reais, de tamanho desconhecido, lido, seqencialmente, at o final. O programa capaz de dinamicamente ir aumentando o tamanho da estrutura geral de dados para abrigar novas informaes.
PROGRAM linked_list ! ! ! ! ! ! ! ! ! Purpose: To read in a series of real values from an input data file and store them in a linked list. After the list is read in, it will be written back to the standard output device. Record of revisions: Date Programmer ==== ========== 01/28/95 S. J. Chapman

! Description of change ! ===================== ! original code IMPLICIT NONE ! Derived data type to store real values in TYPE :: real_value REAL :: value TYPE (real_value), POINTER :: p END TYPE ! List of variables: TYPE (real_value), POINTER :: head ! Pointer to head of list CHARACTER(len=20) :: filename ! Input data file name INTEGER :: nvals = 0 ! Number of data read TYPE (real_value), POINTER :: ptr ! Temporary pointer TYPE (real_value), POINTER :: tail ! Pointer to tail of list INTEGER :: istat ! status: 0 for success REAL :: temp, aux ! Temporary variable ! Get the name of the file containing the input data. WRITE (*,*) 'Enter the file name with the data to be read: ' READ (*,'(A20)') filename ! open input data file. OPEN ( UNIT=9, FILE=filename, STATUS='OLD', ACTION='READ',IOSTAT = istat )

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

119

Forttran 90//95 For ran 90 95


! Was the OPEN successful? fileopen: IF ( ISTAT == 0 ) THEN ! open successful ! The file was opened successfully, so read the data from ! it, and store it in the linked list. input: Do READ (9, *, IOSTAT=istat) temp ! Get value IF ( istat /= 0 ) EXIT ! Exit on end of data nvals = nvals + 1 ! Bump count IF (.NOT. ASSOCIATED(head)) THEN ! No values in list ALLOCATE (head,STAT=istat) ! Allocate new value tail => head ! Tail pts to new value NULLIFY (tail%p) ! Nullify p in new value tail%value = temp ! Store number ELSE ! Values already in list ALLOCATE (tail%p,STAT=istat) ! Allocate new value tail => tail%p ! Tail pts to new value NULLIFY (tail%p) ! Nullify p in new value tail%value = temp ! Store number END IF END Do input ! Now, write out the data. ptr => head output: DO IF (.NOT.ASSOCIATED(ptr)) EXIT WRITE (*,*) ptr%value ptr => ptr%p END DO output ELSE fileopen ! Else file open failed. Tell user. WRITE (*,*) 'File open failed--status = ', istat END IF fileopen END PROGRAM

! Pointer valid? ! Yes: Write value ! Get next pointer

A seguir um exemplo de arquivo de dados para se testar o programa acima


1.0 -2.0 3.0 -4.0 5.0 -6.0 7.0 -8.0 9.0

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

120

Forttran 90//95 For ran 90 95


-10.000

A estrutura derivada de dados utilizada acima pode ser estendida para conter no seu interior vrios valores reais, inteiros, lgicos, caracteres, complexos, assim uma extenso nesta direo seria, por exemplo, o que segue
type :: various_values complex :: c1 integer :: int1, int2 real :: value1, value2, value3 character(20) :: nome1, nome2 logical :: logic1, logic2, logic3 type(various_values), pointer :: p end type

Um outro tipo de extenso, na estrutura de dados acima, incorporar mais de uma ponteiro. Um exemplo bem conhecido na literatura a arvore binria que acrescenta mais um ponteiro ao tipo derivado de dados. Assim cada elemento na estrutura global estar apontando para nenhum, um ou dois outros elementos, formando assim uma arvore binria. Uma estrutura razoavelmente genrica de uma rvore binria seria
type :: various_values_and_two_pointers complex :: c1 integer :: int1, int2 real :: value1, value2, value3 character(20) :: nome1, nome2 logical :: logic1, logic2, logic3 type(various_values_and_two_pointers), pointer :: p1, p2 end type

Imagine-se uma arvore binria para armazenar e disponibilizar os primeiro, segundo e terceiro nomes de uma pessoa bem como o seu respectivo telefone. A seguir o programa que usa esta estrutura de dados
MODULE btree ! ! Purpose: ! To define the derived data type used as a node in the ! binary tree, and to define the operations >, <. and == ! for this data type. This module also contains the ! subroutines to add a node to the tree, write out the ! values in the tree, and find a value in the tree. ! ! Record of revisions: ! Date Programmer Description of change ! ==== ========== ===================== ! 02/04/96 S. J. Chapman Original code ! IMPLICIT NONE ! Restrict access to module contents. PRIVATE

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

121

Forttran 90//95 For ran 90 95


PUBLIC :: node, OPERATOR(>), OPERATOR(<), OPERATOR(==) PUBLIC :: add_node, write_node, find_node ! Declare type for a node of the binary tree. TYPE :: node CHARACTER(len=10) :: last CHARACTER(len=10) :: first CHARACTER :: mi CHARACTER(len=16) :: phone TYPE (node), POINTER :: before TYPE (node), POINTER :: after END TYPE INTERFACE OPERATOR (>) MODULE PROCEDURE greater_than END INTERFACE INTERFACE OPERATOR (<) MODULE PROCEDURE less_than END INTERFACE INTERFACE OPERATOR (==) MODULE PROCEDURE equal_to END INTERFACE CONTAINS RECURSIVE SUBROUTINE add_node (ptr, new_node) ! ! Purpose: ! To add a new node to the binary tree structure. ! TYPE (node), POINTER :: ptr ! Pointer to current pos. in tree TYPE (node), POINTER :: new_node ! Pointer to new node IF ( .NOT. ASSOCIATED(ptr) ) THEN ! There is no tree yet. Add the node right here. ptr => new_node ELSE IF ( new_node < ptr ) THEN IF ( ASSOCIATED(ptr%before) ) THEN CALL add_node ( ptr%before, new_node ) ELSE ptr%before => new_node END IF ELSE IF ( ASSOCIATED(ptr%after) ) THEN CALL add_node ( ptr%after, new_node ) ELSE ptr%after => new_node END IF END IF END SUBROUTINE add_node RECURSIVE SUBROUTINE write_node (ptr) ! ! Purpose: ! To write out the contents of the binary tree ! structure in order. ! TYPE (node), POINTER :: ptr ! Pointer to current pos. in tree

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

122

Forttran 90//95 For ran 90 95


! Write contents of previous node. IF ( ASSOCIATED(ptr%before) ) THEN CALL write_node ( ptr%before ) END IF ! Write contents of current node. WRITE (*,"(1X,A,', ',A,1X,A)") ptr%last, ptr%first, ptr%mi ! Write contents of next node. IF ( ASSOCIATED(ptr%after) ) THEN CALL write_node ( ptr%after ) END IF END SUBROUTINE write_node RECURSIVE SUBROUTINE find_node (ptr, search, error) ! ! Purpose: ! To find a particular node in the binary tree structure. ! "Search" is a pointer to the name to find, and will ! also contain the results when the subroutine finishes ! if the node is found. ! TYPE (node), POINTER :: ptr ! Pointer to curr pos. in tree TYPE (node), POINTER :: search ! Pointer to value to find. INTEGER :: error ! Error: 0 = ok, 1 = not found IF ( search < ptr ) THEN IF ( ASSOCIATED(ptr%before) ) THEN CALL find_node (ptr%before, search, error) ELSE error = 1 END IF ELSE IF ( search == ptr ) THEN search = ptr error = 0 ELSE IF ( ASSOCIATED(ptr%after) ) THEN CALL find_node (ptr%after, search, error) ELSE error = 1 END IF END IF END SUBROUTINE find_node LOGICAL FUNCTION greater_than (op1, op2) ! ! Purpose: ! To test to see if operand 1 is > operand 2 ! in alphabetical order. ! TYPE (node), INTENT(IN) :: op1, op2 CHARACTER(len=10) :: last1, last2, first1, first2 CHARACTER :: mi1, mi2 CALL ushift (op1, last1, first1, mi1 ) CALL ushift (op2, last2, first2, mi2 ) IF (last1 > last2) THEN greater_than = .TRUE. ELSE IF (last1 < last2) THEN

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

123

Forttran 90//95 For ran 90 95


greater_than = .FALSE. ELSE ! Last names match IF (first1 > first2) THEN greater_than = .TRUE. ELSE IF (first1 < first2) THEN greater_than = .FALSE. ELSE ! First names match IF (mi1 > mi2) THEN greater_than = .TRUE. ELSE greater_than = .FALSE. END IF END IF END IF END FUNCTION greater_than LOGICAL FUNCTION less_than (op1, op2) ! ! Purpose: ! To test to see if operand 1 is < operand 2 ! in alphabetical order. ! TYPE (node), INTENT(IN) :: op1, op2 CHARACTER(len=10) :: last1, last2, first1, first2 CHARACTER :: mi1, mi2 CALL ushift (op1, last1, first1, mi1 ) CALL ushift (op2, last2, first2, mi2 ) IF (last1 < last2) THEN less_than = .TRUE. ELSE IF (last1 > last2) THEN less_than = .FALSE. ELSE ! Last names match IF (first1 < first2) THEN less_than = .TRUE. ELSE IF (first1 > first2) THEN less_than = .FALSE. ELSE ! First names match IF (mi1 < mi2) THEN less_than = .TRUE. ELSE less_than = .FALSE. END IF END IF END IF END FUNCTION less_than LOGICAL FUNCTION equal_to (op1, op2) ! ! Purpose: ! To test to see if operand 1 is equal to operand 2 ! alphabetically. ! TYPE (node), INTENT(IN) :: op1, op2 CHARACTER(len=10) :: last1, last2, first1, first2 CHARACTER :: mi1, mi2 CALL ushift (op1, last1, first1, mi1 )

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

124

Forttran 90//95 For ran 90 95


CALL ushift (op2, last2, first2, mi2 ) IF ( (last1 == last2) .AND. (first1 == first2) .AND. & (mi1 == mi2 ) ) THEN equal_to = .TRUE. ELSE equal_to = .FALSE. END IF END FUNCTION equal_to SUBROUTINE ushift( op, last, first, mi ) ! ! Purpose: ! To create upshifted versions of all strings for ! comparison. ! TYPE (node), INTENT(IN) :: op CHARACTER(len=10), INTENT(INOUT) :: last, first CHARACTER, INTENT(INOUT) :: mi last = op%last first = op%first mi = op%mi CALL ucase (last) CALL ucase (first) CALL ucase (mi) END SUBROUTINE ushift SUBROUTINE ucase ( string ) ! ! Purpose: ! To shift a character string to UPPER case. ! ! Record of revisions: ! Date Programmer Description of change ! ==== ========== ===================== ! 11/25/95 S. J. Chapman Original code ! 1. 11/25/95 S. J. Chapman Modified for any collating seq ! IMPLICIT NONE ! Declare calling parameters: CHARACTER(len=*), INTENT(INOUT) :: string ! Declare local variables: INTEGER :: i ! Loop index INTEGER :: length ! Length of input string ! Get length of string length = LEN ( string ) ! Now shift lower case letters to upper case. DO i = 1, length IF ( ( string(i:i) >= 'a' ) .AND. & ( string(i:i) <= 'z' ) ) THEN string(i:i) = ACHAR ( IACHAR ( string(i:i) ) - 32 ) END IF END DO END SUBROUTINE ucase

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

125

Forttran 90//95 For ran 90 95


END MODULE btree PROGRAM binary_tree ! ! Purpose: ! To read in a series of random names and phone numbers ! and store them in a binary tree. After the values are ! stored, they are written out in sorted order. Then the ! user is prompted for a name to retrieve, and the program ! recovers the data associated with that name. ! ! Record of revisions: ! Date Programmer Description of change ! ==== ========== ===================== ! 02/06/95 S. J. Chapman Original code ! USE btree IMPLICIT NONE ! List of variables: INTEGER :: error ! Error flag: 0=success CHARACTER(len=20) :: filename ! Input data file name INTEGER :: istat ! Status: 0 for success TYPE (node), POINTER :: root ! Pointer to root node TYPE (node), POINTER :: temp ! Temp pointer to node ! Nullify new pointers NULLIFY ( root, temp ) ! Get the name of the file containing the input data. WRITE (*,*) 'Enter the file name with the input data: ' READ (*,'(A20)') filename ! Open input data file. Status is OLD because the input data must ! already exist. OPEN ( UNIT=9, FILE=filename, STATUS='OLD', ACTION='READ', & IOSTAT=istat ) ! Was the OPEN successful? fileopen: IF ( istat == 0 ) THEN ! Open successful

! The file was opened successfully, allocate space for each ! node, read the data into that node, and insert it into the ! binary tree. input: DO ALLOCATE (temp,STAT=istat) ! Allocate node NULLIFY ( temp%before, temp%after) ! Nullify pointers READ (9, 100, IOSTAT=istat) temp%last, temp%first, & temp%mi, temp%phone ! Read data 100 FORMAT (A10,1X,A10,1X,A1,1X,A16) IF ( istat /= 0 ) EXIT input ! Exit on end of data CALL add_node(root, temp) ! Add to binary tree END DO input ! Now, write out the sorted data. WRITE (*,'(/,1X,A)') 'The sorted data list is: ' CALL write_node(root)

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

126

Forttran 90//95 For ran 90 95


! Prompt for a name to search for in the tree. WRITE (*,'(/,1X,A)') 'Enter name to recover from tree:' WRITE (*,'(1X,A)',ADVANCE='NO') 'Last Name: ' READ (*,'(A)') temp%last WRITE (*,'(1X,A)',ADVANCE='NO') 'First Name: ' READ (*,'(A)') temp%first WRITE (*,'(1X,A)',ADVANCE='NO') 'Middle Initial: ' READ (*,'(A)') temp%mi ! Locate record CALL find_node ( root, temp, error ) check: IF ( error == 0 ) THEN WRITE (*,'(/,1X,A)') 'The record is:' WRITE (*,'(1X,7A)') temp%last, ', ', temp%first, ' ', & temp%mi, ' ', temp%phone ELSE WRITE (*,'(/,1X,A)') 'Specified node not found!' END IF check ELSE fileopen ! Else file open failed. Tell user. WRITE (*,'(1X,A,I6)') 'File open failed--status = ', istat END IF fileopen END PROGRAM

A seguir um arquivo de dados para se testar este programa


Carneiro Silva Forte Ricieri Alves Honda Park Smith Chung Ling Paulo Joo Ronaldo Maria Paula Eiko Sum John Chang Ding S M P R B H L W S P (602) (309) (987) (276) (145) (849) (743) (478) (586) (667) 534-2398 234-8534 854-4590 794-8520 639-7856 466-8866 136-3484 375-3678 975-8766 889-6544

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

127

Forttran 90//95 For ran 90 95


REFERNCIAS
Stephen J. Chapman (1998), Fortran 90/95 for Scientists and Engineers, McGraw-Hill, Boston, Massachusetts, 874 pages.

William H. Press, Saul A. Teukolsky, William T. Vetterling and Brian P. Flannery (1996), Numerical Recipes in Fortran 90 (The Art of Parallel Scientific Computing), Vol.2, Cambridge University Press, New York, 562pages Microsoft Corporation (1995), Fortran PowerStation Reference (Version 4.0), Microsoft Corporation, 614 pages. Microsoft Corporation (1995), Fortran PowerStation Programmers Guide (Version 4.0), Microsoft Corporation, 752 pages.

Verso preliminar. Escrito em 12/01/99. Impresso em 08/25/05

128

Você também pode gostar