Vous êtes sur la page 1sur 77

T

opicos avancados de programac


ao
No
ambito da Cadeira de Fsica Computacional 2006

Gest
ao de Programas Extensos
Bibliotecas
A Bilioteca gsl
Interface C -FORTRAN : SLATEC e LAPACK
Automatizac
ao com o GNU make
Outros t
opicos

Vtor M. Pereira
Departamento de Fsica da FCUP e Centro de Fsica do Porto

ii

iv

Conte
udo
Conte
udo

vi

Listagens

vii

Nota Pr
evia

ix

I.

Gest
ao de Programas Extensos
1

por Vtor M. Pereira

1. O modelo de compilac
ao do C
1.1. Ideias Basicas . . . . . . . . . . . . .
1.2. O modelo de compilacao em C . . .
1.3. Os passos da compilacao em detalhe
1.3.1. O pre-processador . . . . . .
1.3.2. O compilador . . . . . . . . .
1.3.3. O assembler . . . . . . . . . .
1.3.4. O linker . . . . . . . . . . . .
1.4. Linkagem est
atica e din
amica . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

3
3
3
4
6
7
8
8
10

2. Compilac
ao de m
ultiplas files
2.1. Distribuicao de c
odigo . . . . . . . . . . . . . . . . .
2.2. Criando ficheiros de objectos a partir das fontes . . .
2.3. Criando execut
aveis a partir de ficheiros de objectos
2.4. Recompilar e re-linkar . . . . . . . . . . . . . . . . .
2.5. Partilha de vari
aveis . . . . . . . . . . . . . . . . . .

2.5.1. Ambito
(Scope) . . . . . . . . . . . . . . . . .
2.5.2. Classes de armazenamento (storage classes) .
2.6. Organizacao dos dados em cada ficheiro . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

13
13
16
16
16
18
18
19
21

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

3. Criac
ao de Bibliotecas

23

4. Criac
ao e gest
ao de uma Makefile

27

II. Interface C -FORTRAN : As Bibliotecas SLATEC e LAPACK


31

por J. Lopes dos Santos

4.1. Rotinas em Fortran a partir de C . . . . .


4.1.1. Convencao de nomes . . . . . . . .
4.1.2. Chamada por referencia (pointers)
4.1.3. Compilacao . . . . . . . . . . . . .
4.2. Exemplos de utilizacao de rotinas SLATEC

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

33
33
33
35
35

Conte
udo

4.2.1. Apontadores para funcoes . . . . . . . . . . . . . . . . . . . . . . . . . . . .


4.3. Documentacao da Biblioteca SLATEC . . . . . . . . . . . . . . . . . . . . . . . . . .
4.4. Enderecos u
teis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35
39
40

III. A Biblioteca gsl


41

por Eduardo Castro e Vitor M. Pereira (em Construc


ao)

5. T
opicos preliminares
5.1. O que e a gsl . . . . . . . . . . . . . . . .
5.2. Utilizacao b
asica . . . . . . . . . . . . . . .
5.3. Geradores de N
umeros Aleat
orios . . . . . .
5.4. Funcoes como argumentos . . . . . . . . . .
5.5. Funcoes de n
umero de argumentos vari
avel
5.6. A funcao gsl function . . . . . . . . . . .

.
.
.
.
.
.

43
43
43
45
47
48
50

6. Exemplos
6.1. Minimizacao de Funcoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.1.1. Funcional de energia livre . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.1.2. Inicializacao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

53
53
53
54

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

IV. Outros T
opicos
por Vtor M. Pereira

57

7. Argumentos ao main
7.1. argv e argc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2. Vari
aveis de ambiente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

59
59
61

A. Lista de Comandos

65

Anexos

65

Bibliografia

67

vi

Listagens
1.1.
1.2.
1.3.
1.4.
2.1.
2.2.
2.3.
2.4.
2.5.
3.1.
3.2.
3.3.
4.1.
4.2.
4.3.
4.4.
4.5.
5.1.
5.2.
6.1.
6.2.
7.1.
7.2.
7.3.

C
odigo fonte do programa Hello World (hello.c). . . . . . . . . . . . . . . . . . .
C
odigo fonte pre-processado I (hello.i) . . . . . . . . . . . . . . . . . . . . . . . .
C
odigo fonte pre-processado II (hello.i) . . . . . . . . . . . . . . . . . . . . . . .
C
odigo fonte em assembly (hello.s) . . . . . . . . . . . . . . . . . . . . . . . . . .
C
odigo fonte do segmento main.c. . . . . . . . . . . . . . . . . . . . . . . . . . . .
C
odigo fonte do segmento hello fn.c. . . . . . . . . . . . . . . . . . . . . . . . . .
C
odigo fonte do header hello.h. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Uma alteracao local a main.c. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exemplos de
ambito de vari
aveis (scope.c). . . . . . . . . . . . . . . . . . . . . . .
Funcao bye() (bye fn.c). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Header hello2.h. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Driver para biblioteca libhello.a. . . . . . . . . . . . . . . . . . . . . . . . . . . .
Makefile para o projecto Hello World (Makefile). . . . . . . . . . . . . . . . . . .
Exemplo de c
odigo FORTRAN (twice.f). . . . . . . . . . . . . . . . . . . . . . . . . .
Exemplo de um programa em C que chama uma rotina de FORTRAN (calltwice.c).
Pr
ologo da rotina DGAUS8 do SLATEC (dgaus8.f). . . . . . . . . . . . . . . . . . . .
Exemplo de programa em C que chama a rotina DGAUS8 do SLATEC (calldgaus8.c).
Utilizacao b
asica da gsl (gsl-bessel.c) . . . . . . . . . . . . . . . . . . . . . . .
N
umeros aleat
orios com a gsl (gsl-random.c) . . . . . . . . . . . . . . . . . . . .
Definicao da entropia e energia livre para uma dada magnetizacao (minimization.c).
Programa principal (minimization.c). . . . . . . . . . . . . . . . . . . . . . . . .
Argumentos ao main() (show args.c). . . . . . . . . . . . . . . . . . . . . . . . . .
Problema 4 da folha Exerccios de linguagem C (show args.c). . . . . . . . . . .
Exemplo com vari
aveis de ambiente (environ.c). . . . . . . . . . . . . . . . . . . .

5
6
6
7
13
14
14
17
19
23
23
24
27
34
34
36
38
43
45
53
54
60
61
62

vii

Listagens

viii

Nota Pr
evia
Estas notas destinam-se primariamente aos alunos do curso de Fsica Computacional, leccionado
no departamento de Fsica da FCUP, e, por extensao, a todos quantos se interessarem pelos t
opicos
aqui aflorados. Re
une-se aqui material novo e textos ja existentes no contexto desta cadeira em
anos anteriores, preparados por alguns dos seus docentes.
Os exemplos apresentados est
ao orientados para o compilador gcc do projecto GNU (www.gnu.org),
incluindo algumas funcionalidades que, n
ao sendo embora parte do standard do C , consituem extens
oes a esse standard por parte do gcc 1 .
Ao longo do texto existem frequentes concessoes no que respeita `a utilizacao de termos de
lngua inglesa sem sobreaviso e destaque. A opcao pela utilizacao dos termos directamente na
lngua inglesa em vez da sua traducao e evidente: a quase totalidade da literatura existente nos
t
opicos abordados nestas notas e redigida nessa lngua. Seria talvez mesmo algo improdutivo
estar a introduzir traducoes para termos como assembler ou linker, os quais o leitor depois n
ao
encontraria em mais lado algum, causando confusoes desnecessarias.
Relativamente `
as convencoes, sempre que haja um comando a ser lancado na shell ele ser
a
apresentado como
$ comando

(0.1)

O elemento $ serve para identificar o texto `a sua frente como o comando a lancar e, naturalmente,
n
ao faz parte dele. Sempre que um comando devolva algum output, isso ser
a apresentado como
$ comando
linhas de output

(0.2)
(0.3)

As listagems de c
odigo fonte cujos ficheiros est
ao acessveis juntamente com este documento ser
ao
feitas dentro de caixas com devido destaque e numeracao, sendo tambem apresentado o nome do
ficheiro de c
odigo fonte ao qual a listagem se refere.
Finalmente, estas notas n
ao s
ao um produto acabado, encontrando-se em fase de desenvolvimento
e aperfeicoamento. Como tal, quaisquer coment
arios s
ao benvindos.
Vtor M. Pereira2
Porto, 25 de Maio de 2006.

1 Para

a lista completa das extenco


es do gcc : info gcc C Extensions.

2 vmpereir@fc.up.pt

ix

Nota Previa

Parte I.

Gest
ao de Programas Extensos
por Vtor M. Pereira

1. O modelo de compilac
ao do C
1.1. Ideias B
asicas
Uma nota previa acerca de uma linguagem de programacao que e diferente da linguagem da linguagem C : a shell da GNU . Quando um comando e lancado na shell, ele e imediatamente executado.
Alem disso, a shell e ela propria uma linguagem de programacao, no sentido em que os comandos
que o utilizador escreve s
ao um programa (e que pode tambem criar um ficheiro de texto contendo
uma sequencia de comandos da shell1 ).
Por outro lado, considere o caso do C . Enquanto que um script de comandos de shell pode ser
executado directamente, um programa em C precisa de ser criado essencialmente a dois tempos:
1. Em primeiro lugar, o c
odigo deve ser escrito num ficheiro de texto simples, usando por
exemplo um editor como o emacs . Ao programa nesta forma (ao(s) ficheiro(s) de texto
contendo o c
odigo) d
a-se o nome de c
odigo fonte.
2. Depois, o c
odigo fonte necessita ser processado por um compilador que gerar
a um novo
ficheiro contendo uma traducao do c
odigo fonte numa linguagem de maquina. Este ficheiro
e chamado de execut
avel, e diz-se que o execut
avel foi compilado a partir do c
odigo fonte.
Para executar um programa compilado, em geral e necess
ario escrever o nome do execut
avel
directamente na shell. Se o execut
avel se chamar prog, ent
ao para o correr bastar
a lancar na
shell2 :
$ ./prog

(1.1)

1.2. O modelo de compilac


ao em C
Quando compila um programa, o compilador opera executando uma sequencia ordenada de tarefas
a que se chama passos. Essa sequencia consiste aproximadamente no seguinte:
1. Pre-processamento (expans
ao de macros, inclus
ao de outros ficheiros, etc.);
2. Compilacao (do c
odigo fonte para linguagem assembly );
1E

o que se designa normalmente como um script.


parte do ponto-barra, ./, diz apenas a
` shell que o execut
avel se encontra na directoria actual. Caso contr
ario,
./ dever
a ser substitudo pelo caminho correspondente. O ./ pode ainda ser omitido se $PATH j
a o contiver.

2O

1. O modelo de compilacao do C

Figura 1.1.: Esquema das diferentes fases no modelo de compilacao de C .


3. Assembling (de linguagem assembly para ficheiros de objectos);
4. Link (criacao do execut
avel final a partir de um ou varios ficheiros de objectos).
Esta sequencia encontra-se esquematizada na Fig. 1.1. Em sistemas GNU , a compilacao de um
programa pode ser t
ao simples quanto:
$ gcc source.c

(1.2)

Esta instrucao criar


a o execut
avel chamado a.out a partir do c
odigo fonte contido no ficheiro
source.c. Em geral, estamos interessados em criar um execut
avel com um nome mais personalizado e, para isso, podemos pedir directamente ao compilador para gerar um execut
avel com o
nome desejado. Por exemplo, se prog for esse nome, faramos, para compilar e correr:
$ gcc -o prog source.c

(1.3)

$ ./prog

(1.4)

Um detalhe importante e que o gcc , para adivinhar o conte


