• Nenhum resultado encontrado

Aula 12. Implementação da Programação Modular

N/A
N/A
Protected

Academic year: 2021

Share "Aula 12. Implementação da Programação Modular"

Copied!
19
0
0

Texto

(1)

Prof. Alessandro Garcia Eduardo Fernandes (assistente)

LES/DI/PUC-Rio Abril 2018

Aula 12

Implementação da

Programação Modular

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.

(2)

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

• Variáveis ponteiro para função

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio 3 /37

Classes de memória real

• Executável

– é onde se armazena o código a ser executadoe, 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çãodo programa para outra

• arquivos contendo parâmetros de execução: arquivos .dat e .ini • bases de dados

(3)

Processo de compilação simples (Ligação)

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio

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

5 /37

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 segmentosconstituem o programa carregável

(4)

Composição de um módulo objeto

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio

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 /37

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 locaisno 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

(5)

Processo de compilação simples

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio

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 /37

Composição de um executável

Passos

1. concatenaros módulos objeto

– código e dados estáticos

2. relocaros endereços do módulo de modo que estejam em conformidade com a origem na concatenação

3. resolver os nomes externosao 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

(6)

Relocação

• O ligador ajusta os endereçosdos elementos contidos em cada segmento de modo que passem a ser deslocamentos relativos à origemdos correspondentes segmentosdo 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

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio 11 /37

Resolução de nomes externos, sem bibliotecas

• O ligador cria uma tabela de símbolosque 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 forum 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 forum 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

(7)

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

– repete até todos os símbolos terem sido processados

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio 13 /37

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

(8)

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

• todos evoluem à medida que as bibliotecas forem evoluindo

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio 15 /37

Carga de um programa

• Para poderem ser executados, programas precisam estar em memória real

– fragmentosde um programa executável podem estar em qualquer um dos segmentos: executável, pilha (automático), estático, e dinâmico. A origemestará 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 colocadosos segmentos executável e estático e copia os segmentos do arquivo para a memória

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

(9)

Pré-processamento

• Um pré-processador

– é um processador de linguagem

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

– produz um outro arquivo de texto fonte na mesma linguagem

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio 17 /37

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

(10)

Consistência das interfaces C/C++

• Problema devido a propriedades sintáticas das linguagens C/C++

– variáveis globais externas devem aparecer exatamente uma vez sem o declarador extern

– todas as outras vezes devem vir precedidas deste declarador • Pode-se conseguir isso com código de pré-processamento

que assegure

– sempre que um cliente compilar o módulo de definição, as declarações de variáveis globais externas estejam precedidas de extern

– sempre que o próprio módulo compilar o módulo de definição, as variáveis globais externas não estejam precedidas de extern

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio 19 /37

Padrão para o módulo de definição

• Inicie o código do corpo do módulo de definição com o seguinte esquema de código:

/* Controle de escopo do arquivo de definição */ #ifdef Nome-arquivo-modulo_OWN

#define Nome-arquivo-modulo_EXT #else

#define Nome-arquivo-modulo_EXT extern #endif

• Ao final do código do módulo de definição coloque o código: #undef Nome-arquivo-modulo_EXT

(11)

Padrão para o módulo de definição

• Declare cada variável global externa não inicializada da seguinte forma:

Nome-arquivo-modulo_EXT declaração-de-variável ;

• Declare cada variável global externa inicializada da seguinte forma Nome-arquivo-modulo_EXT declaração-variavel #ifdef Nome-arquivo-modulo_OWN = Inicialização ; #else ; #endif

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio 21 /37

Exemplo de módulo de definição

#ifndef EXEMP_MOD #define EXEMP_MOD #ifdef EXEMP_OWN

#define EXEMP_EXT #else

#define EXEMP_EXT extern #endif

/***** Tipo de dados exportado pelo módulo *****/ typedef struct

{

int UmInt ; int OutroInt ;

} EX_tpMeuTipo ; /* declaração de tipos não é afetada pelas regras */ /***** Estruturas de dados exportada pelo módulo *****/

