Vous êtes sur la page 1sur 17

Fundamentos de Programao Estruturada em C

Luiz Fernando Martha

Referncias Bsicas
Gries, D., The Science of Programming, Springer-Verlag, New York, 1985. Hehl, M.E., Linguagem de Programao Estruturada FORTRAN 77, McGraw-Hill, 1986. Arakaki, R. et. alli., Fundamentos de Programao em C, Livros Tcnicos e Cientficos. Kernighan, B.W.; Ritchie, D.M., The C Programming Language, Prentice-Hall, Second Edition, 1988. Savitch, W.J., Pascal: An Introduction of the Art and Science of Programming, The Benjamin / Cummings Publishing Company, 1984.

Introduo
Principal desafio: Como escrever eficientemente programas grandes? Um enfoque: 1. Pegue uma tarefa longa, quebre em pedaos (blocos) menores. 2. Repita 1. at que os pedaos sejam unidades inteligveis e fceis de serem manipuladas. Programa bem projetado: Unidades pequenas, independentes e bem documentadas. Programao estruturada: Formaliza a idia de dividir em blocos. Isto fora o programador a saber exatamente o estado do programa antes e depois de cada bloco e evita o chamado cdigo espaguete, onde no se tem noo em que estgio da execuo o programa se encontra em uma determinada instruo. Diagrama de blocos: Utilizado para mostrar graficamente o fluxo de controle de um programa. Adotado a notao de diagrama de blocos N-S (Nassi-Schneiderman) [Arakaki, Apndice B]. Comentrios em C:
/* Este um comentrio no meio de um cdigo em C */

Programao seqencial
O fluxo de controle de um programa estruturado feito de bloco em bloco, seqencialmente, onde cada bloco pode ser: uma instruo; um conjunto de instrues; um aninhamento de outros blocos; uma rotina (funo); um programa. Diagrama N-S: Bloco 1 Bloco 2 Bloco n Para se poder garantir que o fluxo de controle feito de forma seqencial, associado a cada bloco existe uma assero de pr-condio e uma assero de ps-condio:
/* pre: assero de pr-condio */ /* pos: assero de ps-condio */

Uma assero um comentrio que afirma alguma coisa que o programador espera ser verdadeira quando a execuo do programa atinge a assero. Exemplo:
/* pre: x + 1 > 0 */ x = x + 1; /* pos: x > 0 */

Uma tripla pre-bloco-pos diz que se a assero pre verdadeira antes da execuo do bloco, ento a assero pos verdadeira aps a execuo. A tripla no diz absolutamente coisa alguma a respeito da execuo do bloco quando pre falsa.

Exemplo em C:
/* Armazene em z o max(a,b) */ if( a > b ) { z = a; } else { z = b; } /* Alternativa em C */ z = ( a > b ) ? a : b;

Estruturas de controle
Na programao estruturada, a estruturao est baseada em um pequeno nmero de estruturas de controle: controle seqencial; controle de deciso; controle de seleo mltipla; controle de iterao do tipo enquanto-faa; controle de iterao do tipo repita-enquanto. Pode-se estabelecer o Princpio da Programao Estruturada (Hehl, 1986): Qualquer algoritmo pode ser escrito combinando-se blocos formados pelas estruturas de controle acima.

Bloco de Seleo com apenas um caso verdadeiro: Condio? Sim Bloco A No

Pseudo-cdigo:
if( exp ) { Bloco A }

Controle de deciso
Diagrama N-S: Condio? Sim Bloco A No Bloco B

Pseudo-cdigo:
/* exp: expresso booleana */ if( exp ) { Bloco A } else { Bloco B }

Expresso booleana: Nos exemplos acima exp uma expresso lgica relacional (expresso booleana) que contem: operadores relacionais: maior que maior ou igual a menor que menor ou igual a igual a diferente de operadores lgicos: conjuno (AND) disjuno (OR) negao (NOT) Expresses booleanas em C: operadores relacionais: > (maior que) >= (maior ou igual a) < (menor que) <= (menor ou igual a) == (igual a) != (diferente de) operadores lgicos: && (conjuno) || (disjuno) ! (negao)

Exemplos de asseres de pr- e ps-condio:


/* Armazene em z o max(a,b) */ /* pre0: TRUE */ if( a > b ) /* pre1: a > b */ z = a; /* pos1: z = a > b */ else /* pre2: a b */ z = b; /* pos2: a b = z */ /* pos0: z = a > b OR a b = z */ /* z = max(a,b) */ /* De uma forma geral: */ /* pre0: TRUE */ if( exp ) { /* pre1: pre0 AND exp */ Bloco A /* pos1: */ } else { /* pre2: pre0 AND NOT exp */ Bloco B /* pos2: */ } /* pos0: pos1 OR pos2 */