udo de um determinado ficheiro,
usa algumas convencoes relativamente `
as extensoes nos nomes dos ficheiros, como as que se apresentam na Tabela 1.13 .

1.3. Os passos da compilac


ao em detalhe
Uma instrucao de compilacao simples como a instrucao (1.3), esconde, na verdade, toda uma
sequencia de passos intermedios desde o c
odigo fonte ate ao execut
avel. No entanto e sempre
3 Esta

e uma lista abreviada. A lista completa encontra-se no man gcc.

1.3. Os passos da compilacao em detalhe

Nome

filename.c
filename.i
filename.h
filename.o
filename.a
filename.so
filename.s
filename

o atribuda pelo gcc


Interpretac
a
C
odigo fonte que necessita pre-processamento;
C
odigo fonte que n
ao necessita de pre-processamento;
Header file a ser includa pelo pre-processador;
File de objectos;
Biblioteca est
atica de objectos;
Biblioteca partilhada de objectos;
C
odigo assembly ;
C
odigo execut
avel.

Tabela 1.1.: Interpretacao das extensoes de ficheiros (gcc ).

possvel, e altamente instrutivo, efectuar cada um dos passos listados na Fig. 1.1 separada e independentemente. De seguida iremos fazer isso para o hello world, o primeiro programa tipicamente
apresentado em qualquer livro sobre C . Suponhamos que o c
odigo fonte desse programa existe num
ficheiro chamado hello.c, e que e o seguinte:

/
File hello . c
Apenas i m p r i m e a mensagem de um programa recemn a s c i d o .

V i t o r M. P e r e i r a
/

#i n c l u d e < s t d i o . h>
9

11

13

i n t main ( v o i d )
{
p r i n t f ( H e l l o , w o r l d ! \ n ) ;
return 0;
}

Listagem 1.1: C
odigo fonte do programa Hello World (hello.c).

Naturalmente que para o testar bastar


a:
$ gcc -o hello hello.c

(1.5)

$ ./hello

(1.6)

Hello, world!

(1.7)

a seguir ao que aparecer


a no terminal a mensagem acima. Mas vamos ent
ao dissecar o processo
de compilacao, fazendo-o explicitamente passo-a-passo.

1. O modelo de compilacao do C

1.3.1. O pr
e-processador
O primeiro passo e ent
ao invocar o pre-processador para expandir os macros e os header files. Para
executar este passo, corremos
(1.8)

$ cpp hello.c > hello.i


, ou, alternativamente,

(1.9)

$ gcc -E hello.c > hello.i

O resultado e o ficheiro hello.i, contendo o c


odigo fonte com todos os macros e headers expandidos. Note-se que o ficheiro hello.i e ainda um ficheiro com c
odigo fonte em C . Por exemplo, as
primeiras 10 linhas contem o resultado da expans
ao do header stdio.h,

#
#
#
#

1
1
1
1

h e l lo . c
<b u i l t i n >
<command l i n e >
h e l lo . c

10

# 1 / u s r / i n c l u d e / s t d i o . h 1 3 4

Listagem 1.2: C
odigo fonte pre-processado I (hello.i)

, e nas linhas finais vem ent


ao o c
odigo editado em hello.c,

933

e x t e r n v o i d f u n l o c k f i l e ( FILE s t r e a m )
# 850 / u s r / i n c l u d e / s t d i o . h 3 4

attribute

((

nothrow

935

# 7 h e l lo . c 2
937

939

941

i n t main ( v o i d )
{
p r i n t f ( H e l l o , w o r l d ! \ n ) ;
return 0;
}

Listagem 1.3: C
odigo fonte pre-processado II (hello.i)

));

1.3. Os passos da compilacao em detalhe

1.3.2. O compilador
O passo seguinte e a compilacao propriamente dita, do c
odigo pre-processado para linguagem
assembly adequada ao processador especfico da maquina onde a compilacao est
a a ser efectuada.
A opcao -S ordena ao gcc que converta o c
odigo pre-processado em linguagem assembly , sem que
seja criado nenhum ficheiro de objectos:
$ gcc -Wall -S hello.i

(1.10)

Em resultado deste comando, ser


a criado um ficheiro hello.s contendo o c
odigo em agora em
assembly . Eis como resulta num processador Intel Centrino (i686) o c
odigo assembly assim gerado:
. file
hello . c
. section
. rodata

. LC0 :
4

10

12

14

16

18

20

22

24

26

. s t r i n g Hello , world !
. text
. g l o b l main
. type
main , @ f u n c t i o n
main :
leal
4(% e s p ) , %e c x
andl
$ 16 , %e s p
pushl
4(%e c x )
pushl
%ebp
movl
%esp , %ebp
pushl
%e c x
subl
$4 , %e s p
movl
$ . LC0 , (% e s p )
call
puts
movl
$0 , %e a x
addl
$4 , %e s p
popl
%e c x
popl
%ebp
leal
4(%e c x ) , %e s p
ret
. size
main , . main
. i d e n t GCC : (GNU) 4 . 1 . 0 20060304 ( Red Hat 4 . 1 . 0 3 )
. section
. n o t e . GNUs t a c k , , @ p r o g b i t s

Listagem 1.4: C
odigo fonte em assembly (hello.s)

Ainda que de passagem, notemos o aspecto seguinte. No nosso c


odigo em hello.c, invoc
amos a
funcao printf, que pertence `
a biblioteca standard do C . Ou seja, esta funcao este objecto n
ao
e definido por n
os mas existe algures numa biblioteca pre-complilada4. Este facto e revelado na
linha 17 do c
odigo assembly : a instrucao call puts e uma chamada `a funcao externa que fara a
tarefa do printf.
4 Essa

biblioteca, no meu sistema, chama-se libc.so

1. O modelo de compilacao do C

1.3.3. O assembler
O objectivo do assembler e converter linguagem assembly em c
odigo de maquina e gerar um ficheiro
de objectos. Havendo chamadas a funcoes externas no c
odigo assembly , o assembler deixa os
enderecos dessas funcoes indefinidos, para serem depois completados pelo linker. O assembler
pode ser invocado atraves da seguinte linha de comandos:
$ as hello.s -o hello.o

(1.11)

, ou, alternativamente,
$ gcc -c hello.c

(1.12)

, ou, ainda,
$ gcc -c hello.s

(1.13)

O ficheiro resultante, hello.o, contem as instrucoes em linguagem de maquina para o programa


hello world, mas contem ainda uma referencia indefinida `a funcao externa printf (ou, no meu
caso, como vimos acima, puts), uma vez que esta foi invocada, mas n
ao definida, em hello.c.
Nesta fase em que temos j
a o nosso c
odigo em linguagem de maquina est
a quase tudo pronto
para podermos executar o programa. Mas se isso fosse feito neste momento, o sistema operativo
n
ao saberia como imprimir o texto Hello World! porque n
ao saberia onde encontrar a tal funcao
printf. Para verificarmos isto mesmo podemos ver quais s
ao os objectos (as funcoes) existentes
no ficheiro hello.o. Basta usar o comando nm:
$ nm hello.o

(1.14)

00000000 T main
U puts
Como era de esperar, existem apenas duas funcoes: o main do nosso c
odigo em hello.c, e o printf,
aqui representado por puts. A parte importante e que, enquanto que ao main est
a associado um
endereco (o n
umero 00000000 na primeira coluna), ao puts n
ao, e por isso, aparece a letra U
(undefined symbol) na coluna central relativa a este objecto. Portanto est
a tudo pronto, falta
s
o colar os objectos, que e como quem diz, procurar onde est
ao os objectos que foram deixados
indefinidos pelo assembler de modo a que, quando o programa for executado, o sistema operativo
saiba o que fazer para executar todas as tarefas pedidas no c
odigo.

1.3.4. O linker
Chegamos ent
ao ao est
adio final da compilacao: o linker. Este ir
a linkar os ficheiros de objectos
e os objectos criando o execut
avel final. Na pratica, um execut
avel requer bastantes mais funcoes
associadas `
a interface com o sistema operativo durante o tempo de execucao (as chamadas run
time libraries). Consequentemente, as instrucoes de linkagem usadas internamente pelo gcc s
ao
em geral bastante complicadas. Por exemplo, o comando completo para linkar o programa Hello
World usando o GNU linker ld e

1.3. Os passos da compilacao em detalhe

$ ld -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o


/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/crtbegin.o
-L/usr/lib/gcc-lib/i686/3.3.1 hello.o -lc
/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/crtend.o /usr/lib/crtn.o
-o hello
O resultado deste comando ser
a o execut
avel chamado hello (passado atraves da opcao -o hello
acima), do nosso programa.
Bom, mas felizmente que n
ao e necess
ario reter na memoria todas as bibliotecas passadas acima
ao ld. O gcc faz isso por n
os de forma muito mais transparente (e conveniente!). A instrucao
anterior e, na verdade, equivalente a lancar na shell
$ gcc hello.o -o hello

(1.15)

Dada a extensao .o do ficheiro hello.o, o gcc sabe que se trata de um ficheiro de objectos e, como
e o u
nico ficheiro passado, ele encarrega-se de linkar os seus objectos com a biblioteca standard do
C e gerar o execut
avel. Temos finalmente um programa utilizavel:
$ ./hello
Hello, world!

(1.16)
(1.17)

Este execut
avel em nada se distingue daquele que obtivemos mais acima de forma bastante menos
penosa atraves de uma invocacao u
nica do gcc em (1.5).
Para terminar podemos mesmo confirmar que o nosso execut
avel incorpora um conjunto muito
maior de objectos alem dos que definimos no nosso c
odigo fonte (o main e o printf), examinando
a tabela de smbolos do execut
avel. Para tal recorremos novamente ao comando nm:
$ nm hello
08049544 A
080482d0 t
08049544 b
08049444 d
08049440 d
08049538 D
08049538 W
080483e4 t
080482f8 t
0804953c D
0804944c d
08049448 d
08049454 d
08049544 A
0804843c r
08049548 A
08048408 T
08049440 a
08049440 a
08048424 R

__bss_start
call_gmon_start
completed.1
__CTOR_END__
__CTOR_LIST__
__data_start
data_start
__do_global_ctors_aux
__do_global_dtors_aux
__dso_handle
__DTOR_END__
__DTOR_LIST__
_DYNAMIC
_edata
__EH_FRAME_BEGIN__
_end
_fini
__fini_array_end
__fini_array_start
_fp_hw

08048334 t frame_dummy
0804843c r __FRAME_END__
08049520 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
08048254 T _init
08049440 a __init_array_end
08049440 a __init_array_start
08048428 R _IO_stdin_used
08049450 d __JCR_END__
08049450 d __JCR_LIST__
w _Jv_RegisterClasses
08048388 T __libc_csu_fini
08048390 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
0804835c T main
08049540 d p.0
08049440 a __preinit_array_end
08049440 a __preinit_array_start
U puts@@GLIBC_2.0
080482ac T _start

1. O modelo de compilacao do C

1.4. Linkagem est


atica e din
amica
O processo de linkagem pode ser est
atico ou dinamico. Os sistemas UNIX e Linux (e em geral
todos os sistemas modernos) permitem a criacao e utilizacao destes dois tipos de bibliotecas:
aticas. Bibliotecas est
aticas n
ao s
ao mais do que conjuntos de ficheiros de objectos
dinamicas5 ou est
que ser
ao linkados a um dado programa durante a fase do link da compilacao. Nesta fase todos os
objectos requeridos pelo programa s
ao reunidos no execut
avel. As bibliotecas est
aticas, por si s
o,
n
ao s
ao relevantes depois de gerado o execut
avel nem durante a execucao6 .
As bibliotecas partilhadas, por outro lado, s
ao linkadas a um programa em dois tempos. Inicialmente, durante a compilacao, o linker verifica que todos os objectos requeridos pelo programa
est
ao, ou linkados ao programa, ou linkados a uma das suas bibliotecas partilhadas. Todavia, os
objectos da biblioteca din
amica n
ao s
ao inseridos directamente no execut
avel como acontece no
caso est
atico. Quando se corre o execut
avel, um outro programa do sistema7 vai encarregar-se de
verificar quais s
ao as bibliotecas partilhadas que foram linkadas com o execut
avel, de as carregar
na memoria e de anexar uma c
opia sua ao execut
avel residente na memoria.
A relativa complexidade de carregar dinamicamente os objectos partilhados torna o programa
relativamente lento (no que se refere ao incio da sua execucao), quando comparado com o mesmo
programa linkado estaticamente. No entanto, nas aplicacoes mais correntes como sejam programas para uso no nosso desktop este aspecto e amplamente ultrapassado pelas vantagens que
surgem quando um segundo programa que usa a mesma biblioteca partilhada e executado: este
pode usar a mesma c
opia da biblioteca em memoria e poupar assim nos recursos pedidos ao sistema.
Por exemplo, a biblioteca standard do C e normalmente uma biblioteca partilhada, e e utilizada
por todos os programas em C . O truque est
a em que apenas uma c
opia da biblioteca e carregada
na memoria, significando que e necess
aria muito menos memoria para a execucao de qualquer
programa em C . Outra vantagem
obvia e a de que, n
ao sendo os objectos partilhados incorporados directamente no execut
avel, o seu tamanho final ser
a muitssimo mais reduzido, poupando-se
tambem em espaco de disco.
Existe porem um detalhe importante na utilizacao de bibliotecas partilhadas. Suponhamos que
estamos a correr um programa compilado com uma dada biblioteca partilhada. Se essa biblioteca
for recompilada e tentarmos correr uma segunda c
opia do nosso programa com a nova biblioteca,
teremos um problema evidente: o loader ir
a ver que uma biblioteca com o mesmo nome est
a
ja carregada em mem
oria (para o primeiro programa) e ir
a linkar esta vers
ao antiga ao segundo
programa, em vez daquela recentemente compilada.
Quando um programa e compilado para usar bibliotecas partilhadas, estas precisarao de ser
carregadas dinamicamente ao tempo de execucao de modo a que seja possvel usar os objectos
externos que elas fornecem. O comando ldd examina um execut
avel e devolve uma lista das
bibliotecas partilhadas que esse programa requer para poder correr. Tais bibliotecas constituem as
dependencias partilhadas do execut
avel. Por exemplo, o comando seguinte mostra como encontrar
(no meu sistema) as dependencias do programa Hello Wold :
5 Tamb
em

designadas de partilhadas.

e, depois de gerado o execut


avel a exist
encia, ou n
ao, da biblioteca est
atica no sistema
e totalmente irrelevante
para a execuca
o do programa (da mesma forma que o ficheiro de objectos hello.o
e totalmente irrelevante depois
de gerado o execut
avel).
7 O chamado dynamic loader.
6 Isto

10

1.4. Linkagem est


atica e dinamica

$ ldd hello
linux-gate.so.1 => (0x002e2000)
libc.so.6 => /lib/libc.so.6 (0x00300000)
/lib/ld-linux.so.2 (0x002e3000)
Mas... isto significa que o execut
avel hello n
ao e independente! Ele s
o correra num sistema
onde existam estas 3 bibliotecas. Bom, neste caso isso n
ao e t
ao grave como parece porque, em
geral, qualquer sistema GNU /Linux ter
a um sistema de C e, sendo o mesmo tipo de arquitectura, o
nosso execut
avel correr
a l
a em princpio. Mas quando as dependencias partilhadas s
ao bibliotecas
ao disponveis no sistema onde pretendemos correr
especficas8, e importante garantir que elas estar
o programa com linkagem din
amica9 .
H
a casos em que poderemos n
ao estar muito interessados em que o nosso programa final fique
em geral possvel
com linkagem din
amica. Para esses casos h
a, felizmente, uma solucao simples. E,
especificar ao gcc que o execut
avel deve ser linkado estaticamente, mesmo quando os objectos
externos se encontram em bibliotecas partilhadas. O que acontece e que, quando nada e dito, o
gcc procede `
a linkagem din
amica daqueles objectos que se encontram em bibliotecas partilhadas
(porque em geral, como vimos, e o modo mais eficiente de usar os recursos em memoria e disco).
Mas, se os objectos se encontram l
a arquivados nessas bibliotecas, deve ser possvel usa-los como
qualquer outros ficheiros .o e, em particular, link
a-los estaticamente. Isso consegue-se passando a
opcao --static ao gcc . Portanto, se eu quiser criar um execut
avel do Hello World com linkagem
est
atica basta fazer:
$ gcc -Wall hello.c --static -o hello-static

(1.18)

