• Nenhum resultado encontrado

Aula 10 Implementação da Programação Modular

N/A
N/A
Protected

Academic year: 2021

Share "Aula 10 Implementação da Programação Modular"

Copied!
31
0
0

Texto

(1)

Alessandro Garcia LES/DI/PUC-Rio Abril 2019

Aula 10

Implementação da

Programação Modular

(2)

Especificação

Objetivo dessa aula

– Visão geral sobre compilação de programas modulares

– Estudar em detalhe a composição generalizada de módulos

– Estudar o uso de ponteiros

• Slides adaptados de: Staa, A.v. Notas de Aula em Programação Modular; 2008.

Referência básica:

– Capítulos 6, 8 e Apêndice 7 do livro texto

Referência complementar:

– Schildt H. C – Completo e Total, 3ª Edição. Makron Books, 1997.

(3)

Sumário

Pilha de execução e classes de memória

Ligação, pré-processamento e compilação

Composição de módulos e estrutura de diretórios

Referências, funções de acesso

Função como dado

(4)

Classes de memória real

• Executável

– é onde se armazena o código a ser executado e, possivelmente, algumas constantes numéricas

• Estática encapsulada

– contém todos os espaços de dados globais encapsulados (declarados

static), e todas as constantes literais definidas no interior de módulos

• Estática visível

– contém todos os espaços de dados globais externados

• Automática

– contém a pilha de execução do programa

• Dinâmica

contém espaços de dados alocados pelo usuário (malloc, new)

• Persistente

– é onde se armazenam os dados que estarão disponíveis de uma

instância de execução do programa para outra

(5)

Ligação

A ligação combina

m >= 1 módulos objeto e módulos

contidos em uma ou mais bibliotecas, produzindo o

programa carregável

(.EXE, .COM)

No módulo objeto todos os endereços gerados

são

deslocamentos

(offsets) relativos a zero dentro da

respectiva classe de memória

O ligador

(linker) justapõe (concatena) os espaços de cada

uma das classes de memória (segmentos: executável e

estática) definidos nos módulos objeto, formando um único

grande espaço para cada classe de memória

Estes dois grandes segmentos

constituem o programa

carregável

(6)

Composição de um módulo objeto

Dados

estáticos

Código

Tab Simb

M1.OBJ

Declarados e definidos, relativos a 0

Tabela de símbolos

- referências a nomes externos

declarados e não definidos

- referências a nomes externos

declarados e definidos (externados

pelo módulo)

(importados)

Relocável, relativo a 0

Tab Reloc

Tabela de relocação

- informa os locais contendo endereços

a serem relocados

(7)

Símbolos definidos no módulo objeto

Cada módulo objeto contém uma tabela de símbolos

agregando os nomes globais externos, particionada em

– símbolos somente declarados

– símbolos declarados e definidos

Os símbolos somente declarados definem uma lista de todos

os locais

no código (ou nos dados) em que o símbolo é

referenciado

Um nome externo somente declarado em um determinado

módulo necessariamente deverá estar declarado e definido

em exatamente um outro módulo do programa sendo

composto

(8)

Processo de compilação simples

Compilador Compilador Compilador Ligador M3.OBJ M2.OBJ M1.OBJ L2.LIB L1.LIB PG.EXE Editor de programas M3.HPP M3.CPP M2.HPP M2.CPP M1.HPP M1.CPP

Programa

(9)

Composição de um executável

Passos

1. concatenar os módulos objeto

– código e dados estáticos

2. relocar os endereços do

módulo de modo que estejam em conformidade com a

origem na concatenação

3. resolver os nomes externos ao módulo definidos em outro

módulo p.exe

código dados estáticos

0 300 530 0 10 30 m1 m2 m3 m1 m2 m3 int a int b int c a a b c

(10)

Relocação

O ligador ajusta os endereços

dos elementos contidos em

cada segmento de modo que passem a ser deslocamentos

relativos à origem

dos correspondentes segmentos

do

programa

A relocação ocorre com relação aos segmentos

– código

– estático local e externo

A tabela de relocação

