LINGUAGEM: Linguagem C - Capítulo 1 - Parte 3

Declarações: Variáveis e Classe de Memória

Objetivo:

Na última aula, vimos que uma DECLARAÇÃO introduz um novo IDENTIFICADOR e associa a este uma interpretação. Dentre as interpretações possíveis, um IDENTIFICADOR pode ser declarado para ser o nome símbólico de uma VARIÁVEL. Neste caso, temos um dos mais importantes tipos de DECLARAÇÃO - a DECLARAÇÃO de VARIÁVEIS.

Nesta aula, nosso objetivo é estudar:
  • a forma geral de DECLARAÇÕES de VARIÁVEIS em C
  • a noção de CLASSE de MEMÓRIA associada a uma VARIÁVEL

Variáveis:

VARIÁVEIS são abstrações de locais de armazenamento de dados. Os dados armazenados em uma VARIÁVEL podem mudar ao longo da execução de um programa. Daí o nome VARIÁVEL.

Uma VARIÁVEL possui dois atributos principais:
  • um TIPO de DADO : determina o significado dos valores achados em uma VARIÁVEL.
  • uma CLASSE de MEMÓRIA (ou CLASSE de ARMAZENAMENTO) : determina o tempo de vida e a área de memória na qual a VARIÁVEL é alocada.
Variáveis também podem ter:
  • um TIPO de ACESSO : determinam a maneira como as variáveis podem ser acessadas ou modificadas.
Variáveis podem ser declaradas tanto fora quando dentro de funções. No primiro caso as variáveis são ditas GLOBAIS; no segundo caso, LOCAIS.

Declaração de Variáveis:

Em C, uma DECLARAÇÃO de VARIÁVEL assume a seguinte forma geral:
ESPEC-DECLARAÇÃO
              DECLARADOR-1 = INICIALIZADOR-1, 
              ...,  
              DECLARADOR-n = INCIALIZADOR-n;  
Onde:

ESPEC-DECLARAÇÃO é uma sequência composta de pelo menos um dentre três categorias de ESPECIFICADOR:
  • ESPECIFICADOR do TIPO de DADO;
    • int, char, float, double e seus MODIFICADORES long, short, signed e unsigned
  • ESPECIFICADOR da CLASSE de MEMÓRIA;
    • auto, register, static e extern
  • ESPECIFICADOR do TIPO de ACESSO;
    • const e volatile

A forma mais simples de um DECLARADOR é um IDENTIFICADOR. Nesta aula, iremos considerar como DECLARADOR apenas IDENTIFICADORES.

Um INICIALIZADOR, de maneira geral, consiste em uma EXPRESSÃO.

Ambiente de Execução C:

Em C, há quatro locais onde uma variável pode ser alocada:
  • na ÁREA de DADOS ESTÁTICA
  • na PILHA de EXECUÇÃO
  • em REGISTRADORES
  • no HEAP




Especificadores da Classe de MEMÓRIA:

Em C, há quatro PALAVRAS RESERVADAS que funcionam como ESPECIFICADORES da CLASSE de MEMÓRIA durante a DECLARAÇÃO de uma VARIÁVEL:
  • auto - define variáveis automáticas na PILHA de EXECUÇÃO
    • são sempre variáveis locais a um BLOCO e que são descartados na saída do BLOCO;
  • register - define variáveis automáticas em REGISTRADORES
    • são sempre variáveis locais a um BLOCO e que são descartados na saída do BLOCO;
  • static
    • podem ser variáveis locais a um BLOCO ou externas a todos os BLOCOS
    • retêm seus valores durante toda execução do programa pois são alocadas na ÁREA de DADOS ESTÁTICA
    • quando em DECLARAÇÕES LOCAIS, DEFINE variáveis estáticas (escopo de BLOCO)
    • quando em DECLARAÇÕES GLOBAIS, DEFINE variáveis estáticas com ligação interna (escopo de UNIDADE DE COMPILAÇÃO)
  • extern
    • podem ser variáveis locais a um BLOCO ou externas a todos os BLOCOS
    • quando em DECLARAÇÕES LOCAIS, DECLARA variáveis estáticas que foram definidas fora do BLOCO das declarações
    • quando em DECLARAÇÕES GLOBAIS, DECLARA variáveis estáticas com ligação externa, i.e, que foram definidas fora da UNIDADE DE COMPILAÇÃO das declarações

Observação:
  • um BLOCO é qualquer parte de um programa delimitada por { }; um BLOCO pode ser o corpo de função bem como o corpo de um comando composto como if, while, etc.

A seguir ilustram-se a utilização de Variáveis auto, register e static LOCAIS. Variáveis static GLOBAIS e variáveis extern serão discutidas em aulas futuras.

Atividade 1: Variáveis auto e register:

Para ilustrar o uso de auto e register, compile e execute o programa abaixo.

/* teste_fat.c ( rev 1.1 )
**/

/* função fat:
   calcula fatorial de x 
**/
unsigned long fat(unsigned short x) {
    register int  i;   /* acesso o mais rápido possível; se não possível em pilha */
    unsigned long ret; /* alocada na pilha; se possível acesso o mais rápido possível */

    ret = 1;
    for ( i = 1; i <= x; i++ )
        ret = ret * i;

    return ret;
}