A instruo break necessria ao final de cada opo pois seno as instrues do bloco seguinte (pertencentes opo seguinte) seriam executadas. Isto feito de forma a permitir que dois ou mais valores diferentes da varivel de seleo fiquem associados a um mesmo bloco. Isto mostrado abaixo:
switch( elemento ) { case a: case b: case c: Bloco A break; default: Bloco N break; }

Implementao com ifelse ifelse em C:


if( elemento == a ) { Bloco A } else if( elemento == b ) { Bloco B } else if( elemento == c ) { Bloco C } else { Bloco N }
outros

Controle de seleo mltipla


Diagrama N-S: a b elemento? c

Bloco A Bloco B Bloco C

Bloco N

(onde a, b, c so constantes) Implementao com switch em C:


switch( elemento ) { case a: Bloco A break; case b: Bloco B break; case c: Bloco C break; default: Bloco N break; }

A estrutura ifelse ifelse tambm pode ser representada no diagrama N-S como mostrado abaixo: Sim Condio? exp1 Sim Condio? exp2 No No Condio? Sim exp3 No

Bloco A Bloco B Bloco C Bloco N

Controle de Iterao (Lao ou Loop)


Existem basicamente dois tipos de controle de iterao: Enquanto ... faa ... (while) Repita ... enquanto ... (do while) Diagrama N-S do loop while: Enquanto( exp ) Bloco

Um exemplo do loop while mostrado na implementao da funo binary que acha a posio de um nmero inteiro x em um vetor v[0..n-1] ordenado seqencialmente (veja Kernighan & Ritchie, 1988):
int binary( int x, int v[], int n ) { int low, high, mid; low = 0; high = n 1; while( low <= high ) { mid = (low + high) / 2; if( x < v[mid] ) high = mid 1; else if( x > v[mid] ) low = mid + 1; else /* found match */ return( mid ); } return( 1 ); }

Diagrama N-S do loop do while: Bloco Enquanto( exp ) Tanto o loop while quanto o loop do while terminam quando suas expresses booleanas so falsas. Uma diferena importante entre os dois tipos de loops que no loop while a expresso booleana verificada primeiro e se o seu valor for falso, o corpo do loop nunca executado; no loop do tipo do while, o corpo do loop sempre executado pelo menos uma vez pois a expresso booleana verificada no final do loop. Implementao de laos em C:
/* Enquanto ... faa ... */ while( exp ) { Bloco } /* Repita ... enquanto ... */ do { Bloco } while( exp );

A linguagem C tambm apresenta um outro tipo de lao enquanto ... faca .... o loop do tipo for, cujo diagrama N-S e instrues so mostrados a seguir: Para <inic>; Enquanto <exp>; Passo <reinic> Bloco
for( inic; exp; reinic ) { Bloco }

Onde inic um bloco de instrues de inicializao do lao, exp a expresso booleana para repetio da iterao e reinic um bloco de instrues para reinicializao da iterao. O lao for acima inteiramente anlogo ao lao while abaixo:
init; while( exp ) { Bloco reinic; }

interassante observar que na linguagem Pascal o loop do while substituido pelo loop repeat until que termina quando a expresso booleana verdadeira.

A utilizao mais freqente do loop for para fazer algum tipo de iterao com vetores, tal como inicializar com zeros o vetor de inteiros v[0..n-1]:

void init_vector( int v[], int n ) { int i; for( i=0; i<n; i++ ) v[i] = 0; }

Desenvolvimento de Loops
Dentro da programao estruturada foi caracterizado a existncia de asseres que podem ser feitas sobre o estado do programa antes de depois de cada bloco. Esta asseres so uma garantia que o programador tem sobre o comportamento do programa durante a sua execuo. Dada a importncia do lao em programao, interessante que se possa estabelecer outras asseres a respeito do seu comportamento de forma a se ter uma gantantia de que as iteraes de um lao iro terminar, convergindo para o resultado desejado. Considere o loop a seguir, que armazena na varivel s a soma dos elementos do vetor b[0..n1]. O loop est comentado de forma que a assero inv descreve os valores de s e i antes (e depois) de cada iterao.
/* pre: * b[0..n1], onde n 0; * (n = 0 => b[0..1], vetor vazio) */ i = 0; s = 0; /* inv: * i = 0; * s a soma de b[0..1]; * n iteraes a fazer */ while( i != n ) { /* inv: * 0 i < n; * s a soma de b[0..i1]; * ni iteraes a fazer */ s += b[i++]; } /* inv = pos: * i = n; * s a soma de b[0..n1]; * 0 iteraes a fazer */