contida no módulo objeto informa os

pontos no código e nos dados globais do módulo que

deverão ser ajustados

– no módulo objeto os deslocamentos são relativos a zero

– para relocar basta somar a origem do segmento do módulo definida no segmento composto às referências internas ao módulo registradas na tabela de relocação

(11)

Resolução de nomes externos, sem bibliotecas

• O ligador cria uma tabela de símbolos que conterá os nomes externos. Cada símbolo informa

– o endereço no segmento composto

– a lista dos locais que referenciam o símbolo ainda não definidos

• Ao encontrar um nome externo

– adiciona-o à tabela caso ainda não figure lá

– se for um nome externo declarado e definido

• se a tabela de símbolos do ligador já define o nome, é emitido um erro de duplicação de definição

• caso contrário, percorre a lista dos locais que referenciam o símbolo e atribui o endereço definido

– se for um nome externo somente declarado

• se a tabela de símbolos do ligador já define o nome, atribui esta definição aos locais no módulo que referenciam este símbolo

• caso contrário, o ligador acrescenta a lista do módulo à lista do ligador

• Ao terminar o processamento

– para cada símbolo não definido contido na tabela do ligador, é emitido um erro de símbolo não definido

(12)

Resolução de nomes externos, com bibliotecas

Uma biblioteca estática

(.lib) é formado por

– uma lista de módulos

– uma tabela de símbolos contendo os símbolos externados pelos módulos e a referência para o código do respectivo módulo na lista de módulos

Após compor todos os módulos objeto, para cada símbolo

ainda não definido

– o ligador procura este símbolo, segundo a ordem de fornecimento das bibliotecas

• caso seja encontrado, o módulo correspondente é extraído da biblioteca e acrescentado ao programa sendo montado

– para isso segue o procedimento anterior

• caso não seja encontrado, é emitido um erro de símbolo não definido

(13)

Ligação dinâmica

Bibliotecas dinâmicas

(.dll) são carregadas à medida que

forem acessadas durante o processamento

– ao encontrar um símbolo externo ainda não resolvido

utiliza a .dll, se já carregada, ou então carrega ela

• substitui a referência ao símbolo para a referência à função

– cada biblioteca é compartilhada por todos os programas que a usem

– cada programa estabelece espaços próprios para os dados

Vantagens

– uma biblioteca dinâmica é carregada uma única vez

considerando todos os programas em execução simultânea

– pode-se trocar uma biblioteca sem precisar recompilar ou religar todo o programa

(14)

Ligação dinâmica

Problemas

– precisa-se projetar com muito cuidado as bibliotecas

dinâmicas, visando explicitamente a possibilidade do seu reúso em diversos programas

– as bibliotecas são conhecidas pelo nome, portanto pode ocorrer colisão de nomes

• bibliotecas diferentes com o mesmo nome

– é necessário assegurar que a versão correta da biblioteca seja utilizada com cada um dos programas

• todos os programas utilizam a mesma versão da biblioteca, a menos que se possa armazenar as bibliotecas em locais distintos

(15)

Carga de um programa

Para poderem ser executados programas precisam estar em

memória real

– fragmentos de um programa executável podem estar em

qualquer um dos segmentos: executável, pilha (automático), estático, e dinâmico. A origem estará no segmento executável.

Ao ativar um programa é ativado o carregador

que recebe

como parâmetro o nome do arquivo

contendo o programa a

ser carregado

O carregador

– determina onde serão colocados os segmentos executável e estático e copia os segmentos do arquivo para a memória

– efetua as necessárias relocações de modo a ajustar os endereços contidos nesses segmentos

(16)

Pré-processamento

Um pré-processador

– é um processador de linguagem

– recebe um arquivo contendo texto fonte e diretivas de pré-processamento

(17)

Padrão de programação C

Ao desenvolver programas em C ou C++ siga o

recomendado no apêndice 1 Padrão de Composição de

Módulos C e C++.

Todos os módulos que podem ser incluídos devem conter

um controle de compilação única

– módulo de definição

– tabelas de definição

– tabelas de dados