, ou, se ja tiver o ficheiro de objectos:


$ gcc hello.o --static -o hello-static

(1.19)

Para ter a certeza de que o nosso novo execut


avel hello-static n
ao tem linkagem dinamica,
corremos outra vez o tabelador de dependencias din
amicas, ldd:
$ ldd hello-static
not a dynamic executable

(1.20)
(1.21)

Ca est
a: n
ao tem dependencias din
amicas nenhumas. Mas, claro, existem sempre os tais sen
ao a
que aludimos acima. Como o hello-static est
a agora linkado estaticamente, todos os objectos
fazem parte do execut
avel, incluindo os das run time libraries. Ora vejamos. Para hello, que e
dinamico, tenho no meu sistema:
$ nm hello | wc -l
39

(1.22)
(1.23)

8 Como

acontece com bibliotecas para resolver determinados problemas num


ericos.
um exemplo pr
atico. Quando instalamos uma peca qualquer de software num sistema GNU /Linux temos
essencialmente duas hip
oteses. Uma
e instalar a partir das fontes: consiste em copiar a totalidade do c
odigo fonte
do programa (normalmente um pacote .tar.gz), compil
a-lo na nossa m
aquina e depois colocar os execut
aveis nos
lugares apropriados (isto, em geral
e feito de forma simples com dois comandos apenas: make e make install). A
outra hip
otese
e instalar apenas os bin
arios o c
odigo execut
avel que algu
em j
a se encarregou de compilar antes.
Ora, sendo um bin
ario
e preciso garantir que a nossa m
aquina tem a mesma arquitectura daquela para a qual o
software foi compilado (da que, nestes casos, existam v
arias vers
oes de bin
arios para diferentes arquitecturas).
Al
em, disso, como quase todas as aplicaco
es recorrem a bibliotecas partilhadas, tanto num caso como no outro
os scripts de instalaca
o devem (e fazem-no em geral) verificar se as depend
encias existem no sistema.

9 Eis

11

1. O modelo de compilacao do C

exactamente 39 objectos. Mas no hello-static, que e est


atico, tenho:
$ nm hello-static | wc -l
1787

(1.24)
(1.25)

nada mais nada menos do que 1787 objectos! Isto reflecte-se, obviamente, no tamanho do executavel: enquanto que o execut
avel hello ocupa
$ ls -lh hello
-rwxr-xr-x 1 vpereira users 4.6K May 24 10:57 hello

(1.26)
(1.27)

4.6 KB, o est


atico hello-static ocpupa
$ ls -lh hello-static
-rwxr-xr-x 1 vpereira users 478K May 24 11:01 hello-static
478 KB, ou seja, e cerca de 100 vezes maior!

12

(1.28)
(1.29)

2. Compilac
ao de m
ultiplas files
2.1. Distribuic
ao de c
odigo
Depois de alguma pratica ou necessidade de programacao, facilmente se chega a um ponto em
que se torna conveniente dividir o c
odigo de um programa por varios ficheiros separados. Entre
muitas outras vantagens, esta divisao torna o c
odigo muito mais facil de gerir e, sobretudo se se
trata de c
odigo extenso e complexo, de entender. Entre as maiores vantagens de assim proceder,
est
a a possibilidade de compilar as diversas partes desse programa separada e independentemente.
Os programadores, em geral, desenham um programa dividindo-o por seccoes representativas de
determinadas tarefas que se pretende desempanhar. A ideia e que cada uma destas seccoes esteja
contida num ou mais ficheiros, os quais poder
ao conter uma ou varias funcoes. Um dos ficheiros
conter
a necessariamente o main(), e os restantes poder
ao ser considerados como uma biblioteca
de funcoes.
Para perceber como tal e possvel e como se efectua na pratica, nada melhor do que um exemplo
simples, nesta altura j
a nosso conhecido. No exemplo seguinte, dividiremos o programa Hello
a o nosso header file
World por tres ficheiros separados: main.c, hello fn.c e hello.h que ser
particular. Eis no que consiste o c
odigo de main.c:

/
F i l e main . c
U t i l i z a d a p a r a d e m o n s t r a r a d i s t r i b u i c a o de c o d i g o
por d i v e r s a s f i l e s .

V i t o r M. P e r e i r a
/

#i n c l u d e h e l l o . h
10

12

14

i n t main ( v o i d )
{
h e l l o ( world ) ;
return 0;
}

Listagem 2.1: Codigo fonte do segmento main.c.

agina 5, vemos que a chamada


Comparando-o com o c
odigo fonte mostrado na Listagem 1.1 da p
ao printf foi aqui substituda por uma chamada a uma nova funcao, apropriadamente chamada

13

2. Compilacao de m
ultiplas files

hello. Claro que esta u


ltima n
ao faz parte da biblioteca do C , sendo definida por n
os. Mas, em vez
de a declararmos e definirmos neste mesmo ficheiro main.c vamos faze-lo no ficheiro independente
udo e:
hello fn.c cujo conte

10

/
File hello fn . c
U t i l i z a d a p a r a d e m o n s t r a r a d i s t r i b u i c a o de c o d i g o
p o r d i v e r s a s f i l e s . Contem a d e f i n i c a o da f u n c a o
h e l l o u s a d a p e l o main . c

V i t o r M. P e r e i r a
/
#i n c l u d e < s t d i o . h>
#i n c l u d e h e l l o . h

12

14

16

void
h e l l o ( c o n s t c h a r name )
{
p r i n t f ( H e l l o , %s ! \ n , name ) ;
}

Listagem 2.2: C
odigo fonte do segmento hello fn.c.

Ou seja, esta funcao apenas imprime a string passada como argumento para o stdout, em geral,
o terminal. Claro que, como sabemos, o compilador precisa de conhecer as declaracoes de todas
as funcoes antes que estas sejam chamadas pela primeira vez. Da que, como a funcao hello ser
a
definida fora de main.c, seja necess
ario incluir um prot
otipo seu para que o seu tipo, argumentos
e return value sejam conhecidos pelo compilador quando este processar o ficheiro main.c. Isso
est
a assegurdo pela instrucao #include "hello.h" passada no cabecalho de main.c1. No nosso
header poremos apenas o tal prot
otipo:

/
File hello2 . h
U t i l i z a d a p a r a d e m o n s t r a r a d i s t r i b u i c a o de c o d i g o
p o r d i v e r s a s f i l e s . S e r a o h e a d e r comum que contem o
p r o t o t i p o da f u n c a o h e l l o r e q u e r i d a p e l o main . c
/

v o i d h e l l o ( c o n s t c h a r name ) ;
v o i d bye ( v o i d ) ;

Listagem 2.3: Codigo fonte do header hello.h.

Portanto, n
ao obstante estar repartido por tres ficheiros, este programa faz exactamente o mesmo
que o c
odigo apresentado na Listagem 1.1. Para o compilar, um dos metodos consiste em passar
importante que saiba a diferenca e o
que foi usado #include "hello.h" e n
ao #include <hello.h>. E
significado de cada uma das formas.

1 Note-se

14

2.1. Distribuicao de c
odigo

todos os ficheiros do c
odigo fonte ao gcc :
$ gcc -Wall main.c hello fn.c -o newhello

(2.1)

, chamando newhello ao novo execut


avel. Note-se que o header hello.h n
ao consta da lista de
ficheiros passadas ao gcc , precisamente porque a instrucao include no c
odigo encarrega-se de dar
a informacao `
acerca da necessidade deste ficheiro ao compilador. Note-se tambem que se apenas
passassemos main.c, teramos problemas:

$ gcc -Wall main.c


/tmp/ccGPQgAP.o: In function main:
main.c:(.text+0x19): undefined reference to hello
collect2: ld returned 1 exit status

, e o compilador queixar-se-ia, com toda a raz


ao, de que n
ao encontra uma tal funcao hello2 .
Bom, mas correndo o execut
avel gerado com o comando 2.1, oter-se-a, `a semelhanca de 1.5,
$ ./newhello
Hello, world!

(2.2)
(2.3)

Depois do captulo anterior, j


a sabemos que, ao lancar o comando gcc acima na shell, um conjunto
ordenado de passos ocorre atr
as do pano, para que o execut
avel seja gerado. Neste caso, em
que varios ficheiros com c
odigo fonte foram passados ao compilador, cada uma delas foi processada
independentemente ate `
a fase do assembling, sendo que, na fase seguinte do linking, o linker juntou
e organizou os todos objectos gerados independentemente ate a para os deixar no execut
avel final.
Ora isto claramente abre uma nova possibilidade. Quando um programa est
a inteiramente
contido num ficheiro u
nico e
obvio que qualquer alteracao na fonte, implica a recompilacao de
todo o c
odigo. E recompilar leva algum tempo, sobretudo se pensarmos num programa com varios
milhares de linhas de c
odigo e dezenas de funcoes/objectos. Alem disso, em geral as alteracoes ao
c
odigo s
ao relativamente localizadas: isto e, depois de termos um programa de pe, o mais certo
e serem necess
arios pequenos ajustes, algumas correccoes, e n
ao uma reescrita completa da fonte
desde o zero. Quando os programas s
ao organizados de forma a que os seus objectos estejam
definidos modularmente em ficheiros separados, estas tarefas ficam altamente simplificadas, uma
vez que apenas os ficheiros alterados necessitam de nova compilacao3 .
Neste metodo de trabalho, os ficheiros de c
odigo fonte s
ao compiladas separadamente e depois
linkadas um procedimento a dois tempos. No primeiro, compilam-se as fontes gerando apenas
os ficheiros de objectos correspondentes. No segundo, estes ficheiros de objectos s
ao combinadas
(linkadas) no execut
avel final.
2 Note-se

que
e precisamente o linker (o j
a nosso conhecido ld) quem se queixa atrav
es da mensagem collect2:
ld returned 1 exit status
3 Al
em do que, como
e evidente, fica muito mais f
acil trabalhar com v
arios ficheiros pequenos, do que com um
mega-ficheiro onde, apesar de toda a dilig
encia dos editores de texto, se poder
a perder mais tempo a
` procura da
linha a corrigir, do que a efectuar ou pensar na correcca
o.

15

2. Compilacao de m
ultiplas files

2.2. Criando ficheiros de objectos a partir das fontes


J
a sabemos desde o captulo anterior que ao gcc pode ser pedido que interrompa o processo de
compilacao no final de cada um dos seus passos intermedios. Em particular, sabemos ja que o
comando
$ gcc -Wall -c main.c

(2.4)

ir
a compilar o c
odigo em main.c e, em vez de gerar um execut
avel gera apenas um novo ficheiro
de objectos main.o. O comando correspondente para o ficheiro que contem a definicao da funcao
e
$ gcc -Wall -c hello fn.c

(2.5)

Nestes casos, n
ao e necess
ario usar a opcao -o para instruir o compilador `acerca do nome para o
ficheiro de objectos resultante, j
a que o gcc cria automaticamente um ficheiro com o mesmo nome,
substituindo .c por .o4 .

2.3. Criando execut


aveis a partir de ficheiros de objectos
O passo final na criacao de um execut
avel e usar o linker para linkar os nossos ficheiros de objectos.
Na pratica e muito mais f
acil (e seguro) usar o proprio gcc para essa tarefa. Se lhe forem passados
apenas ficheiros de objectos, o gcc sabe que devera apenas proceder ao link desses ficheiros e gerar
um execut
avel. Isto e, o nosso execut
avel newhello obtem-se de:
$ gcc main.o hello fn.o -o newhello

(2.6)

De passagem, repare-se que esta e uma das raras vezes em que n


ao invoc
amos a opcao -Wall
para o gcc , uma vez que ela apenas diz respeito ao compilador e, nesta fase, tanto main.c como
a compilados com sucsso. Alem disso, a fase do linking e um processo sem
hello fn.c foram j
qualquer margem para ambiguidades: ou linka ou n
ao linka5 , de modo que n
ao faz sentido ter
avisos nesta fase. Seja o gcc , seja o ld chamado a fazer o link, o produto final e mesmo, pronto a
funcionar:
$ ./newhello
Hello, world!

(2.7)
(2.8)

2.4. Recompilar e re-linkar


Claro, nenhum c
odigo minimamente serio, fica pronto, no seu estado final, depois da primeira
compilacao. Para efeitos do nosso exemplo, admitamos que n
ao era exactamente esta a mensagem
4 Como

e evidente, a opca
o -o pode ser usada tamb
em aqui, no caso de querermos que o ficheiro de objectos tenha
um nome diferente. No entanto isso raramente se justifica.
5 O link falha sempre que existam objectos que o linker n
ao consegue identificar ou encontrar. E se isso acontece o
execut
avel n
ao pode ser criado: n
ao faz sentido dar ao linker o livre arbtrio de decidir continuar neste ou naquele
caso (como o compilador, no sentido estrito, faz) porque perderamos totalmente o controlo e deixaramos de
estar a fazer programaca
o.

16

2.4. Recompilar e re-linkar

que pretendemos imprimir no ecr


a. Bom, nesse caso editamos main.c e fazemos a alteracao
necess
aria:

/
F i l e main . c
U t i l i z a d a p a r a d e m o n s t r a r a d i s t r i b u i c a o de c o d i g o
por d i v e r s a s f i l e s .

V i t o r M. P e r e i r a
/

#i n c l u d e h e l l o . h
10

12

14

i n t main ( v o i d )
{
h e l l o ( everyone ) ;
return 0;
}

Listagem 2.4: Uma alteracao local a main.c.

Para obter um novo execut


avel basta compilar o ficheiro recem alterado
$ gcc -Wall -c main.c

(2.9)

Dado que nada mudou no que diz respeito aos restantes segmentos do c
odigo, n
ao h
a necessidade
nica coisa a fazer e re-linkar o main.o com os
de recompilar a outro ficheiro hello fn.c. A u
ficheiros de objectos j
a compilados anteriormente:
$ gcc main.o hello fn.o -o newhello

(2.10)

Como e esperado, este newhello executa as novas instrucoes, imprimindo:


$ ./newhello
Hello, everyone!

(2.11)
(2.12)

Para um projecto computacional extenso, este modo de proceder poupa valioso tempo ao programador entre compilacoes. Estes passos podem ser facilmente automatizados atraves de um script
que incorpore a sequencia de comandos acima. Tudo isso, incluindo a verificacao autom
atica dos
ficheiros que carecem de recompilacao, e feito de um modo muito eficiente pelo programa make que
abordaremos mais adiante.
importante que n
E
ao se fique com a ideia de que utilidade deste esquema de compilacao se
limita aos casos de grandesprogramas, e aplicacoes complexas. Um exemplo t
ao prosaico como
a um exemplo desta metodologia em funcionamento: (quase)
um programa que calcule 2 e j
ninguem ir
a definir a sua propria funcao sqrt() para obter tal resultado. Ir
a, isso sim, recorrer,
por exemplo ao sqrt() fornecido pela biblioteca matematica do C . Mas ninguem ir
a compilar a
totalidade da biblioteca standard do C sempre que quiser calcular uma raz quadrada! Toda a gente
sabe que basta simplesmente utiliz
a-la compilando o c
odigo com a opcao -lm. Esta utilizaca
o

17

2. Compilacao de m
ultiplas files

t
ao trivial s
o e possvel porque sqrt(), juntamente com todos os outros objectos que constituem
essa biblioteca, existem j
a no sistema pre-compilados. O programador apenas trata de os linkar (o
-lm faz isso mesmo) com o seu c
odigo, de forma completamente transparente e c
omoda.
Numa u
ltima nota, igualmente evidente, diremos que a modularizacao de um programa permite
que uma determinada funcao, ou conjunto de funcoes, possa ser utilizado por varios programas
diferentes, sem que haja a necessidade de reescrever ou copiar constantemente o seu c
odigo de
programa para programa. Isto e u
til em todas as circunst
ancias, aplicando-se nomeadamente `as
rotinas numericas desenvolvidas no
ambito da cadeira de Fsica Computacional.