Alteradores do fluxo seqencial


Um programa escrito com uma disciplina de programao estruturada muito mais fcil de ser documentado e entendido. No entanto, a eficincia deve prevalecer. Por isso, alguns desvios da forma estruturada so permitidos desde que no comprometam a clareza do programa e que quebrem de uma forma harmnica o fluxo seqencial. So quatro as instrues de desvio do fluxo seqencial em C:
break; continue; goto; return.

A instruo break em C causa a terminao do comando while, do while, for ou switch que envolve o break e que o mais interno. O fluxo passa para a instruo seguinte ao comando terminado. A instruo continue em C faz com que o fluxo de um lao (while, do while ou for) passe para o fim do lao de forma que a expresso booleana para repetio seja imediatamente verificada. Os comandos continue e goto podem ser entendidos pelos dois loops inteiramente equivalentes mostrados a seguir:
while( exp ) { Bloco A continue; Bloco B } | while( exp ) | { | Bloco A | goto CONTIN; | Bloco B | CONTIN: | } /* CONTIN um rtulo */

A instruo return interrompe o fluxo pois causa o retorno imediato para o funo que chamou a funo ativa.

Algumas observaes podem ser feitas a respeito do loop comentado: Se n = 0 (vetor vazio), ento a soma final s = 0. Tem-se sempre ni iteraes a fazer: ni decresce a medida que o loop executado; se existe alguma iterao a fazer, ento ni > 0;

ni chamado de funo teto (ft) do loop pois estabelece um valor mximo (um teto) para o nmero de iteraes que ainda devem ser feitas durante a execuo do loop. Savitch (1984) chama esta funo de variant expression. Se inv uma assero verdadeira e ft=0; ento a assero de ps-condio pos tambm verdadeira.

/* pre: ... */ initS; /* inv: ... */ /* ft: ... */ while( exp ) { S; } /* pos: ... */

Exemplos de desenvolvimento de loops: 1. Este programa acha a primeira posio i do valor x no vetor b[0..n1], dado que x est em b[]:
/* pre: x b[0..n1] */ i = 0; /* inv: x b[0..i1] * x b[i..n1] */ /* ft: n i */ while( b[i] != x ) i++; /* pos: b[i] = x */

A assero inv chamada de invariante do loop porque ela invariavelmente verdadeira antes e depois de cada iterao. A funo teto ft uma garantia de que o loop ir terminar. Com base neste exemplo, pode-se descrever um mtodo geral para o entendimento de um loop genrico, tal como o mostrado a seguir, e para demonstrar que ele ir terminar:
/* pre: ... */ initS; /* instrues para iniciar */ /* inv: ... */ while( exp ) { /* inv: ... */ S; /* instrues do loop */ } /* inv: ... */ /* pos: ... */

Mtodo: 1. Ache um invariante inv. 2. Mostre que initS; estabelece o invariante, isto , comprove a tripla:
/* pre: */ initS; /* inv: */

2. Este segundo programa calcula quociente q e o resto r quando x dividido por y. Para x>y>0, q e r so definidos por:
x = q*y + r

0 r < q

3. Mostre que cada iterao do loop mantm a validade do invariante:


/* inv: AND exp */ S; /* inv: */

4. Mostre que a assero pos verdadeira aps o trmino do loop:


/* inv: AND NOT exp *//* pos: */

5. Demonstre que o loop termina, isto , ache uma funo teto ft tal que: ft decresce em cada iterao; /* inv: AND exp */ ft > 0. Um loop fica convenientemente comentado tal como mostrado abaixo. Este formato deixa claro o que so o invariante e a funo teto, sendo que eles s precisam ser escritos uma s vez:

/* pre: x > */ q = 0; r = /* inv: x = * 0 */ /* ft: r */ while( r >= { r = y; q++; } /* pos: x = * 0 */

y > 0 x; q*y + r r

q )

q*y + r r < q

3. O terceiro programa um vetor b[0..n1], dado que contem nmeros 1s, 2s e 3s, de tal forma que os 1s precedam os 2s que por sua vez precedam os 3s:
/* pre:
0 n 1's ? 2's ? 3's

Exerccios propostos
1. Indique se as seguintes triplas de instrues so falsas ou verdadeiras. Em caso de falsa, justifique. Todas as variveis so inteiras. (a) /* i par */
i = i/2; /* i par */