#if !defined( Nome-arquivo_MOD ) #define Nome-arquivo_MOD

/* Comentário cabeçalho do arquivo */

Corpo do arquivo

#endif

(18)

Estrutura de diretórios (pastas)

É fortemente recomendável criar uma estrutura de

diretórios

(pastas) padrão para cada projeto

– pastas com menos arquivos são mais fáceis de manipular

– cada pasta conterá arquivos de alguns poucos tipos

– Exemplo: autotest/instrum

+ Projeto X

+ Batches contém arquivos .bat

+ Composicao contém arquivos .make, .comp, ... + ModulosFonte contém arquivos .c, .h, ...

+ ModulosObjeto contém arquivos .obj, .build, ... + Produto contém arquivos .exe, .log, ...

+ Tabelas contém arquivos .tabstr, .incstr, ... + Teste contém arquivos .script, .count, ... + ... (por exemplo os de apoio ao ambiente)

(19)

O que são referências?

Uma referência

é formada por um conjunto de parâmetros

a

serem fornecidas a uma função de acesso

para acessar um

determinado espaço

– exemplo: elemento de um vetor em C

< origemVetor , dimElemento , inxElemento >

– exemplo: disco físico

< unidade , cilindro , trilha , setor >

– exemplo: arquivo

< pArq , inxByte , idOrigem >

Cada classe de referência

possui uma função de acesso

associada a ela

exemplo vetor: tpX vtX[ dimVtX ] ; ... vtX[ i ] ...

(20)

Dereferenciação

Dereferenciar

é a operação de converter uma referência em

um endereço real através de sua função de acesso,

exemplos:

(obs.: não são fragmentos de código em C)

A[j] :: &A + j * sizeof( tpA ) – tudo medido em bytes

*pX :: [ pX ] ou por extenso: conteúdo de pX

seja: pElemTabSimb * ObterElemTabSimb(

char * pszSimbolo)

*( ObterElemTabSimb( "um_simbolo" ))

é o espaço de dados associado ao símbolo "um_simbolo“

• :: operador “é definido por”

operador de dereferenciação de ponteiro

(21)

Dereferenciação

A dereferenciação pode ser realizada até chegar ao valor

expA = expB

RHS

right hand side LHS

left hand side

busca-se o valor referenciado pelo endereço RHS

i.e. dereferencia-se o endereço RHS atribui-se o valor RHS

(22)

Dereferenciação composta

Assumindo que os espaços de dados referenciados existam:

typedef struct tgElemLista {

char szSimbolo[ DIM_SIMBOLO ] ; unsigned IdSimbolo ;

struct tgElemLista * pProx; } tpElemLista ;

tpElemLista * Tabela[ DIM_TABELA ] ;

( Tabela[ ObterHash( szSimboloDado )]->pProx)->szSimbolo

ou

*(*( Tabela[ ObterHash( szSimboloDado )]).pProx ).szSimbolo

acessam o mesmo vetor de caracteres – o segundo

(23)

Ponteiros

Ponteiros são casos especiais

de referências

função de acesso é implícita: *pX :: conteúdo de pX

Há quem prefira outra definição de modo que se caracterize

o controle que se pode ter quando se usa referências,

contrastando com a falta de controle quando se usa

ponteiros, exemplo

vtA[ inxA ] ::

if (( inxA < 0 ) || ( inxA >= dimA ))

ErroAcesso( __FILE__ , __ LINE__) ;

pA[ inxA ] :: ??? pois não se sabe o valor de dimA

(24)

Ponteiros – Problemas comuns

Vazamento de Memória:

– Retornar de função sem destruir espaços de memória (não encadeados em alguma estrutura) referenciados por ponteiros locais

Falha de Segmentação:

– Dereferenciar ponteiro com valor NULL

– Dereferenciar ponteiro não inicializado

– Dereferenciar ponteiro cujo espaço de memória foi destruído

(free/delete)

Apêndice 7: regras

e recomendações

para o uso de

ponteiros

(25)

Ponteiro para função

Funções possuem:

– Um tipo

– Um espaço de dados que é ocupado pelo código da função