/* função main:
   testa a função fat acima 
**/

int main() {
    auto int i;  /* alocada na pilha */

    for( i = 0; i < 20; i++ )
        printf("fat(%i) = %lu \n",i,fat(i));

}

A Fazer - No programa acima:
  • declare uma VARIÁVEL GLOBAL especificando auto como CLASSE de ARMAZENAMENTO, compile e observe as mensagens geradas pelo compilador;
  • declare uma VARIÁVEL GLOBAL especificando register como CLASSE de ARMAZENAMENTO, compile e observe as mensagens geradas pelo compilador;

Que conclusões podemos tirar sobre o uso de auto e register em DECLARAÇÕES GLOBAIS?

Atividade 2: Variáveis static em DECLARAÇÕES LOCAIS:

Para ilustrar o uso dos ESPECIFICADORES static em DECLARAÇÕES LOCAIS podemos refatorar o programa anterior da seguinte maneira:
  • para n > 12, fat(n) estoura a representação unsigned long int
  • assim, uma primeira medida deve ser barrar o cálculo de fat(n) para n>12
  • dado que o número de valores possíveis é pequeno, uma segunda idéia é armazenar os valores já calcular para evitar repetir cálculos em chamadas futuras.

/* teste_fat.c  (rev 1.2)
**/

/* função fat:
   para x <=12, calcula fatorial de x
   para x > 12, retorna 0
**/

unsigned long fat(unsigned short x) {
    register int                i;
    static   unsigned long      ret[13];

    if ( x > 12 )
        return 0;

    if ( ret[x] != 0 )
        return ret[x];

    ret[x] = 1;
    for ( i = 1; i <= x; i++ )
        ret[x] = ret[x] * i;

    return ret[x];
}

/* função main:
   testa a função fat acima 
**/

int main() {
    int i; 

    for( i = 0; i < 20; i++ )
        printf("fat(%i) = %lu \n",i,fat(i));

}

Em DECLARAÇÕES LOCAIS, o especificador static é usado para indicar uma variável que é DECLARADA LOCALmente mas que irá residir na área de VARIÁVEIS GLOBAIS. Há duas conseqüências:
  • por se tratar de uma declaração local, a variável só é visível no BLOCO onde foi declarada
    • por exemplo, a variável static unsigned long ret[12] só é visível dentro da função fat
  • por residir na área de variáveis globais, a variável reterá seu valor entre ativações sucessivas do BLOCO onde foi declarada
    • por exemplo, a variável static unsigned long ret[12] retém seus valores mesmo quando a função fat não está executando. Assim, na primeira vez que se chama fat(5), o valor correspondente é calculado e armazenado em ret[5]. Em uma segunda chamada fat(5), ao invés de se realizar o mesmo cálculo novamente, o valor é recuperado de ret[5]. Em outras palavras, a função passa a "lembrar" cálculos anteriores!

A Fazer - No programa acima:
  • na DECLARAÇÃO da variável ret, elimine o ESPECIFICADOR static, compile, execute o programa e verifique o funcionamento do programa. Explique a saída gerada!

Leitura Recomendada:

  • CCT capítulo 2
  • K&R capítulos 2 e 4
  • Franek, Frantisek (2003) "Memory as a Programming Concept in C and C++ ." Cambridge University Press.

Exercícios:

  • Conside o seguinte programa
int i;

void g() {
 static int j;
 j = i;
 i = i * 2;
 printf("g(){ i = %d  j = %d }\n",i,j);
}

void f() {
 i = 2;
 g();
 printf("f() { i = %d }\n",i);
}

int main() {
 int i;

 i = 3;
        f();
        g();
 printf("main() { i = %d }\n",i);
 return 0;
}
    • Qual a saída gerada na tela quando o programa é executado?
    • Descreva passo a passo como o ambiente de execução (área estática de dados + pilha de execução) evolui durante a execução do programa.
  • Utilizando variáveis auto e register, escreva programas para calcular:
    • exponenciação inteira, i.e, a ^ b, onde b é um número inteiro.
    • raiz quadrada de x, utilizando o método de newton ( onde r(x) representa a raiz de x)
    • cosseno, utilizando a série
      • utilize a função fat() desenvolvida na atividade 2
      • veja como exemplo o Programa seno discutido em sala de aula

Bibliografia e fonte:

  • [CCT] Schildt, H. (1996) C, completo e total: 3a Ed.. São Paulo, Makron.
  • LP, UFMA; Coutinho, Lucian. Linguagem de programação para ciencia da computação da ufma.http://www.deinf.ufma.br/~lrc/2009.1/LP/
  • [K&R] KERNIGHAN, B. e RITCHIE, D. (1990) C, a linguagem de programação: padrão ANSI. Rio de Janeiro: Campus.
  • DEITEL, H. M. (1999) Como programar em C. Rio de Janeiro: LTC.
  • Módulo Consultoria e Informática (1989) Linguagem C: programação e aplicações. Rio de Janeiro: LTC.