*/ f = 0; h = 0; /* inv:
0 f 1's 2's

k = n 1;
h ? k 3's n

(b) /* i negativo */
i = i*i; /* i no-negativo */

*/ /* ft: k h */ while( h <= k ) switch( b[h] ) { case 1: t = b[h]; b[h] = b[f]; b[f] = t; f++; h++; break; case 2: h++; break; case 3: t = b[h]; b[h] = b[k]; b[k] = t; k ; break; } /* pos:
0 n 1's 2's 3's

(c) /* z = a**b (a elevado a b) */


b = b/2; a = a*a; /* z = a**b */

(d) /* TRUE */
while( i par ) i = i/2; /* i impar */

2. Escreva um loop, com inicializao, para somar os primeiros n nmeros naturais pares e positivos. Utilize as asseres de pr-condio, ps-condio e invariante, e a funo teto dadas abaixo:
/* pre: */ /* pos: */ /* inv: * */ /* ft: */ n > 0 s = 2 + 4 + 6 + ... + (2*n) 2 k 2*n e k par e s = k + (k+2) + ... + (2*n) k/2

b */

Obs.: O invariante abaixo seria menos eficiente pois exigiria at 2 trocas em cada iterao do loop:
/* inv:
0 n 1's 2's 3's ?

b */

3. dado um vetor b[0..n1], onde n 0. Escreva um loop (com inicializao) para inverter o vetor b (por exemplo, se inicialmente b = [1,3,5,2,6], ento para invert-lo significa modific-lo para b = [6,2,5,3,1]). Utilize as asseres de pr-condio, ps-condio e invariante, e a funo teto dadas abaixo:
/* pre: */ /* pos: */ /* inv: * * */ /* ft: */ b[0..n1] no invertido b[0..n1] invertido b[0..k1] invertido b[k..j] no invertido b[j+1..n1] invertido jk

4. Um polinmio de grau n (n 0) em y tem a seguinte forma:


b0 + b1*y + b2*y2 + ... + bn*yn

Suponha que os coeficientes sejam dados em um vetor b[0..n]. Escreva um loop (com inicializao) para achar o valor v do polinmio para um dado valor de y. A funo teto ft: ni. Usando uma varivel auxiliar x, o invariante do loop :
/* inv: * * */ 0 i n e x = yi e v = b0 + b1*y + ... + bn*yi

5. (Hehl, 1986) Escreva um programa em C para o algoritmo apresentado no diagrama N-S abaixo. O que faz este programa? Para i=1; enquanto i<=7; passo i=i+1 Para j=1; enquanto j<=7; passo j=j+1 n = 1; x = 1; y = 1; Enquanto n > 0 n par? Sim n = n / 2; x = x * x; Imprime i, j, y; No n = n - 1; y = y * x;

Funes, Variveis e Utilizao de Memria em C


Luiz Fernando Martha

Referncias Bsicas
Kernighan, B.W.; Ritchie, D.M., The C Programming Language, Prentice-Hall, Second Edition, 1988. Savitch, W.J., Pascal: An Introduction of the Art and Science of Programming, The Benjamin / Cummings Publishing Company, 1984. memria usada para armazenar as variveis locais que so necessrias para executar a funo. Estas variveis locais incluem cpias dos eventuais argumentos que so passados para a funo. Tomemos, por exemplo, a funo power, mostrada abaixo, que retorna o valor do nmero inteiro x elevado n-sima potncia. Esta funo chamada pela rotina principal main que imprime na tela todas as potncias do nmero 2 de 0 at 9.
int power( int x, int n ) { int i, p;

Introduo
Um dos aspectos que caracterizam uma linguagem de programao o mecanismo utilizado para o acionamento de uma rotina (funo). Este mecanismo varia quanto ao escopo de utilizao de memria do computador e quanto forma como os argumentos (parmetros) so passados da rotina que chama para a rotina que chamada. Uma das diferenas bsicas entre Fortran e C est justamente neste mecanismo.

Funes e utilizao de memria


Do ponto de vista de um programador de Fortran, uma subrotina pode ser entendida como um bloco de instrues e dados locais que tem uma localizao fixa na memria do computador. Quando uma subrotina acionada em Fortran, o fluxo de controle salta (jump) para a sua primeira instruo. O problema com este mecanismo que em Fortran no permitido que uma rotina chame a si prpria recursivamente, mesmo que indiretamente (alguns compiladores podem permitir isto, mas tal procedimento no padronizado). Isto ocorre porque os dados locais de uma rotina tm endereo fixo na memria. O mesmo no ocorre quando uma rotina (funo) acionada em C (ou mesmo Pascal). Neste caso, o programa requisita do sistema operacional um bloco de memria para executar a funo enquanto esta estiver ativa. Esta 9
p = 1; for( i=1; i<=n; i++ ) p *= x; return( p ); } void main( void ) { int i; for( i=0; i<10, i++ ) printf( %d %d\n, i, power( 2, i ) ); }