2.5. Partilha de vari


aveis
No exemplo modular que apresent
amos acima, toda a informacao a partilhar entre as diferentes
funcoes era passada atraves de par
ametros de funcoes usava apenas vari
aveis locais. Esta e
uma das formas de tornar acessveis a um modulo, vari
aveis e objectos declarados e inicializados
noutro. S
o que passar tudo como argumento de funcoes pode tornar-se laborioso muito rapidamente, quando a lista de par
ametros e extensa, ou o n
umero de modulos elevado. Alem disso,
a passagem de argumentos por valor implica a criacao de c
opias locais das vari
aveis envolvidas,
e, necess
ariamente, duplicacao da mem
oria, introduzindo dificuldades quando se trata de grandes
arrays ou estruturas.
Para obviar a estas dificuldades e/ou para criar um programa mais simples e legvel, e comum
o recurso a vari
aveis com um
ambito (scope) mais vasto do que apenas o local. Para c
odigo num
ficheiro u
nico, isso consegue-se atraves de vari
aveis declaradas fora de qualquer bloco/funcao, que
assim ter
ao
ambito global e duracao permanente. Mas para c
odigo repartido por varios ficheiros,
existem algumas subtilezas.

2.5.1. Ambito
(Scope)
Qualquer vari
avel presente no c
odigo fonte tem um ambito6 . O ambito da vari
avel define a porcao
do programa onde essa vari
avel est
a acessvel, pode ser acedida e manipulada. Para uma analogia, imagine-se um transeunte na rua. A partir do seu posto, ele pode ver determinadas coisas,
como sejam os predios `
a sua volta, ou outras pessoas na rua. Mas h
a certas coisas que ele n
ao
consegue ver, como sejam as pessoas que est
ao dentro dos predios que ele ve. Mas estas u
ltimas
conseguirao ver o transeunte das suas janelas. Precisamente do mesmo modo, algumas vari
aveis
de um programa s
ao como este homem visveis a partir de qualquer outra parte do programa
(as vari
aveis globais), enquanto que outras se encontram escondidas como os moradores dentro
das paredes que s
ao os parentesis curvos {}.
Os principais
ambitos possveis s
ao:
Prot
otipo Vari
aveis/funcoes numa lista de par
ametros de um prot
otipo de funcao tem ambito de
prot
otipo. Como se trata de um ambito altamente limitado, estes identificadores s
ao, na
pratica, pouco mais do que coment
arios.
6 N
ao

18

apenas as vari
aveis, mas qualquer identificador, tem um a
mbito.

2.5. Partilha de vari


aveis

Bloco Vari
aveis/funcoes declaradas dentro de um bloco ({}) tem o ambito desse bloco. Os
par
ametros de uma funcao, em particular, tem ambito de bloco sendo este delimitado pelos
({}) que delimitam a sua definicao. O ambito inicia no ponto em que a vari
avel e declarada,
e termina com o } do bloco correspondente.
File Vari
aveis/funcoes declaradas fora de todos os blocos e listas de par
ametros tem ambito de
ficheiro. O
ambito de ficheiro principia no ponto da declaracao extendendo-se ate ao final
desse ficheiro de c
odigo.
A listagem seguinte exemplifica alguns casos:

/
F i l e scope . c
Exemplos de s c o p e / a m b i t o de v a r i a v e i s

V i t o r M. P e r e i r a
/

int a ;

10

12

14

i n t main ( )
{
int b ;

16

// Ambito g l o b a l . A v a r i a v e l a e v i s i v e l n e s t e p o n t o do
// programa , e a t e ao f i n a l d e s t e f i c h e i r o de c o d i g o mas
// a s v a r i a v e i s b e c nao s a o v i s i v e i s a q u i .

//
//
//
//

Ambito l o c a l no main ( ) . As v a r i a v e i s a e b s a o
v i s i v e i s a q u i . A v a r i a v e l b e v i s i v e l a t e ao } que
t e r m i n a o main ( ) . V a r i a v e i s c e d nao s a o v i s i v e i s
aqui .

18

{
int c ;

20

22

24

int d ;

26

// Ambito l o c a l n e s t e b l o c o d e l i m i t a d o p o r e s t e s
// { . . . } . V a r i a v e i s a , b e c s a o t o d a s v i s i v e i s a q u i .

//
//
//
//

Ambito l o c a l no main ( ) . As v a r i a v e i s a e b e d s a o
v i s i v e i s a q u i . A v a r i a v e l d tem um s c o p e a n a l o g o ao
de b , mas m a i s l i m i t a d o , uma v e z que e s s e s c o p e
comeca n e s t e p o n t o e a t e ao f i m do main ( ) .

return 0;

28

Listagem 2.5: Exemplos de ambito de vari


aveis (scope.c).

2.5.2. Classes de armazenamento (storage classes)


A sintaxe geral para a declaracao de uma vari
avel e
[storage_class] type D1 [, D2, ...];

19

2. Compilacao de m
ultiplas files

o da Declarac
o
Posic
a
a

Especificador

Ambito

o
Durac
a

Fora de qualquer bloco


Dentro de um bloco
Dentro de um bloco

nenhum, extern, static


nenhum, auto, register
extern, static

File
Bloco
Bloco

Est
atica
Autom
atica
Est
atica

Tabela 2.1.: Classes de armazenamento, ambito e suracao de vari


aveis.
, onde as partes entre [...] s
ao opcionais.
O ponto onde uma vari
avel e declarada n
ao determina univocamente o seu ambito quando o
c
odigo se encontra repartido por diferentes ficheiros. Temos assim de considerar as classes de
armazenamento de uma vari
avel, as quais deteminam o seu ambito, a sua duracao e o seu modo
de linkar. Quanto ao seu tempo de vida, podemos ter vari
aveis:
Est
aticas A vari
avel e gerada e inicializada apenas uma vez, antes de o programa iniciar. A
vari
avel existe continuamente ao longo da execucao do programa.
Autom
aticas A vari
avel e gerada de novo sempre que a execucao do programa entra no bloco no
qual ela est
a definida. Quando esse bloco e terminado, a memoria ocupada pela vari
avel e
libertada.
A classe de armazenamento de uma vari
avel e determinada pela posicao da sua declaracao no
c
odigo fonte, e pelo especificador de armazenamento, se houver. Este e um de entre:
raramente usado uma vez que toauto Determina que a vari
avel ter
a uma duracao autom
atica. E
das as vari
aveis declaradas dentro de um bloco sem nenhum identificador de armazenamento
tem duracao autom
atica, por omissao.
static Vari
aveis assim declaradas tem duracao est
atica. Este especificador e utilizado para declarar vari
aveis est
aticas com um ambito limitado.
extern Este especificador usa-se para declarar vari
aveis com duracao est
atica e que podem ser
utilizadas em todo o c
odigo fonte, incluindo ficheiros separadas.
register Solicita ao compilador para armazenar a vari
avel em causa num registo do CPU, se
tal for possvel. Consequentemente, o operador de enderecamento deixa de poder ser usado,
mas, em todos os outros aspectos, s
ao equivalentes `as vari
aveis declaradas como auto.
Tendo isto em conta, podemos resumir as varias propriedades das vari
aveis na tabela 2.1.
importante notar, que, em C , todas as funcoes s
E
ao automaticamente extern e tem o ambito
tambem para respeitar as regras de ambito, que e necessario incluir um prot
de ficheiro. E
otipo de
todas as funcoes (ou a sua definicao, se for o caso) antes que elas sejam chamadas nalguma porcao
de c
odigo: s
o assim se garante que elas estejam no ambito correcto. Por esse motivo, os prot
otipos
vao, ou devem ir, sempre especificados nos header files.

20

2.6. Organizacao dos dados em cada ficheiro

2.6. Organizac
ao dos dados em cada ficheiro
Todos os ficheiros de c
odigo devem ter uma organizacao coerente e funcional. A ordem pela qual
as instrucoes aparecem num ficheiro de c
odigo e, tipicamente:
Um pre
ambulo, onde constar
ao varios #define, #include, e typedef relativos a tipos de
dados importantes;
Declaracao de todas as vari
aveis externas e globais. Estas u
ltimas poder
ao ser inicializadas
tambem nesta fase;
Uma ou varias funcoes.
A ordem destes items e importante, uma vez que em C todo e qualquer objecto tem de ser declarado
antes de utilizado pela primeira vez. Funcoes com return, devem ser definidas, ou pelo menos
prototipadas, antes de serem chamadas. Estes prot
otipos encontram-se geralmente numa das
header file (*.h).

21

2. Compilacao de m
ultiplas files

22

3. Criac
ao de Bibliotecas
Um biblioteca e uma coleccao de ficheiros de objectos reunidos num u
nico ficheiro designado de
portanto, um modo conveniente de distribuir um largo n
arquivo. E,
umero de ficheiros de objectos
relacionados entre si. Para exemplicar esta funcionalidade, e como se pode tirar partido dela,
demonstraremos a seguir como usar a aplicacao ar (GNU archiver) para criar um biblioteca est
atica1 .
Invoquemos o nosso sempre presente projecto do Hello World, para o qual vamos criar uma
biblioteca chamada libhello.a que vai conter a definicao de duas funcoes. Uma delas e a funcao
aa
hello() que est
a definida no ficheiro hello fn.c apresentado na Listagem 2.2. A outra ser
funcao bye() que definiremos num ficheiro chamado bye fn.c:

/
F ile bye fn . c
U t i l i z a d a p a r a d e m o n s t r a r a d i s t r i b u i c a o de c o d i g o
p o r d i v e r s a s f i l e s . Contem a d e f i n i c a o da f u n c a o
bye u s a d a p e l o main . c

V i t o r M. P e r e i r a
/

11

#i n c l u d e < s t d i o . h>
#i n c l u d e h e l l o 2 . h

13

15

17

v o i d bye ( v o i d )
{
p r i n t f ( Goodbye ! \ n ) ;
}

Listagem 3.1: Funcao bye() (bye fn.c).

Ambas as funcoes usam o header hello2.h:

/
File hello2 . h
U t i l i z a d a p a r a d e m o n s t r a r a d i s t r i b u i c a o de c o d i g o
p o r d i v e r s a s f i l e s . S e r a o h e a d e r comum que contem o
p r o t o t i p o da f u n c a o h e l l o r e q u e r i d a p e l o main . c
/
1 Limitamo-nos

aqui a
` descrica
o de bibliotecas est
aticas

23

3. Criacao de Bibliotecas

v o i d h e l l o ( c o n s t c h a r name ) ;
v o i d bye ( v o i d ) ;

Listagem 3.2: Header hello2.h.

O primeiro passo na criacao da biblioteca e compilar as fontes e gerar os ficheiros de objectos


correspondentes:
$ gcc -Wall -c hello_fn.c
$ gcc -Wall -c bye_fn.c
$ ls bye_fn.* hello_fn.*
bye_fn.c bye_fn.o hello_fn.c

hello_fn.o

De seguida criamos a biblioteca usando o ar:


$ ar cr libhello.a hello fn.o bye fn.o

(3.1)

O cr acima trata-se de duas opcoes para o ar (c: criar o arquivo, r: substituir no caso de ja
existir um .o com o mesmo nome no arquivo). O nome pretendido para a biblioteca (libhello.a)
e passado antes dos seus membros e, se ela ainda n
ao existir ser
a ent
ao criada. O ar tambem
permite listar o conte
udo das bibliotecas, atraves da opcao t (tabelar):
$ ar t libhello.a
hello_fn.o
bye_fn.o
Bom, agora que temos uma biblioteca h
a que lhe dar algum uso. Para isso criamos um main
bem simples:

/
F i l e main . c
U t i l i z a d a p a r a d e m o n s t r a r a u t i l i z a c a o de
uma b i b l i o t e c a recemc r i a d a .

V i t o r M. P e r e i r a
/

#i n c l u d e h e l l o 2 . h
10

12

14

16

i n t main ( v o i d )
{
h e l l o ( everyone ) ;
bye ( ) ;
return 0;
}

Listagem 3.3: Driver para biblioteca libhello.a.

24

Temos agora duas hip


oteses para compilar este programa (main.c). A primeira e simplesmente:
$ gcc -Wall main.c libhello.a -o hello

(3.2)

, que fara com que o main() seja linkado com os objectos da biblioteca. A segunda hipotese e usar
um atalho e recorrer `
a opcao -l do gcc :
$ gcc -Wall -L. main3.c -lhello -o hello