EXEMP_EXT int EX_vtNum[ 5 ] #if defined( EXEMP_OWN )

= { 1 , 2 , 3 , 4 , 5 } ; #else

; #endif #undef EXEMP_EXT

(12)

Padrão para o módulo de implementação

• No módulo de implementação, redija o código de inclusão do respectivo módulo de definição na forma a seguir:

#define Nome-Arquivo-Modulo_OWN #include "Nome-Arquivo-Modulo.H" #undef Nome-Arquivo-Modulo_OWN • Exemplo /* Inclusões do compilador / #include <stdio.h>

/* Inclusão do próprio módulo de definição / #define EXEMP_OWN

#include "EXEMP.H" #undef EXEMP_OWN

/* Inclusão de módulos de definição de servidores */ #include "Modulo1.H"

#include "Modulo2.H" #include "Tabela.INC" . . .

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio 23 /37

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)

(13)

O que são referências?

• Uma referênciaé formada por um conjunto de parâmetros a serem fornecidas a uma função de acessopara 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 ] ...

– exemplo disco físico: um serviço do sistema operacional

exemplo arquivo: fseek( , , )

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio 25 /37

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

(14)

Dereferenciação

• A dereferenciação pode ser realizada até chegar ao valor expA = expB

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio

RHS

right hand side

LHS

left hand side

busca-se o valor referenciado pelo endereço RHS i.e. dereferencia-seo endereço RHS atribui-se o valor RHS ao endereço LHS 27 /37

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

(15)

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

pA + inxA :: ??? idem

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio 29 /37

Ponteiros – Problemas comuns

• Vazamento de Memória:

– Retornarde função sem destruirespaç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

(16)

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 referenciaesse 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); }

Abr 2018 Alessandro Garcia © LES/DI/PUC-Rio 31 /37

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 tipoque a função B caso ambas tenham a mesma assinatura

– note que não se pergunta o que a função faz –semântica – no entanto deveria existir uma “igualdade” semântica

(17)

Abr 2018 33 /37

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 constantedo tipo ponteiro para uma função do tipo:

int ( int , double )

Alessandro Garcia © LES/DI/PUC-Rio

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

(18)

Abr 2018 35 /37

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 )) ;

Alessandro Garcia © LES/DI/PUC-Rio

Ponteiro para função

(19)

Prof. Alessandro Garcia Eduardo Fernandes (assistente)

LES/DI/PUC-Rio Abril 2018

Aula 12

Implementação da

Programação Modular

Referências

Documentos relacionados

Os objetivos desse estudo foram: verificar a evolução do peso e da composição corporal de nutrizes nos três primeiros meses pós-parto, bem como os fatores que influenciaram o

Fazemo-lo, não apenas para cuidar e alimentar o elo emocional forte que mantém a ponte ativa entre os Açorianos de cá e os que permanecem ou voltaram à nossa Região,

Apresentação e filogênese do sistema nervoso (S.N.); embriologia e histologia do S.N.; Medula, meninges e líquor; Tronco encefálico e formação reticular; Sistema

No caso dos dispositivos para testes de rastreio do sangue (à excepção dos testes de HBsAg e anti-HBc), todas as amostras verdadeiras positivas devem ser identificadas como

Curvas de rarefação (Coleman) estimadas para amostragens de espécies de morcegos em três ambientes separadamente (A) e agrupados (B), no Parque Estadual da Ilha do Cardoso,

Medição de Radiação Térmica com Modulador Sigma-Delta Termoresistivo Tese de Doutorado em Engenharia Elétrica - Valter Rosa - UFBA-SET2016.. Figura 4.9 Foto da bancada de testes

No mês de dezembro, os títulos públicos em carteira foram remunerados por um IPCA (inflação oficial) médio de -0,06%. A variação do índice INPC do mês de novembro, que

Com intuito, de oferecer os gestores informações precisas atualizadas e pré-formatas sobre os custos que auxiliem nas tomadas de decisões corretas, nos diversos