O escopo de utilizao de memria deste programa pode ser esquematizado grficamente atravs de um diagrama de retngulos, tal como mostrado a seguir.

main: i : __ power: x : __ n : __ i : __ p : __

program main integer a, b a = 2 b = 5 call swap( a, b ) stop end

Em C, a maneira errada de chamar e escrever esta rotina (funo) est mostrada a seguir:
void swap( int x, int y ) { int temp; temp = x; x = y; y = temp; }

No diagrama, cada retngulo representa a memria ativada para executar uma funo. Ao lado de cada varivel local colocado um espao para preencher o seu valor. As variveis locais em C so chamadas de automticas pois tm alocao de memria automtica.

Mecanismos de passagem de argumentos


importante observar que em C os argumentos de uma funo chamada so passados como variveis temporrias que contm cpias das variveis locais da funo que chamou. A este mecanismo d-se o nome passagem de argumentos por valor. Uma alternativa seria a passagem de argumentos por referncia, onde o endereo do argumento passado. Isto o que feito em Fortran. A linguagem Pascal contem os dois mecanismos de passagem de parmetros. Um problema da passagem de argumentos por valor que a funo chamada no pode alterar diretamente uma varivel que chamou, visto que a funo chamada s recebe cpias de argumentos. Por exemplo, suponha que se deseja escrever uma rotina swap para trocar os valores de duas variveis entre si. Em Fortran um programa que utilize esta rotina poderia ser:
subroutine swap( x, y ) integer x, y integer temp temp = x x = y y = temp return end

void main( void ) { int a = 2, b = 5; swap( a, b ); }

Isto est errado porque, como os argumentos so passados por valor, a funo swap no pode afetar os valores de a e b. Isto s seria conseguido se a e b fossem passados por referncia. bvio que este mecanismo tambm pode ser implementado em C, conforme ser mostrado na prxima seo. A vantagem da passagem de argumentos por valor est na garantia de privacidade de dados de uma determinada rotina, se este for o objetivo. Quando uma rotina chama uma funo passando parmetros por valor, ela pode ter certeza de que as variveis locais que foram usadas como parmetros no sero alteradas pela funo chamada.

Implementao de passagem de argumentos por referncia em C


Em C, o endereo (posio na memria) de uma varivel pode ser acessado explicitamente. Por exemplo, seja o nmero inteiro int a;. O indentificador &a identificado pelo compilador como o endereo da varivel a.

10

Tambm permitido a criao de variveis que contm endereos de outras variveis. So os chamados ponteiros, que so declarados por exemplo como int *p;. Neste caso, p um ponteiro para um inteiro. Isto quer dizer que o valor de p um endereo na memria de um inteiro. Por exemplo, para um inteiro a, a instruo p = &a; atribui a p o endereo de a. Alm disso, existe a possibilidade de acessar o contedo do endereo que apontado por um ponteiro. Por exemplo, considere o programa abaixo:
void main( void ) { int a = 10; int *p; p = &a; printf( %d\n, *p ); }

A princpio a implementao deste mecanismo de passagem de argumento por referncia em C pode parecer complicada, principalmente se o programador estiver acostumado com Fortran, onde no se tem um acesso explcito a endereos de variveis e no existe variveis ponteiros para outras variveis. No entanto, a lgica da linguagem C consistente (sempre passa argumentos por valor, mesmo que sejam endereos de variveis) e consegue implementar os dois mecanismos de passagem de argumentos.

Simulao de passagem de argumentos por diagrama de retngulos


O objetivo desta seo descrever por diagramas de retngulos o escopo de utilizao de memria e o mecanismo de passagem de argumentos durante a execuo de um programa. Uma extenso do exemplo anterior da funo swap utilizada para descrever esta execuo. A extenso tal que a troca dos valores das variveis s feita se um tereiro parmetro dado (por valor) for positivo. Este programa mostrado abaixo:
void swap( int f, int *px, int *py ) { int temp; if( f > 0 ) { L2: temp = *px; L3: *px = *py; L4: *py = temp; L5:} } void main( void ) { int a = 2, b = 5, f = 1; L1: L6: } swap( f, &a, &b );