(3.3)

Neste u
ltimo comando, a opcao -lhello instrui o gcc a linkar com a biblioteca libhello.a, e -L
serve para dizer ao gcc para procurar essa biblioteca na directoria actual2 . Note-se que a nossa
recem-criada biblioteca libhello.a e passada ao gcc como passaramos, por exemplo, a biblioteca
matematica usando -lm. A parte a reter e que, quando uma bibliteca e passada ao gcc atraves do
-l, deve indicar-se apenas a porcao no seu nome que vem `a frente de lib, tal como fizemos acima.
Correndo o nosso execut
avel obtemos ent
ao:
$ ./hello
Hello, everyone!
Goodbye!
, e confirmamos a simplicidade que e criar e usar uma biblioteca definida `a medida das nossas
necessidades. Fica implcito no que se referiu acima que esta nossa biblioteca e usada e invocada
como qualquer outra biblioteca est
atica do sistema ou standart do C (n
ao tem um estatuto superior
nem inferior por ter sido criada por n
os).

2E

preciso indicar isto ao gcc porque, por omiss


ao, ele s
o procura bibliotecas num conjunto restrito de caminhos.
Uma alternativa a usar -L constantemente consiste em adicionar caminhos a
` vari
avel de ambiente LIBRARY PATH
ou LD LIBRARY PATH.

25

3. Criacao de Bibliotecas

26

4. Criac
ao e gest
ao de uma Makefile
Para aqueles n
ao familiarizados com o programa make , este captulo apresenta uma demonstracao
simples da sua utilizacao. O make entra em cena sempre que o nosso c
odigo comeca a ficar dema quase impens
siado extenso e/ou segmentado em diferentes ficheiros e bibliotecas. E
avel compilar
um programa com dezenas de ficheiros de c
odigo, usando uma instrucao gcc para cada um deles numa shell. Alem disso, j
a sabemos que em muitos casos, apenas um ficheiro precisa de ser
recompilado, mesmo em projectos grandes.
O make determina automaticamente quais s
ao as partes do programa que carecem de recompilacao e procede `
a compilacao apenas destes segmentos. Para isso, o make usa um script, um
conjunto de regras e instrucoes, criado pelo programador e normalmente chamado Makefile . A
Makefile contem basicamente um resumo do projecto, das dependencias e das instrucoes de compilacao para gerar um ou varios produtos finais.
Mais concrectamente, a Makefile especifica um conjunto de regras de compilacao em termos
de alvos (targets) como sejam os execut
aveis finais pretendidos e das suas dependencias
como sejam as fontes ou ficheiros de objectos de acordo com o formato seguinte:
alvo:
depend^
encias
comando
Para cada alvo, o make verifica o tempo da u
ltima alteracao de todas as suas dependencias para
deteminar quais delas foram alteradas e, consequentemente, necessitam de ser recompiladas usando
muito importante reter que as linhas onde se encontra(m) o(s) comando devem ser
o comando. E
indentadas com um u
nico TAB, sem espacos.
O make da GNU contem um conjunto implcito de regras que simplificam muito a construcao de
Makefile s. Estas especificam, por exemplo, que ficheiros .o s
ao obtidos de ficheiros .c atraves
de compilacao; e que um execut
avel se cria linkando os ficheiros .o. Estas regras implcitas est
ao
definidas em termos de vari
aveis, tais como CC (o compilador de C ), ou CFLAGS (as opcoes de
compilacao a passar ao compilador). Estas e quaisquer outras vari
aveis podem ser definidas usando
instrucoes do tipo
VARIAVEL=VALOR
em qualquer ponto da Makefile . Vejamos um exemplo do make em accao retomando o nosso
exemplo do programa Hello World repartido em ficheiros separados, tal como discutido na seccao
2. Antes de mais nada criamos a Makefile seguinte usando um editor de texto convencional:

27

4. Criacao e gestao de uma Makefile

# F i l e Makefile
# Exemplo de uma m a k e f i l e p a r a o H e l l o World
# D e f i n a m o s que o n o s s o c o m p i l a d o r e o g c c :
CC=g c c

10

# D e f i n a m o s q u a i s a s o p c o e s a p a s s a r ao g c c
CFLAGS=W a l l
# Definamos o t a r g e t para c o m p i l a r e g e r a r o e x e c u t a v e l
main : main . o h e l l o f n . o

12

14

# O t a r g e t clea n s e r v e apenas para apagar os . o


clean :
rm f main . o h e l l o f n . o

Listagem 4.1: Makefile para o projecto Hello World (Makefile).

Esta Makefile le-se do seguinte modo:


1. Usando o compilador de C gcc , e considerando a opcao de compilacao -Wall, contrua-se o
alvo main (um execut
avel) a partir dos ficheiros de objectos main.o e hello fn.o.
2. Este u
ltimos, por sua vez, s
ao gerados a partir de main.c e de hello fn.c, respectivamente.
3. O alvo clean n
ao depende de nada e simplesmente remove os ficheiros de objectos produzidos
durante a compilacao.
No passo 1 temos as regras e definicoes explcitamente criadas pelo programador, enquanto que em
1, confi
amos nas regras implcitas do make , n
ao sendo necess
ario especificar nenhuma regra. Para
usar esta Makefile basta lancar o comando make na shell, na directoria onde a Makefile reside.
Quando chamado sem argumentos, o make executa o primeiro alvo que encontrar: neste caso o alvo
main:
$ make
gcc -Wall
-c -o main.o main.c
gcc -Wall
-c -o hello_fn.o hello_fn.c
gcc
main.o hello_fn.o
-o main
$ ./main
Hello, world!
Como output, o make devolve a sequencia dos comandos que ele proprio est
a a executar. E vemos
claramente que s
ao os comandos que n
os executaramos se fossemos compilar este projecto a
` m
ao,
ficheiro a ficheiro, como o fizemos anteriormente. No final o nosso execut
avel main est
a pronto e
s
o foi preciso digitar make na linha de comandos!
Vejamos o que acontece quando editamos o main.c (Listagem 2.1), alteramos a mensagem a
imprimir, e corremos o make :

28

$ emacs main.c &


$ make
gcc -Wall
-c -o main.o main.c
gcc
main.o hello_fn.o
-o main
$ ./main
Hello, everyone!
Vemos aqui que o make s
o recompilou o main.c (a u
nica parte que necessitava de nova compilacao)
e linkou o main.o gerado com o hello fn.o que ja tinha sido compilado acima. Tudo isto sem
que tivesse sido necess
ario dizer nada alem de, simplesmente, make .
Tendo o programa pronto, j
a nos podemos libertar dos ficheiros de objectos usando o segundo
alvo da Makefile . Mas para usar qualquer alvo que n
ao seja o primeiro, e necess
ario especifica-lo
como argumento passado ao make , isto e, fazendo:
$ make clean
rm -f main.o hello_fn.o
Em geral, uma Makefile mais sofisticada ter
a varios alvos e intrincadas dependencias, bem
como definicoes de regras, etc. que saem do contexto destas notas. Remetemos os detalhes mais
avancados sobre o make e sobre Makefile s para as referencias.
O make e um programa bastante poderoso e altamente personaliz
avel de acordo com as nossas
necessidades. Convem n
ao ficar com a ideia de que s
o e u
til em contextos de programacao porque,
na verdade, pode aplicar-se nos mais variados contextos que requeiram este tipo de tarefas: criar
um produto final usando um dado comando, produto esse que depende de determinados ficheiros,
os quais ter
ao eventualmente mais dependencias encadeadas. Por exemplo, e comum usar-se uma
Makefile para criar documentos de texto em LATEX.

29

4. Criacao e gestao de uma Makefile

30

Parte II.

Interface C -FORTRAN : As Bibliotecas


SLATEC e LAPACK
por J. Lopes dos Santos

31

4.1. Rotinas em Fortran a partir de C

Resumo
Nesta parte descreve-se o modo como rotinas escritas em FORTRAN podem ser usadas por
programadores em C . O objectivo e facilitar a utilizacao de rotinas existentes, ja que as
bibliotecas cientficas s
ao quase todas escritas em FORTRAN . Os exemplos apresentados
usam rotinas da cml (Common mathematical library) do SLATEC (Sandia, Los Alamos,
Air Force Weapons Laboratory, Tecnhical Expert Committee). Os compiladores em
que os exemplos foram testados s
ao os compiladores da GNU , gcc e g77, a correr em
maquinas linux. O material deste artigo e baseado no site de Bertrand Laubsh da
Universidade do Oregon.

4.1. Rotinas em Fortran a partir de C


H
a tres aspectos a considerar para usar rotinas escritas em FORTRAN a partir de C.
Convencao de nomes
Chamada por referencia
Compilacao

4.1.1. Convenc
ao de nomes
A maior parte dos compiladores de FORTRAN, junta um undescore , , aos nomes das funcoes
ou subrotinas. Os compiladores de C, em geral, n
ao. Assim acontece com os compiladores gcc e
g77. Uma rotina com o nome
TWICE
em FORTRAN ser
a referida num programa em C como
twice

4.1.2. Chamada por refer


encia (pointers)
Em FORTRAN a passagem de argumentos para uma subrotina ou funcao e sempre feita por referencia
e n
ao por valor como em C. Assim num programa em fortran a chamada
TWICE(A)
passa `a subrotina TWICE o endereco da vari
avel A. Se nesta subrotina existir a instrucao

33

A= 2*A
a vari
avel A ter
a o seu valor alterado no programa de chamada. Isto significa que no programa
em C a funcao twice deve ter como argumento um apontador (pointer) para a vari
avel A. A
declaracao de twice seria ent
ao
double twice (double *);
e um fragmento de c
odigo possvel seria:
double twice (double *);
double b;
double a=3.0;
b = twice (&a);

Eis um exemplo completo de uma funcao em FORTRAN e de um programa em C que a chama,


adapatados do site acima referido:

11

C F i l e :
twice . f
C F u n c t i o n to be c a l l e d by a C program
C
DOUBLE PRECISION FUNCTION TWICE(X)
C Comment
C Return 2 argument
DOUBLE PRECISION X , Y
Y=2.0
TWICE=YX
RETURN
END
C THE END

Listagem 4.2: Exemplo de c


odigo FORTRAN (twice.f).

11

13

/
file :
c a l l t w i c e . c T h i s program c a l l s t w i c e ( x )
Notes :

g c c n e e d s t h e u n d e r s c o r e appended t o f u n c i o n names .

F o r t r a n ALWAYS P a s s e s r e f e r e n c e s , n o t v a l u e s . So a r g u m e n t s i n f u n c
t i o n c a l l h a v e t o be p o i n t e r s .

V i t o r M. P e r e i r a
/
#i n c l u d e <math . h>
double t w i c e ( double ) ;

34

4.2. Exemplos de utilizacao de rotinas SLATEC

15

/ argument must be p o i n t e r /

23

i n t main ( ) {
double x , y ;
x =2.0;
y=t w i c e (&x ) ;
p r i n t f ( Two t i m e s two i s %g . \ n , y ) ;
return 0;
}

25

/ end o f c a l l t w i c e /

17

19

21

Listagem 4.3: Exemplo de um programa em C que chama uma rotina de FORTRAN (calltwice.c).

4.1.3. Compilac
ao
O problema essencial da compilacao e a inclus
ao correcta das bibliotecas usadas pelos programas.
No caso acima indicado h
a duas possibilidades:
Compilar files nomef .f com g77 e compilar e linkar os ficheiros nomec .c com nomef .o e
a biblioteca matematica do C com gcc;
Compilar files nomec .c com gcc e compilar e linkar os ficheiros nomef .f com nomec .o
com o g77.
No caso da seccao anterior qualquer dos seguintes procedimentos funciona:
$ gcc -c calltwice.c
$ g77 calltwice.o twice.f

Ou,
$ g77 twice.f
$ gcc twice.o calltwice.c -lm

4.2. Exemplos de utilizac


ao de rotinas SLATEC
4.2.1. Apontadores para func
oes
Nesta seccao mostramos um exemplo completo de um programa em C que usa a rotina SLATEC,
dgaus8, que calcula um integral num intervalo finito por um algoritmo adaptativo de GaussLegendre com 8 pontos.

35

As rotinas de integracao numerica disponveis nas bibliotecas numericas usam, quase sempre,
metodos de quadratura gaussiana. Estes metodos s
ao baseados numa aproximacao do tipo
Z

dxw(x)f (x)

f (xk )wk

em que as abcissas, xk , e os pesos, wk , s


ao calculados a partir de polinomios, Pk (x), que satisfazem
uma relacao de ortogonalidade com a funcao de peso w(x)
Z

dxw(x)Pk (x)Pk (x) = 0 k 6= k .

Para escrever de raiz um programa deste tipo e necess


ario dispor de (ou calcular) uma tabela de
necess
abcissas e pesos para os varios tipos de funcoes de peso, w(x). E
ario gerar os polinomios;
encontrar as razes de Pn (x),que s
ao as abcissas de uma regra de integracao com n nodos; calcular
ainda necess
os pesos. E
ario um controlo de erros, atraves de processos recursivos de sub-divis
ao
do intervalo. Os conhecimentos tecnicos de an
alise numerica para estas tarefas s
ao apreci
aveis.
Por isso, o recurso a bibliotecas numericas, como a SLATEC, pode ser precioso. Mesmo assim,
estas rotinas exigem do utente um conhecimento dos metodos mais detalhado do que os pacotes de
software de alto nvel como o Maple ou o Mathematica. Estes tendem a esconder todos os detalhes
de implementacao do utente. S
ao de uso mais facil, mas, em contrapartida, s
ao mais lentos e
tornam mais difcil o controlo de erros.
Vejamos primeiro a documentacao desta rotina. As funcoes slatec s
ao documentadas integralmente no proprio c
odigo fonte. Reproduz-se aqui o prologo de dgaus8.f

10

12

14

16

18

20

22

24