– Um nome que referencia esse espaço de dados

Em C é possível criar variáveis e parâmetros do tipo

“Ponteiro para função”:

int soma (int a, int b) {

return a + b; }

int main () {

int (*operacao) (int, int); operacao = soma;

operacao(1, 2); }

(26)

Função como um dado

O tipo de uma função é estabelecido pela sua assinatura

física

– valor retornado

– lista dos tipos de parâmetros

• os nomes dos parâmetros não fazem parte da assinatura

• o nome da função não faz parte da assinatura

exemplo: int ( int , double )

exemplo: tpHistórico * ( tpIdAluno )

Uma função A será do

mesmo tipo

que a função B caso

ambas tenham a mesma assinatura

– note que não se pergunta o que a função faz – semântica

(27)

Dado tipo ponteiro para função

Em C e C++ podem ser definidas variáveis do tipo ponteiro

para função

exemplo: int (* VarF1 )( int , double )

VarF1 é um ponteiro para função do tipo: int ( int , double )

exemplo: tpHist * (* VarF2 )( tpIdAl )

VarF2 é um ponteiro para função do tipo: tpHist * ( tpIdAl )

– Exemplo de atribuição

int Func( int X , double Y ) {

...

} /* Func */

VarF1 = Func ;

Func é uma constante do tipo ponteiro para uma função do tipo:

(28)

Exemplo simples: integração

/* Função de integração numérica utilizando regra dos trapézios */ double Integrar( double LimInf ,

double LimSup , int NumInt ,

double ( * Func )( double X )) {

double Integral = 0.0 ; double Intervalo ;

int i ;

assert( NumInt >= 2 ) ;

Intervalo = ( LimSup - LimInf ) / ( NumInt - 1 ) ; Integral += Func( LimInf ) ;

Integral += Func( LimSup ) ; Integral /= 2.0 ;

for ( i = 1 ; i < NumInt - 1 ; i++ ) {

Integral += Func( Intervalo * i ) ; } /* for */ Integral *= Intervalo ; return Integral ; } /* Integrar */ A B Y X 0 1 2 3 4 5 6 7 8

(29)

Exemplo simples: integração, uso

/* exemplo de uso */

double Quadrado( double X ) {

return X ** 2 ; }

double Cubo( double X ) { return X ** 3 ; } printf( ″\n F = x ** 2 de x=1 a x=10: %lf″ , Integrar( 1.0 , 10.0 , 20 , Quadrado )) ; printf( ″\n F = x ** 3 de x=1 a x=10: %lf″ , Integrar( 1.0 , 10.0 , 20 , Cubo )) ;

(30)

Ponteiro para função

(31)

Referências

Documentos relacionados

Resumo da reunião mensal das Diretorias Regionais, Municipais, Distritais e Representantes Locais do Ciesp, realizada em 19 de março.. -Convênio Receita Federal e Corpo de Bombeiro:

A partir de uma investigação etnográfica realizada em um curso de língua portuguesa para mães imigrantes, apresentamos e discutimos os significados do trabalho e

processo de inquirição conduzido pelo GIASE, revelam, para cada estabelecimento e agrupamento, informação quanto aos diferentes níveis e cursos ministrados e

Para valores menores ou iguais que a unidade de medida caseira: PERCENTUAL DE MEDIDA CASEIRA FRAÇÃO A INDICAR até 30% 1/4 de ... METODOLOGIA A SER EMPREGADA PARA

KNOWN: Inner and outer radii of a tube wall which is heated electrically at its outer surface and is exposed to a fluid of prescribed h and T. Thermal contact resistance between

A abertura de vagas de estágios nas empresas do setor eletro-eletrônico, instaladas na Zona Franca de Manaus-AM, que passaram a responder pela maior parte dos estágios na área

Estudos da neurociência sustentam que a música e a linguagem são duas formas de comunicação humana que estão próximas em se tratando de processamento mental e

Esta pesquisa buscou abordar um assunto extremamente necessário e urgente para a educação básica brasileira, as competências socioemocionais em sala de aula, tanto é que um