Este programa imprime o valor 10 na tela, pois o identificador *p identificado pelo compilador como o contedo do endereo que identificado por p. Com base no que foi exposto acima, a implementao da passagem de argumentos por referncia em C pode ser feita facilmente. Isto , como se tem um acesso explcito ao endereo de uma varivel, quando se deseja passar um argumento por referncia (para alterar o valor da varivel correspondente na funo chamada, por exemplo), basta que se passe (por valor) o endereo (ou um ponteiro contendo o endereo) da varivel. A implementao correta da funo swap para trocar os valores de duas variveis entre si est mostrada abaixo:
void swap( int *px, int *py ) { int temp; temp = *px; *px = *py; *py = temp; } void main( void ) { int a = 2, b = 5; swap( &a, &b ); }

No diagrama de retngulos, o ativamento de uma funo representado por um retngulo. As variveis locais so identificadas por um identificador que representa a memria utilizada pela varivel. As variveis locais que so ponteiros tambm tm setas que apontam para os identificadores correspondentes. A seguir so mostrados diagramas para a execuo do programa acima imediatamente antes de cada 11

uma das instrues com rtulos (labels L1, L2, L3, L4, L5 e L6). Antes de L1: main: a : __ 2 b : __ 5 f : __ 1

Antes de L5: main: a : __ 5 b : __ 2 f : __ 1 swap: f : __ 1 px : __ py : __ temp : __ 2 Antes de L6: main: a : __ 5 b : __ 2 f : __ 1

Antes de L2: main: a : __ 2 b : __ 5 f : __ 1 swap: f : __ 1 px : __ py : __ temp : __ ?

Variveis externas
Antes de L3: main: a : __ 2 b : __ 5 f : __ 1 swap: f : __ 1 px : __ py : __ temp : __ 2 Antes de L4: main: a : __ 5 b : __ 5 f : __ 1 swap: f : __ 1 px : __ py : __ temp : __ 2 A alocao de variveis externas esttica, isto , existe um espao de memria reservado para cada varivel externa j na hora que o programa ativado. As variveis externas, porque so acessadas globalmente, fornecem um meio alternativo para a comunicao entre as funes. Assim, Um programa em C consiste de um conjunto de objetos externos, que podem ser tanto variveis quanto funes. O adjetivo externo utilizado para diferenciar de objetos internos a uma funo, que so os seus parmetros (cpias de variveis passadas) e variveis locais, chamadas de variveis automticas pois tm alocao de memria automtica. Variveis externas so definidas fora de qualquer funo, e so portanto potencialmente disponveis para muitas funes (variveis externas so semelhantes s variveis definidas dentro de uma instruo COMMON de Fortran). As funes so sempre externas, pois C no permite uma funo ser definida dentro de outra funo.

12

no exemplo da troca de valores de variveis, a implementao poderia ser:


/* Variveis externas */ int a = 2; int b = 5; void swap_ab( void ) { int temp; temp = a; a = b; b = temp; } void main( void ) { swap_ab( ); }

mdulo. Elas diferem das variveis externas globais (que so visveis para todas as funes de todos os mdulos de compilao) pela instruo static que precede o tipo da varivel.

Funes estticas
Funes estticas so funces que so visveis apenas por um mdulo de compilao. Somente as funes do mdulo podem acionar (chamar) uma funo esttica. A vantagem de funes estticas que elas so privadas ao seu mdulo de compilao, podendo at repetir nomes de outras funes estticas de outros mdulos. Em C, static significa no somente permanncia em memria, mas tambm um certo grau de privacidade: variveis estticas internas so visveis apenas por uma funo; e variveis estticas externas e funes estticas so visveis apenas por um mdulo de compilao. O uso de funes estticas e variveis estticas externas auxilia a modularizao de um programa pois aumenta o grau de independncia de um mdulo em relao aos demais.

Uma varivel externa pode ser compartilhada por funes que esto localizadas em mdulos de compilao distintos (arquivos fontes distintos). Para isso, ela deve ser definida em um mdulo e declarada em outros mdulos que a utilizem, o que feito usando a instruo extern. Exemplos disso podem ser:
extern int extern int a; b;

Variveis estticas
Alm da varivel automtica (alocada automaticamente na memria quando a sua funo acionada) e da varivel externa, existe uma terceira classe que se chama varivel esttica. Variveis estticas podem tanto ser internas quanto externas. Variveis estticas internas so locais para uma determinada funo tanto quanto so as variveis automticas, mas, diferentemente destas ltimas, elas permanecem existentes na memria ao invs de irem e virem cada vez que a funo ativada. A declarao de uma varivel esttica interna difere da declarao de uma varivel automtica apenas pela instruo static que precede o tipo da varivel:
static int a;