DECK DGAUS8
SUBROUTINE DGAUS8 (FUN , A , B , ERR, ANS , IERR )
C BEGIN PROLOGUE DGAUS8
CPURPOSE I n t e g r a t e a r e a l f u n c t i o n o f one v a r i a b l e o v e r a f i n i t e
C
i n t e r v a l u s i n g an a d a p t i v e 8 p o i n t L e g e n d r e Gauss
C
algorithm .
Intended p r i m a r i l y f o r high accuracy
C
i n t e g r a t i o n o r i n t e g r a t i o n o f smooth f u n c t i o n s .
C LIBRARY
SLATEC
CCATEGORY H2A1A1
CTYPE
DOUBLE PRECISION (GAUS8S , DGAUS8D)
CKEYWORDS ADAPTIVE QUADRATURE, AUTOMATIC INTEGRATOR ,
C
GAUSS QUADRATURE, NUMERICAL INTEGRATION
CAUTHOR Jones , R . E . , (SNLA)
C DESCRIPTION
C
C
A b s t r a c t a DOUBLE PRECISION r o u t i n e
C
DGAUS8 i n t e g r a t e s r e a l f u n c t i o n s o f one v a r i a b l e o v e r f i n i t e
C
i n t e r v a l s u s i n g an a d a p t i v e 8 p o i n t L e g e n d r e Gauss a l g o r i t h m .
C
DGAUS8 i s i n t e n d e d p r i m a r i l y f o r h i g h a c c u r a c y i n t e g r a t i o n
C
o r i n t e g r a t i o n o f smooth f u n c t i o n s .
C
C
The maximum number o f s i g n i f i c a n t d i g i t s o b t a i n a b l e i n ANS
C
i s t h e s m a l l e r o f 18 and t h e number o f d i g i t s c a r r i e d i n
C
double p r e c i s i o n a r i t h m e t i c .
C

36

4.2. Exemplos de utilizacao de rotinas SLATEC

26

28

30

32

34

36

38

40

42

44

46

48

50

52

54

56

58

60

62

64

66

68

70

72

74

76

78

C
D e s c r i p t i o n o f Arguments
C
C
I n p u t FUN , A , B , ERR a r e DOUBLE PRECISION
C
FUN name o f e x t e r n a l f u n c t i o n to be i n t e g r a t e d .
T h i s name
C
must be i n an EXTERNAL s t a t e m e n t i n t h e c a l l i n g program .
C
FUN must be a DOUBLE PRECISION f u n c t i o n o f one DOUBLE
C
PRECISION argument . The v a l u e o f t h e argument to FUN
C
i s t h e v a r i a b l e o f i n t e g r a t i o n w h i c h r a n g e s from A to B .
C
A
lower l i m i t of i n t e g r a t i o n
C
B
u p p e r l i m i t o f i n t e g r a t i o n ( may be l e s s t h a n A)
C
ERR i s a r e q u e s t e d p s e u d o r e l a t i v e e r r o r t o l e r a n c e .
Normally
C
p i c k a v a l u e o f ABS(ERR) s o t h a t DTOL . LT . ABS(ERR) . LE .
C
1 . 0 D3 where DTOL i s t h e l a r g e r o f 1 . 0 D18 and t h e
C
double p r e c i s i o n u n i t r o u n d o f f D1MACH ( 4 ) . ANS w i l l
C
n o r m a l l y h a v e no more e r r o r t h a n ABS(ERR) t i m e s t h e
C
i n t e g r a l o f t h e a b s o l u t e v a l u e o f FUN(X ) .
Usually ,
C
s m a l l e r v a l u e s o f ERR y i e l d more a c c u r a c y and r e q u i r e
C
more f u n c t i o n e v a l u a t i o n s .
C
C
A n e g a t i v e v a l u e f o r ERR c a u s e s an e s t i m a t e o f t h e
C
a b s o l u t e e r r o r i n ANS to be r e t u r n e d i n ERR . Note t h a t
C
ERR must be a v a r i a b l e ( n o t a c o n s t a n t ) i n t h i s c a s e .
C
Note a l s o t h a t t h e u s e r must r e s e t t h e v a l u e o f ERR
C
b e f o r e making any more c a l l s t h a t us e t h e v a r i a b l e ERR .
C
C
Output ERR, ANS a r e double p r e c i s i o n
C
ERR w i l l be an e s t i m a t e o f t h e a b s o l u t e e r r o r i n ANS i f t h e
C
i n p u t v a l u e o f ERR was n e g a t i v e .
(ERR i s unchanged i f
C
t h e i n p u t v a l u e o f ERR was nonn e g a t i v e . ) The e s t i m a t e d
C
e r r o r i s s o l e l y f o r i n f o r m a t i o n to t h e u s e r and s h o u l d
C
n o t be u s e d a s a c o r r e c t i o n to t h e computed i n t e g r a l .
C
ANS computed v a l u e o f i n t e g r a l
C
IERR a s t a t u s c o d e
C
Normal c o d e s
C
1 ANS most l i k e l y meets r e q u e s t e d e r r o r t o l e r a n c e ,
C
o r A=B .
C
1 A and B a r e t o o n e a r l y e q u a l to a l l o w n o r m a l
C
i n t e g r a t i o n . ANS i s s e t to z e r o .
C
Abnormal c o d e
C
2 ANS p r o b a b l y d o e s n o t meet r e q u e s t e d e r r o r t o l e r a n c e .
C
CREFERENCES (NONE)
CROUTINES CALLED D1MACH, I1MACH , XERMSG
C REVISION HISTORY (YYMMDD)
C
810223 DATE WRITTEN
C
890531 Changed a l l s p e c i f i c i n t r i n s i c s to g e n e r i c .
(WRB)
C
890911 Removed u n n e c e s s a r y i n t r i n s i c s .
(WRB)
C
890911 REVISION DATE from V e r s i o n 3 . 2
C
891214 P r o l o g u e c o n v e r t e d to V e r s i o n 4 . 0 format .
(BAB)
C
900315 CALLs to XERROR changed to CALLs to XERMSG .
(THJ)
C
900326 Removed d u p l i c a t e i n f o r m a t i o n from DESCRIPTION s e c t i o n .
C
(WRB)
CEND PROLOGUE DGAUS8

37

Listagem 4.4: Pr
ologo da rotina DGAUS8 do SLATEC (dgaus8.f).

Se o pacote slatec4linux.tgz (ver apendice 6) estiver correctamente instalado, este prologo pode
ser visto usando o comando man do unix, man dgaus8. A biblioteca SLATEC tem em qpdoc uma
introducao a varias rotinas de integracao. Para a ver basta executar o comando man qpdoc.
Olhemos agora para um programa em C que chama esta rotina:
/
Example o f c a l l o f a s l a t e c r o u t i n e t h a t
t a k e s a f u n c t i o n name a s argument
J .M. B . L o p e s d o s S a n t o s
F i s i c a C o m p u t a c i o n a l Maio 2001

/
#i n c l u d e < s t d i o . h>
#i n c l u d e <math . h>

10

14

t y p e d e f double d d f o r t r a n ( double ) ; / d d f o r t r a n d e c l a r e s a f u n c t i o n
r e tur ni ng double
taking a p oin t er to double
a s argument /

16

/ Here i s t h e s l a t e c r o u t i n e .

18

v o i d d g a u s 8 ( d d f o r t r a n , double , double , double , double , i n t ) ;

12

20

22

24

26

28

30

32

main ( )
{
d d f o r t r a n pfunc ;
/ p f u n c i s a p o i n t e r t o a d d f o r t r a n f u n c t i o n /
double f u n c ( double ) ;
/ i n t e g r a n d
/
double a = 0 . 0 ;
/ i n t e g r a l l i m i t s /
double b = 1 . 0 ;
double e r r o =1.E7;
/ t o l e r a t e d e r r o r /
double i n t e g ;
/ t o s t o r e i n t e g r a l v a l u e /
int I e rr ;
/ f l a g f o r e r r o r c o n d i t i o n /
p f u n c =&f u n c ;
/ p f u n c p o i n t s t o f u n c /
d g a u s 8 ( p f u n c , &a , &b , &e r r o , &i n t e g , & I e r r ) ; / t h e
s l a t e c r o u t i n e /
p r i n t f ( %e \ t %e \n , i n t e g , e r r o ) ;
}

38

double f u n c ( double x )
{
double y = x ;
return yy ;
}

40

34

36

Check d g a u s 8 d o c u m e n t a t i o n /

/ t h e i n t e g r a n d /

End d g a u s 8 . c To c o m p i l e s i m p l y do :
g c c c
dgaus8 . c

42

38

4.3. Documentacao da Biblioteca SLATEC

g77 d g a u s 8 . o l s l a t e c l l a p a c k
44

Listagem 4.5: Exemplo de programa em C que chama a rotina DGAUS8 do SLATEC (calldgaus8.c).
A funcao dgaus8 tem como primeiro argumento o nome de uma funcao. Em C teremos que passar
um apontador para uma funcao. A declaracao
typedef double ddfortran(double *);

define um novo tipo, ddfortran, que corresponde a uma funcao de dupla precisao com argumento
que e um apontador para vari
avel double. A instrucao
ddfortran *pfunc;

define pfunc como apontador para uma funcao e com


pfunc =&func;

pfunc fica a apontar para a funcao func. pfunc e o primeiro argumento de dgaus8. Os restantes
argumentos s
ao apontadores para diferentes vari
aveis, tal como foi descrito no exemplo anterior.
Para compilar este exemplo podemos compilar o programa em C
$ gcc -c dgaus8.c
e depois link
a-lo com a biblioteca SLATEC (que pode necessitar da LAPACK).
$ g77 dgaus8.o -lslatec -llapack
Eventualmente, podemos ter que indicar o PATH da biblioteca libslatec. Por exemplo, se estiver
instalada em /usr/local/lib
$ g77 dgaus8.o -L/usr/local/lib -lslatec -llapack

4.3. Documentac
ao da Biblioteca SLATEC
A instalacao da biblioteca SLATEC exige dois pacotes, slatec4linux.tgz e slatec src.tgz, ambos disponveis no site da netlib [1]. As instrucoes para instalacao est
ao no apendice 6. A documentacao fica instalada em man pages. O comando

39

$ man nome de rotina


mostra a documentacao da rotina cujo nome for o indicado. No site netlib existe um ficheiro toc
com uma listagem das mais de 1400 rotinas deta biblioteca.
Estando online, um recurso extremamente u
til encontra-se em http://gams.nist.gov um ndice
e repositorio de software matematico e estatstico, com excelentes facilidades de pesquisa e optima
documentacao. A podemos encontar informacao sobre bibliotecas de distribuicao gratuita e comerciais para virtualmente todos os problemas de c
alculo cientfico.

4.4. Enderecos
uteis
[1] http://www.netlib.org Reposit
orio de software para c
alculo cientfico.
[2] http://gams.nist.gov indce cruzado e repositorio de software matematico e estatstico.
Excelente facilidades de procura. Estruturado com a classificacao gams (Guide to availbale
mathematical software).
[3] http://www.physics.orst.edu/bertrand/C slatec html/begin.html Um site com um
tutorial de utilizacao da biblioteca slatec a partir de C.

40

Parte III.

A Biblioteca gsl
por Eduardo Castro e Vitor M. Pereira (em Construc
ao)

41

5. T
opicos preliminares
5.1. O que
e a gsl
A GNU Scientific Library (gsl ) reune um conjunto de rotinas para c
alculo numerico a maior
parte delas bastante testadas ao longo de muitos anos que foram re-escritas de raz em C , e com
especial preocupacao na interface entre a biblioteca e o programa/programador, garantindo assim
a sua utilizacao n
ao s
o em ambiente C , mas em muitas outras linguagens de alto nvel (o Python,
por exemplo).
A biblioteca abrange um vasto n
umero de t
opicos em an
alise numerica, tendo disponveis rotinas
nas seguintes `
areas:
Complex Numbers
Roots of Polynomials
Special Functions
Vectors and Matrices
Permutations
Combinations
Sorting
BLAS Support
Linear Algebra
CBLAS Library
Fast Fourier Transforms
Eigensystems

Random Numbers
Quadrature
Random Distributions
Quasi-Random Sequences
Histograms
Statistics
Monte Carlo Integration
N-Tuples
Differential Equations
Simulated Annealing
Numerical Differentiation
Interpolation

Series Acceleration
Chebyshev Approximations
Root-Finding
Discrete Hankel Transforms
Least-Squares Fitting
Minimization
IEEE Floating-Point
Physical Constants
Wavelets

O manual da gsl (info gsl) est


a organizado justamente de acordo com cada um destes temas
contendo varios exemplos de utilizacao.

5.2. Utilizac
ao b
asica
Comecemos por um exemplo bastante simples. Uma das seccoes da gsl fornece verias ferramentas
relativas a funcoes especiais. Consideremos o c
odigo seguinte que calcula a funcao de Bessel J0 (x)
para x = 5:

/
F i l e g s l b e s s e l . c
D emonstracao de u t i l i z a c a o da g s l

43

5. Topicos preliminares

V i t o r M. P e r e i r a
/
#i n c l u d e < s t d i o . h>
#i n c l u d e < g s l / g s l s f b e s s e l . h>

10

12

14

16

i n t main
{
double
double
printf
return
}

( void )
x = 5.0;
y = gsl sf bessel J0 (x );
( J0(%g ) = %.18 e \n , x , y ) ;
0;

Listagem 5.1: Utilizacao b


asica da gsl (gsl-bessel.c)

A instrucao #include <gsl/gsl sf bessel.h> trata de carregar o header da gsl onde as funcoes
de Bessel est
ao declaradas. Neste #include foi passado, n
ao s
o o nome do header, como tambem
uma parte do seu caminho. Isto acontece porque, no meu sistema, os headers da gsl encontram-se
todos no direct
orio /usr/include/gsl/ e, logo, como o gcc em princpio s
o procura headers em
/usr/include/, e necess
ario dar o resto do caminho.
A funcao gsl sf bessel J0() e uma das funcoes oferecidas pela gsl , e o seu prot
otipo e
simplesmente1
double gsl_sf_bessel_J0 (double X)
Para compilar este programa fazemos:
$ gcc -Wall gsl-bessel.c -lgsl -lgslcblas -o gsl-bessel

(5.1)

Esta instrucao compila o nosso programa gsl-bessel.c e procede `a linkagem com a gsl . Como
ja sabemos desde a seccao 3, a opcao -lgsl pede ao compilador que faca o link com uma biblioteca
chamada libgsl.a (ou libgsl.so). Numa instalacao convencional, ela estar
a em /usr/lib/.
Caso contr
ario, pode ser necess
ario acrescentar a opcao -Lcaminho com o caminho para onde esta
biblioteca estiver instalada. Mas vemos que existe outra biblioteca, chamada libgslblas.a, a ser
linkada com o nosso programa. Bom, na verdade n
ao e com o nosso programa mas sim com a gsl .
Ou seja, a libgsl.a depende desta outra biblioteca gslcblas que proporciona um conjunto de
rotinas de
algebra linear usadas pela gsl 2 , da que esta tenha de entrar tambem na fase do link.
Este nosso programa devolve ent
ao o resultado
$