Variveis estticas externas so locais para um mdulo de compilao (arquivo fonte), isto , s so visveis para as funes que compem o 13

Exerccios propostos
1. Desenhe uma seqncia de diagramas de retngulos para indicar o estado de execuo do programa abaixo imediatamente antes de cada instruo com rtulo Li:. Indique nestes diagramas um identificador para a varivel externa que aparece, com o valor correspondente para cada estado.
int i; void fncA( void ) { L5: i = 13; } void fncB( void ) { L4: fncA(); } void fncC( void ) { L3: fncB(); } void main( void ) { L1: i = 12; L2: fncC(); L6: }

3. Execute o programa abaixo e mostre uma seqncia de diagramas de retngulos, com identificadores para as variveis e os argumentos de funo. Os diagramas devem mostrar o estado do programa antes de cada instruo com rtulo Li:. Coloque os valores correspondentes para os identificadores. Este exerccio foi projetado para for-lo(a) a entender o mecanismo de utilizao de memria e de passagem de argumentos de funes em C, mesmo em face chamada recursiva de funes.
void fnc( int *y, int z ) { int x; if( z <= 1 ) *y = 16; else { x = z 2; L1: fnc( &x, *y 4 ); } L3: } L2: void main( void ) { int x = 6; L0: fnc( &x, x 3 ); L4: }

2. Execute os programas abaixo e mostre uma seqncia de diagramas de retngulos, com identificadores para as variveis e o argumento de funo. Os diagramas devem mostrar o estado dos programas antes de cada instruo com rtulo Li:. Coloque os valores correspondentes para os identificadores.
int x, y; void fnc(int a) { L1: a = 12; L2: y = x; } void main(void) { x = 4; L0: fnc( x ); L3: } | | | | | | | | | | | | int x, y; void fnc(int *a) { L1: *a = 12; L2: y = x; } void main(void) { x = 4; L0: fnc( &x ); L3: }

14

Estruturas e Alocao Dinmica de Memria em C


Luiz Fernando Martha

Referncia Bsica
Kernighan, B.W.; Ritchie, D.M., The C Programming Language, Prentice-Hall, Second Edition, 1988.

Estruturas
Uma estrutura (struct) uma coleo de uma ou mais variveis, possivelmente de tipos diferentes, agrupadas segundo um nico nome para facilidade de manuseio. Um exemplo de uma declarao de uma estrutura seria:
struct data { int dia; int mes; int ano; int dia_ano; char nome_mes[4]; };

/* definio de uma varivel deste * tipo */ Data d;

Pode-se inclusive declarar a estrutura e definir um tipo para ela de uma s vez:
typedef struct data { int dia; int mes; int ano; int dia_ano; char nome_mes[4]; } Data;

A definio de uma varivel do tipo estrutura pode ser feita como exemplificada abaixo:
/* sem inicializao */ struct data d; /* com inicializao */ struct data d = {4,7,1776,186,"Jul"};

O acesso a um campo (membro) de uma estrutura feito da seguinte maneira:


int Data t; d;

t = d.dia;

Estruturas tambm podem ser declaradas dentro de outras estruturas, tal como mostrado abaixo:
struct pessoa { char nome[81]; struct data nasc; };

Alternativamente, pode-se criar um tipo para esta estrutura:


struct data { int dia; int mes; int ano; int dia_ano; char nome_mes[4]; }; /* criao do tipo Data */ typedef struct data Data;

E o acesso a um campo desta estrutura pode ser exemplificado como:


struct pessoa empreg;

t = empreg.nasc.dia; /* equivalente a: t = (empreg.nasc).dia; */

15

Ponteiros para estruturas


Considere o problema de escrever uma funo para preencher os campos de uma estrutura. Existem duas alternativas para se passar uma estrutura como parmetro para esta funo: ou passa-se por valor ou por referncia. Neste caso, no faz sentido passar a estrutura por valor pois seria passado uma cpia da estrutura e o preenchimento dos seus campos pela funo no surtiria efeito. A maneira correta passar a estrutura por referncia, isto , passar o endereo (ponteiro) da estrutura. Isto exemplificado abaixo.
void preenche( Data *pd ) { pd>dia = 4; pd>mes = 7; pd>ano = 1776; pd>dia_ano = 186; pd>nome_mes = "Jul"; } void main( void ) { Data d; preenche( &d ); }

void acessa_dia( Pessoa *empreg ) { int t; t = empreg>nasc>dia; /* isto equivalente a: t = (empreg>nasc)>dia; */ }

A declarao de uma estrutura tambm pode ser auto-recursiva. Isto est exemplificado abaixo para a declarao e definio de tipo de um elemento de lista que aponta para o elemento seguinte (do seu mesmo tipo) na lista:
typedef struct lista { struct lista *next; int dados; } ListElem;

Vetor de estruturas
Assim como pode-se declarar um vetor de caracteres, ou um vetor de inteiros, pode-se tambm declarar um vetor de estruturas:
/* Declarao esttica: */ struct pessoa staff[100]; /* ou ainda: */ Data dias[366];

Neste caso pd um ponteiro para uma estrutura do tipo Data. O acesso de um campo da estrutura, cujo ponteiro dado, exemplificado por pd>ano, o que equivalente a (*pd).ano. Uma estrutura tambm pode conter campos que so ponteiros para estruturas. Um exemplo disto :
typedef struct pessoa { char nome[81]; struct data *nasc; } Pessoa;

E a indexao feita da mesma forma que feita para qualquer outro vetor:
/* Atrubui a "d" todos os valores * do vigsimo (ndice = 19) dia do * vetor "dias": */ Data d; d = dias[19];

Neste caso, a estrutura Pessoa tem dois campos, um o nome da pessoa e o outro um ponteiro para a data do seu nascimento. A funo abaixo mostra um exemplo do acesso a um campo da data de nascimento de uma pessoa cujo registro passado (por referncia):

Alocao dinmica de estruturas


A linguagem C possibilita a requisio de memria para variveis de uma forma dinmica em tempo de execuo. Isto feito atravs de chamadas a funes que so disponveis em bibliotecas padres que acompanham todos os compiladores C.

16

O modelo de utilizao das funes para alocao dinmica de memria o seguinte: Toda vez que o programa precisa de um espao na memria para uma varivel (que pode ser uma estrutura) ou um vetor, ele chama uma funo de alocao de memria passando o tamanho em palavras (bytes) da memria necessria. O sistema operacional verifica se existe o tanto de memria disponvel em uma rea contgua e retorna, como valor de retorno da funo chamada, o endereo (ponteiro) onde inicia a poro de memria requisitada. Se no existe memria disponvel o valor retornado nulo. Esta memria requisitada pelo programa pode ser liberada tambm em qualquer instante da execuo. Isto feito atravs da chamada de uma funo de liberao. Isto resulta na devoluo ao sistema operacional desta poro de memria para que ela possa ser utilizada por outros clientes ou em outra fase da execuo. Existe tambm a possibilidade de realocao de uma memria que foi requisitada anteriormente para aumentar ou diminir o seu tamanho. O sistema operacional verifica se existe uma rea de memria contgua (que sempre existir caso a realocao for para um tamanho menor) e retorna o ponteiro para o nova rea (que pode inclusive ser o mesmo endereo). Caso no exista uma rea contgua, a funo de realocao retorna um valor nulo. Quando h a realocao, a poro de memria que havia sido requisitada anteriormente copiada para a nova rea. As declaraes (prototypes) das funes de alocao so as seguintes:
void *malloc( int total_size ); void *calloc( int n, int size );

void free( void *pointer_to_area ); void *realloc( void *old_area, int total_new_size );

As funes abaixo mostram um exemplo de alocao dinmica de um vetor de estruturas:


typedef struct pessoa { char nome[81]; struct data *nasc; } Pessoa; static Pessoa *pessoas;

void aloca_pessoas( int npessoas ) { pessoas = (Pessoa *)calloc(npessoas, sizeof( Pessoa ) ); } void libera_pessoas( void ) { free( pessoas ); }

Abaixo se encontra um exemplo de alocao dinmica de uma lista com trs elementos:
typedef struct lista { struct lista *next; int dados; } ListElem; static ListElem *list_head = NULL;

void aloca_lista( void ) { ListElem *elem1, *elem2, *elem3; elem1 = (ListElem *)malloc( sizeof( ListElem ) ); elem2 = (ListElem *)malloc( sizeof( ListElem ) ); elem3 = (ListElem *)malloc( sizeof( ListElem ) ); list_head = elem1>next elem2>next elem3>next } void libera_lista( void ) { ListElem *elem; while( list_head != NULL ) { elem = list_head; list_head = elem>next; free( elem ); } } elem1; = elem2; = elem3; = NULL;

A funo malloc recebe como parmetro o tamanho total de memria requisitada. A funo calloc recebe um parmetro para o nmero de entidades requisitadas e outro para o tamanho de cada entidade. As declaraes das funes de liberao e realocao esto mostradas abaixo:

17

Vous aimerez peut-être aussi