./gsl-bessel
J0(5) = -1.775967713143382920e-01

(5.2)
(5.3)

, para esta funcao particular.


1 Consulte-se

o manual da gsl em info gsl Special Functions Bessel Functions Regular Cylindrical
Bessel Functions.
2 Basicamente, a gslcblas
e a conhecida BLAS re-escrita para a gsl

44

5.3. Geradores de N
umeros Aleat
orios

5.3. Geradores de N
umeros Aleat
orios
A gsl fornece uma extensa coleccao de geradores de n
umeros pseudo-aleatorios os quais podem ser
acedidos de uma forma universal, incluindo a escolha do gerador durante o tempo de execucao, o
que permite mudar de gerador facilmente sem ter de recompilar o programa. As funcoes nessarias
para este recurso s
ao definidas no header gsl rng.h. Esta generalidade tem um preco que, neste
caso, e o processo de inicializacao e definicao do gerador de n
umeros aleat
orios. Mais do que nos
outros, neste caso e melhor ver um exemplo:

/
F i l e g s l random . c
Exemplo de u t i l i z a c a o de g e r a d o r de numeros a l e a t o r i o s e n t r e [ 0 , 1 [

V i t o r M. P e r e i r a
/

11

13

#i n c l u d e < s t d i o . h>
#i n c l u d e < g s l / g s l r n g . h>
i n t main ( v o i d )
{
c o n s t g s l r n g t y p e T ;
gsl rng r ;

// Tipo de g e r a d o r
// P o i n t e r p a r a novo g e r a d o r

15

17

int i , n = 10;
double u ;

21

// Le a s v a r i a v e i s de a m b i e n t e GSL RNG TYPE e GSL RNG SEED , s e


// d e f i n i d a s , e u s a o s s e u s v a l o r e s p a r a d e f i n i r o g e r a d o r e
// s e m e n t e a u s a r :

23

gsl rng env setup ();

19

25

T = gsl rng default ;


r = g s l r n g a l l o c (T ) ;

// D e f i n e o t i p o de g e r a d o r a u s a r
// A l o c a memoria p a r a o g e r a d o r

27

// I m p r i m e o t i p o de g e r a d o r u s a d o :
29

p r i n t f ( E i s 10 numeros g e r a d o s p e l o g e r a d o r % s \ n , g s l r n g n a m e ( r ) ) ;
31

// C a l c u l a e i m p r i m e 10 numeros a l e a t o r i o s :
33

35

f o r ( i = 0 ; i < n ; i ++)
{
u = gsl rng uniform ( r );

37

// D e v o l v e numero a l e a t o r i o e n t r e
// [ 0 , 1 [ u s a n d o o g e r a d o r r

p r i n t f ( %.5 f \n , u ) ;
39

41

// L i b e r t a a memoria a s s o c i a d a ao g e r a d o r

43

gsl rng free ( r );

45

5. Topicos preliminares

return 0;

45

Listagem 5.2: N
umeros aleat
orios com a gsl (gsl-random.c)

Este c
odigo pode ser compilado com
$ gcc -Wall gsl-random.c -lgsl -lgslcblas -o gsl-random

(5.4)

e executado, obtendo-se:
$ ./gsl-random
Eis 10 numeros gerados pelo gerador mt19937
0.99974
0.16291
0.28262
0.94720
0.23166
0.48497
0.95748
0.74431
0.54004
0.73995
Esta sequencia de n
umeros foi gerada pelo gerador gsl rng mt19937, aquele que e escolhido por
omissao quando se definiu T = gsl rng default;. Claro que poderamos ter usado um outro
qualquer, entre os muitos que a gsl disponibiliza. Por exemplo, se quisessemos usar o gerador
odigo fonte.
ranlux bastaria, em vez de T = gsl rng default;, definir T = gsl rng ranlux; no c
Mas a gsl faz melhor do que isso! Ao usar T = gsl rng default; podemos escolher o gerador
durante a execucao, sem alterar o c
odigo fonte ne recompila-lo. Isso e feito por intermedio de
uma vari
avel de ambiente chamada GSL RNG TYPE que, pode ser definida pelo utilizador antes de
executar o programa. Esta vari
avel deve conter o gerador de n
umeros aleat
orios a usar. Por
exemplo o comando seguinte faz isso:
$ GSL_RNG_TYPE="ranlux" ./gsl-random
Eis 10 numeros gerados pelo gerador ranlux
0.53982
0.76155
0.06030
0.79600
0.30631
0.08278
0.66542
0.46075
0.92574
0.61915

46

5.4. Funcoes como argumentos

Vemos que o gerador mudou (e, logo, muda a sequencia tambem), sem ser necess
ario tocar no
c
odigo fonte do programa. Do mesmo modo, e possvel escolher a semente durante o tempo de
execucao atraves da vari
avel de ambiente GSL RNG SEED. Por exemplo:
$ GSL_RNG_SEED=12345 ./gsl-random
Eis 10 numeros gerados pelo gerador mt19937
0.92962
0.89015
0.31638
0.13071
0.18392
0.03976
0.20456
0.82644
0.56773
0.53208
gera uma sequencia obviamente diferente da gerada acima com o mt19937.

5.4. Funco
es como argumentos
No uso de bibliotecas numericas cujas rotinas realizam tarefas como procurar um mnimo, determinar uma raz ou calcular um integral e frequente receberem como argumentos funcoes. Vejamos
um exemplo. A seguinte funcao tem dois argumentos. O segundo e uma vari
avel double. O
primeiro e uma funcao de argumento double que devolve double tambem.

double iterate function(double func(double), double x)


{
return func(func(x));
}
A sua declaracao poderia tomar a seguinte forma:

double iterate function(double func(double), double x);


A seguinte chamada devolveria o valor f(f(x))

iterate function(f, x)
Em C o nome de uma funcao e, de facto, um apontador para a funcao. Isso significa que a definicao
de iterate function tambem poderia ser feita como se segue:

47

5. Topicos preliminares

double iterate function(double (*func)(double), double x)


{
return (*func)((*func)(x));
}
uma vez que (*func) e a propria funcao para onde aponta o apontador func. Os parentesis s
ao
necess
arios pois enquanto

double (*func)(double x);


declara uma funcao double de argumento double, a declaracao

double *func(double x);


afirma que func e uma funcao de argumento double que devolve um apontador para double.
A declaracao de iterate function pode ser feita omitindo o nome do par
ametro formal, como
fizemos para o segundo argumento

double iterate funcion(double (*)(double), double);


A chamada da funcao pode tomar as duas formas

iterate function(f,x);
/* forma 1*/
iterate function (&f,x);
/* forma 2 */

5.5. Funco
es de n
umero de argumentos vari
avel
O mecanismo do par
agrafo anterior tem uma defeito grave. Suponhamos que queriamos iterar uma
funcao com dois argumentos:

double f(double x, double y);


Neste caso a funcao que definimos iterate function n
ao pode ser usada pois o seu primeiro
argumento n
ao e compatvel com esta declaracao. Os autores da biblioteca GSL resolvem este
problema recorrendo a uma definicao um pouco mais complicada das funcoes, mas que permite
usar as rotinas, sem alteracoes, para funcoes com qualquer n
umero de argumentos. Esta definicao
usa o conceito de estrutura, struct, e se o leitor n
ao est
a familiarizado com o seu uso aconselha-se
a leitura do captulo 9 de [7].

48

5.5. Funcoes de n
umero de argumentos vari
avel

Este conceito permite construir tipos derivados de dados mais complexos que os nativos do C, e
constitui uma ferramenta essencial da linguagem em qualquer tarefa menos trivial de programacao.
Quando realizamos uma tarefa como procurar uma raz ou integrar a funcao singularizamos
um dos seus argumentos e tratamos os outros como par
ametros. Na biblioteca GSL usamos uma
struct (estrutura) com dois elementos. O primeiro elemento desta estrutura e a funcao, que tem
dois argumentos:
double (*function)(double x, void *params)
Note-se que o segundo argumento desta funcao e um apontador para void. Na definicao concreta
de uma funcao este apontador apontar
a para a lista de par
ametros. Por isso o seu tipo e void, o
que permitir
a sem conflitos fazer um cast (convers
ao explcita3 ) e apont
a-lo para qualquer tipo
de vari
avel. O segundo elemento da estrutura e precisamente este apontador, ao qual teremos que
aceder. Assim a construcao do tipo gsl function fica:
struct gsl function struct{
double (*function)(double x, void *params);
void *params;
};
typedef struct gsl function struct gsl function;
O uso de typedef permite simplificar declaracoes posteriores. Por exemplo:
gsl function F, *FF;
declara F como sendo uma estrutura acima descrita e FF um apontador para uma tal estrutura.
Vejamos agora como podemos definir uma instancia de uma funcao gsl function.
Eis o c
odigo para definir uma funcao de uma vari
avel e dois par
ametros:
/* Template para par de parametros */
struct pair{
double a1, a2;
};
/*
Funcao de uma variavel, com dois parametros
Note-se o template generico usado em gsl function
*/
double f(double x, void *params);
3 Se

i
e uma vari
avel do tipo int, ent
ao
(double) i

converte o valor de i fazendo com que a express


ao seja do tipo double. No entanto a vari
avel i permanece
inalterada.

49

5. Topicos preliminares

double f(double x, void * params)


{
struct pair *p = (struct pair *) params;
/* lado direita ha um cast */
double a = (p -> a1);
double b = (p -> a2);
return a*exp(-b*x);
}
Escolhemos agrupar os dois par
ametros num par. A declaracao da funcao segue o formato usado em
gsl function. S
o assim poderemos usar esta funcao como um dos membros da estrutura (struct)
odigo da funcao e inicializado o apontador p como
que e uma gsl function. Na primeira linha do c
apontador para pair, e passa a apontar para a mesma vari
avel que o argumento da funcao, params.
Como este e um apontador para void e feito um cast. Vemos aqui a raz
ao de usar um apontador
para void em gsl funcion. Qualquer cast e legtimo. As linhas seguintes inicializam os dois
par
ametros tempor
arios com os valores para que aponta o argumento params. Uma chamada a
esta funcao poderia ter a forma:

double x = 1.;
/* Decalracao e inicializacao da estrutura pd */
struct pair pd = {0.5, 1.0};
void *pp = &pd;
f1 = f(x, pp);

5.6. A func
ao gsl function
Tendo visto como podemos incluir numa u
nica declaracao funcoes com n
umero arbitrario de
par
ametros vejamos agora como inicializar uma gsl function.

/* F e uma gsl function e


FF um apontador para uma gsl function */
gsl function F, *FF;
/* f e pd definidos previamente */
F.function = &f;
F.params = &pd;
/*
FF aponta para F.
E mais eficiente passar apontadores
do que estruturas complexas
*/
FF = &F;

Como calcular o valor de uma gsl function? Eis dois processos equivalentes, um usando F e o
outro FF. Recorde-se que os membros de uma struct gsl function s
ao function e params.

50

5.6. A funcao gsl function

f2 = (F.function)(x, (F.params));
f3 = (FF -> function)(x, (FF -> params));
A biblioteca GSL define uma macro com dois argumentos GSL FN EVAL(F,x) em que F e um apontador para uma gsl function e x o valor da vari
avel onde a funcao deve ser calculada. A macro
e:
#define GSL FN EVAL(F,x) (*((F) -> fuction))(x,(F) -> params)
Note-se que esta u
ltima express
ao e perfeitamente equivalente `a usada na atribuicao de f3.

51

5. Topicos preliminares

52

6. Exemplos
6.1. Minimizac
ao de Funco
es
Para ilustrar o uso desta biblioteca vamos agora apresentar um exemplo completo. Primeiro o
problema.

6.1.1. Funcional de energia livre


Na aproximacao de campo medio o estudo de um ferromagneto resume-se `a determinacao dos
mnimos de um funcional de energia livre relativamente ao par
ametro de ordem m, a magnetizacao
[8]. Alem de m, o funcional depende da temperatura (T ) e do campo (h). Tem a forma, em
unidades apropriadas,
1
f (m, T, h) = m2 hm T s(m)
(6.1)
2
com a entropia s(m) dada por




1m
1+m
1+m
1m

s(m) =
log
log
2
2
2
2
Eis o c
odigo destas funcoes. Note-se que usamos a forma da declaracao das funcoes gsl para o
funcional de campo medio:

73

75

77

79

81

83

double e n t r o p y ( double m)
{
i f ( f a b s (m) == 1 . )
return 0 . ;
else
r e t u r n 0.5(1.+m) l o g ( 0 . 5 ( 1 . +m)) 0.5(1. m) l o g ( 0 . 5 ( 1 . m) ) ;
}
double f m e a n f i e l d ( double m, v o i d params )
{
double temp , f i e l d ;
s t r u c t p a i r p = ( s t r u c t p a i r ) params ;

85

87

temp = ( p > temp ) ;


f i e l d = ( p > f i e l d ) ;
r e t u r n 0.5mm f i e l d m temp e n t r o p y (m) ;

53

6. Exemplos

0.8

2.0
1.8
1.6
1.4
1.2
1.0
0.8
0.6
0.4
0.2
0.0

0.6

f(m)

0.4

0.2

-0.2

-0.4
-1

-0.5

0.5

Figura 6.1.: Funcional de energia livre em funcao da magnetizacao, para diferentes temperaturas
em campo nulo (h = 0).

89

Listagem 6.1: Definicao da entropia


(minimization.c).

energia

livre

para

uma

dada

magnetizacao

As funcoes de minimizacao da biblioteca usarao como argumento um apontador para uma gsl function,
aqui designada por F:
gsl function F;
F.function = &f mean field;
F.params = &pd;
Antes de prosseguir com a construcao do programa para minimizar o funcional de energia livre
convem ter uma imagem do seu comportamento. Consideremos o caso h = 0. Na fig. 6.1 est
a
representado o funcional de energia livre, subtrado do seu valor a magnetizacao nula f (m, T, 0)
f (0, T, 0), em funcao de m para varios valores de T .

6.1.2. Inicializac
ao

#i n c l u d e < s t d i o . h>
#i n c l u d e < s t d l i b . h>

54

6.1. Minimizacao de Funcoes

11

13

#i n c l u d e <math . h>
#i n c l u d e < g s l / g s l e r r n o . h>
#i n c l u d e < g s l / g s l m i n . h>
#d e f i n e MAX ITER 10000
#d e f i n e TMIN 0 . 1
#d e f i n e TMAX 1 . 5
struct pair
{
double temp , f i e l d ;
};

15

17

19

21

23

25

27

29

double e n t r o p y ( double m) ;
double f m e a n f i e l d ( double m, v o i d params ) ;
i n t main ( )
{
int i , i t e r = 0 , status ;
c o n s t g s l m i n f m i n i m i z e r t y p e T ;
gsl min fminimizer s ;
double m = 0 . , temp ;
double a = 1. , b = 1 . ;
s t r u c t p a i r pd ;
gsl function F;
F . f u n c t i o n = &f m e a n f i e l d ;
F . params = &pd ;

31

33

T = gsl min fminimizer brent ;


s = g s l m i n f m i n i m i z e r a l l o c (T ) ;

35

pd . f i e l d = 0 . ;

37

39

41

43

45

47

49

51

53

55

f o r ( i = 0 ; i <= 1 0 0 ; ++i )
{
temp = TMIN + i (TMAX TMIN ) / 1 0 0 . ;
i f ( temp >= 1 . )
m = 0.;
else
m = 1 . 1 . e 8;
a = 1.;
b = 1.;
pd . temp = temp ;
g s l m i n f m i n i m i z e r s e t ( s , &F , m, a , b ) ;
do
{
++i t e r ;
status = gsl min fminimizer iterate ( s );
m = gsl min fminimizer x minimum ( s );
a = gsl min fminimizer x lower ( s );
b = gsl min fminimizer x upper ( s );

55

6. Exemplos

1.0

0.8

m(T)

0.6

0.4

0.2

0.0
0.0

1.0

0.5

1.5

Figura 6.2.: Magnetizacao m do sistema em funcao da temperatura T , em campo nulo (h = 0),


obtida por minimizacao do funcional de energia livre f (m, T ) usando as funcoes de
minimizacao da biblioteca GSL.
s t a t u s = g s l m i n t e s t i n t e r v a l ( a , b , 1 . e 4 , 0 . 0 ) ;
}
w h i l e ( s t a t u s == GSL CONTINUE && i t e r < MAX ITER ) ;
// p r i n t f (%d\n , i t e r ) ;
i f ( i t e r == MAX ITER )
{
p r i n t f ( F o i a t i n g i d o o numero maximo de i t e r a c o e s \n ) ;
exit (1);
}
p r i n t f ( %f \ t%f \n , pd . temp , m) ;

57

59

61

63

65

}
gsl min fminimizer free (s );

67

69

return 0;
71

Listagem 6.2: Programa principal (minimization.c).

56

Parte IV.

Outros T
opicos
por Vtor M. Pereira

57

7. Argumentos ao main
7.1. argv e argc
Em computacao e muito frequente a situacao em que onde a execucao de um programa depende
de determinados par
ametros que dever
ao ser passados, por exemplo, pelo utilizador. Um exemplo
classico e quando se espera que o programa escreva algo para determinado ficheiro, cujo nome deve
ser definido durante a execucao. Uma das formas de implementar esse tipo de necessidade consiste
em usar os recursos de leitura do C , como sejam, as funcoes scanf(), getchar(), etc. Uma outra
forma de o fazer de modo bastante mais pratico e passar esses par
ametros como argumentos ao
main()1 .
Claro que, como o main() e chamado pelo sistema operativo, a u
nica forma de lhe passar
argumentos e atraves do sistema operativo, ou, na pratica, da shell. Passar um argumento ao
main() de um programa cujo execut
avel se chama prog e t
ao simples como
$ ./prog arg1 arg2 arg3

(7.1)

. No gcc , por exemplo, todas as opcoes de compilacao e ficheiros de input s


ao argumentos que a
shell passa ao main() do gcc .
Para usar esta funcionalidade, necess
ario declarar o main() com uma instrucao do tipo:
int main(int argc, char *argv[]);
, em vez do habitual int main(void); . Deste modo, ficamos com o main() a admitir dois
argumentos:
argc (argument count ) e um inteiro contendo o n
umero de argumentos passados ao programa;
argv (argument vector ) e um array de apontadores para string, contendo todos os argumentos
pela mesma ordem que foram passados.
Estes argumentos s
ao automaticamente inicializados pelo sistema de modo a que, quando o programa arranca se verificam as seguintes condicoes:
argc e igual ou maior a um;
1 Apesar

de ter um estatuto especial dentro do programa, o main


e uma funca
o de C , equivalente a qualquer outra.
Em particular, tamb
em admite argumentos um n
umero var
avel deles, mais ou menos como o printf().

59

7. Argumentos ao main

argv[argc] e o apontador NULL;


argv[0] a argv[argc-1] s
ao apontadores para strings;
argv[0] e uma string contendo o nome do execut
avel do programa.
O primeiro elemento de argv e, portanto, especial e, alem disso, argc vai ser sempre igual ao
n
umero de argumentos passados ao programa, mais um. Vejamos um exemplo de um programa
cuja u
nica tarefa e escrever todos os argumentos passados ao main():

/
F i l e show args . c
I m p r i m e t o d o s o s a r g u m e n t o s p a s s a d o s na l i n h a de comando

V i t o r M. P e r e i r a
/
#i n c l u d e < s t d i o . h>
#i n c l u d e < s t d l i b . h>

10

12

14

16

i n t main ( i n t a r g c , c h a r a r g v )
{
int i ;
f o r ( i =0; i <a r g c ; ++i )
p r i n t f ( a r g v [%d ] = %s \n , i , a r g v [ i ] ) ;
e x i t ( EXIT SUCCESS ) ;
}

Listagem 7.1: Argumentos ao main() (show args.c).

Este programa funciona de uma forma muito simples:


$ ./show_args primeiro 20 terceiro 4 penultimo 6
argv[0] = ./show_args
argv[1] = primeiro
argv[2] = 20
argv[3] = terceiro
argv[4] = 4
argv[5] = penultimo
argv[6] = 6
, e l
a est
a: o argv[0] contem o nome do nosso execut
avel, e os restantes elementos os argumentos
propriamente ditos.
importante sublinhar que os argumentos s
E
ao guardados em argv[] como strings. Isto significa
que, se quisermos usar o seu valor numerico, n
ao e possvel faze-lo directamente, sendo necess
ario
recorrer `as funcoes de convers
ao de string para inteiro (atoi()), double (atof()), etc2 . Um
2 Para

60

mais detalhes destas e outras funco


es relevantes neste contexto consultar o manual de stdlib.h e string.h

7.2. Vari
aveis de ambiente

exemplo de uma aplicacao que usa os valores numericos dos argumentos e dado pelo problema 4
da folha de problemas Exerccios de linguagem C :

/
F i s i c a Computacional
E x e r c i c i o s de Linguagem C
V i t o r M. P e r e i r a ( Fev 2 0 0 6 )

/

/
4 . Imprima a soma de d o i s numeros p a s s a d o s na l i n h a de comandos .
Exemplo :
. / soma 2 3
2+3=5

11

13

15

17

19

21

/
#i n c l u d e < s t d i o . h>
#i n c l u d e < s t d l i b . h>
#i n c l u d e < a s s e r t . h>
i n t main ( i n t a r g c , c h a r a r g v [ ] )
{
int i i ;
double sum = 0 . 0 ;

23

/ So v a l e a pena p r o s s e g u i r s e h o u v e r p e l o menos 2 t e r m o s : /
25

a s s e r t ( argc > 2);


27

p r i n t f ( \ n Here s y o u r r e s u l t : \ n\n ) ;
29

f o r ( i i = 1 ; i i < a r g c ; ++ i i ) {
sum += a t o f ( a r g v [ i i ] ) ;
p r i n t f ( %g + , a t o f ( a r g v [ i i ] ) ) ;
}

31

33

p r i n t f ( \ b\b = %g \n , sum ) ;
return 0;

35

37

Listagem 7.2: Problema 4 da folha Exerccios de linguagem C (show args.c).

7.2. Vari
aveis de ambiente
Finalmente, existe tambem com frequencia a necessidade de comunicar com um programa de
forma semi-permantente, sem ter o utilizador de estar sempre a definir o input. Claro que se
pode sempre criar um ficheiro de configuracao que ser
a lida pelo programa, e esse e o modo mais

61

7. Argumentos ao main

adequado de proceder sempre que a quantidade de informacao a passar ao programa e grande.


Para situacoes intermedias, as vari
aveis de ambiente proporcionam mais uma solucao pratica. As
vari
aves de ambiente s
ao constantemente usadas por varias aplicacoes para acederem, por exemplo,
ao direct
orio actual, ou ao login do utilizador.
A utilizacao desta funcionalidade e totalmente an
aloga `a discutida acima para o caso de argc e
argv[], sendo que neste caso, a declaracao do main() devera ser algo do tipo:
int main (int argc, char *argv[], char *envp[])
, onde o terceiro argumento, envp[], e um array de apontadores para string. Cada elemento conter
a
a string de uma dada vari
avel de ambiente, na forma NOME=Valor. Esta forma e u
til3 quando n
ao
conhecemos as vari
aveis de ambiente disponveis. Mas se, pelo contrario, quisermos aceder, definir
ou alterar o valor de uma vari
avel em particular, ent
ao o modo mais expedito consiste em usar as
funcoes getenv(), putenv(), setenv(), etc. proporcionadas pelo stdlib.h.

/
F ile environ . c
D emonstracao da u t i l i z a c a o de v a r i a v e i s de a m b i e n t e

V i t o r M. P e r e i r a
/
#i n c l u d e < s t d i o . h>
#i n c l u d e < s t d l i b . h>

10

12

i n t main ( i n t a r g c , c h a r a r g v [ ] , c h a r envp [ ] )
{
c h a r u s e r , home , h o s t ;

14

p r i n t f ( T e r c e i r a v a r i a v e l de a m b i e n t e : %s \n\n , envp [ 3 ] ) ;
16

home = g e t e n v ( HOME ) ;
h o s t = g e t e n v ( HOSTNAME ) ;
u s e r = g e t e n v ( USER ) ;

18

20

p r i n t f ( Ola %s , b e n v i d o a maquina %s . \ n , u s e r , h o s t ) ;
p r i n t f ( A s u a home e s t a em %s . \ n , home ) ;

22

return 0;

24

Listagem 7.3: Exemplo com vari


aveis de ambiente (environ.c).

A listagem acima mostra um exmplo de aplicacao que recorre inicialmente ao vector de vari
aveis
de ambiente, imprimindo uma delas (a terceira, neste caso), e depois, acede ao conte
udo de outras
3 vari
aveis especficas. No meu sistema, este programa devolve o seguinte:
$ ./environ
3 Esta

utilizaca
o do main() com mais de dois argumentos n
ao
e standard. Trata-se de uma extens
ao do gcc . No
entanto, o uso de getenv(), etc. descrito abaixo
e standard.

62

7.2. Vari
aveis de ambiente

Terceira variavel de ambiente: HOSTNAME=nome.fc.up.pt


Ola vpereira, benvido a maquina nome.fc.up.pt.
A sua home esta em /home/vpereira.

63

7. Argumentos ao main

64

A. Lista de Comandos
Lista e descricao sum
aria dos programas mais relevante referidos neste documento. A mior parte
deles constituem os chamados GNU binnary utils (binutils). Informacao detalhada pode ser sempre
obtida nas p
aginas do manual correspondente, ou atraves do info binutils. Abaixo deixamos
apenas a descricao tal como devolvida pelo whatis.
ar
as
g77
gcc
ldd
ld
make
nm
wc

Create and maintain library archives


The portable GNU assembler
GNU project Fortran 77 compiler
GNU project C and C++ compiler
Print shared library dependencies
The GNU linker
GNU make utility to maintain groups of programs
List symbols from object files
Print the number of newlines, words, and bytes in files

65

A. Lista de Comandos

66

Bibliografia
[1] Peter Prinz and Ulla Kirch-Prinz, C Pocket Reference, OReilly, 2002.
[2] Brian J. Gough, An Introduction to GCC - for the GNU compilers gcc and g++, Network
Theory Ltd, 2004.
[3] Richard Stalman, The GNU make Manual (info make), Version 3.80, 2002.
[4] The GNU C Manual (info gcc), Version 4.1.0, 2006.
[5] The GNU Scientific Library Reference Manual (info gsl), Version 1.7, 2006.
[6] The GNU binary utilities (info binutils), Version 2.16.91.0.6, 2006.
[7] Al Kelley and Ira Pohl, A Book on C: programming in C, Addison-Wesley, 4th ed., 2001.
[8] P. M. Chaikin and T. C. Lubensky, Principles of condensed matter physics, Cambridge
University Press, 1995.

67

Vous aimerez peut-être aussi