Sobre Variáveis, Ponteiros, Arrays, Parâmetros e Alocação de Memória Queridos, Este texto ainda não está terminado. Ainda vou mudar alguns trechos e ajeitar a formatação, principalmente dos exemplos. Ainda assim, preferi divulgar essa versão temporária para que vocês possam estudar para a prova de terça-feira. O texto trata também de alguns assuntos que vocês ainda não aprenderam. Por ora, podem pular as partes que falam de linguagens forte e fracamente tipadas, ponteiros e alocação de memória. Como sempre, fico a disposição para tirar qualquer dúvida que possa surgir sobre o texto. Boa prova! Variáveis Cordialmente, Lucas Frucht Por trás de todos os demais conceitos que serão abordados nesse texto está o conceito de iável. Embora seja um conceito relativamente simples (ou talvez, justamente, por ser simples), ele não costuma ser explorado em profundidade. Dominar este conceito é a chave para entender os demais. Declarar uma iável em seu código-fonte é dizer ao compilador que ele deve reser um espaço na memória com um determinado tamanho (que depende do tipo da iável) e interpretar o conteúdo daquele espaço como sendo de um determinado tipo. A segunda é mais importante nas linguagens fortemente tipadas. O nome que damos à iável é um rótulo com o qual a identificamos para o compilador. program iavel; a : integer; b : char; c : real; 0000 0000 b 00c4 5d0 c 00c4 5d0 00c4 5d04 00c4 5d06 Uma vez que o espaço é reservado, ele poderá ser usado para armazenar diferentes valores ao longo da execução do programa. Sempre que usamos uma iável o compilador acessa a posição da memória associada àquele rotulo para obter seu conteúdo atual. Quando atribuímos um novo valor a ela, o compilador armazena o valor na posição de memória associada ao rotulo.
Vale lembrar que não existe posição de memória vazia. Quando o compilador reserva o espaço na memória ele continuará com a informação que estava lá antes do programa ser executado. Por esse motivo, nunca podemos assumir que uma iável guarda um determinado valor sem antes inicializá-la (dar a ela um valor inicial). program iavel; a : integer; b : char; c : real; 0 b 00c4 5d0? a := 0; c 00c4 5d0 00c4 5d04? 00c4 5d06? Pascal é uma linguagem fortemente tipada. Suas iáveis dos tipos byte e char, por exemplo, têm o mesmo tamanho, mas seus conteúdos são interpretados de forma distinta. O tipo byte é usado para armazenar números, enquanto o tipo char é usado para armazenar caracteres. A linguagem não permite que iáveis do tipo byte sejam usadas em funções (e procedimentos) criadas para manipular caracteres. Da mesma forma, não é permitido que iáveis do tipo char sejam usadas em funções (e procedimentos) criadas para manipular números, nem em expressões aritméticas. Qualquer um desses usos inadequados irá resultar em um erro de compilação. C é uma linguagem fracamente tipada. Não há nenhum outro tipo nativo cujas iáveis tenham o mesmo tamanho das iáveis do tipo char. Variáveis desse tipo podem ser usadas tanto para armazenar caracteres quanto números e cabe ao desenvolvedor garantir que as funções que manipularão essas iáveis interpretarão seu conteúdo de forma adequada. É possível, inclusive, somar o caractere 1 com o caractere ; e o resultado não será o caractere (tipicamente será o caractere c ). Já se o caractere a for somado com o número 1 o resultado, tipicamente, será b 1. Ponteiros Embora exista uma grande mítica em torno deles, os ponteiros não são nada além de mais um tipo de iável. A única diferença está no modo como o compilador interpreta o conteúdo de iáveis desse tipo. Ao invés de interpretar como um caractere, um número inteiro ou um decimal, o conteúdo de ponteiros será interpretado como um endereço de memória. Por isso, que se diz que um ponteiro aponta para uma posição de memória. As linguagens que possuem ponteiros oferecem operadores para permitir que seja acessado o conteúdo da posição de memória que está armazenada em um ponteiro. Adicionalmente, o tipo do ponteiro indica o 1 Foram considerados resultados típicos os que são obtidos quando é usada uma tabela de codificação baseada no ASCII, como é a maioria das utilizadas atualmente. Em linguagens fracamente tipadas um número inteiro ou real pode ser interpretado com um ponteiro e vice-versa.
tamanho e como deve ser interpretado este conteúdo. Essas linguagens oferecem também operadores que indicam o endereço de uma iável. program ponteiros; a, b, c : integer; ptrb, ptrc : ^integer; a := ; ptra := @a; ptrb := @b; ^ptrb := ; c := ^ptra; b 00c4 5d0 00c4 5d0 c 00c4 5d04 ptra 00c4 5d06 00c4 5d08 00ca 5d00 00c4 5d09 ptrb 00c4 5d0a 00c4 5d0b 00c4 5d0c 00c4 5d0 00c4 5d0d Arrays Um array é um conjunto de iáveis de um mesmo tipo. Geralmente elas são dispostas seqüencialmente na memória. Desta forma, supondo que um inteiro ocupe 4 bytes, quando se declara um array de 10 inteiros, se está indicando que o compilador deve reser 40 bytes de memória e interpretar cada 4 bytes como um número inteiro. O nome dado ao array é um rotulo para o endereço da primeira posição do array, ou de um ponteiro para sua primeira posição. O operador que indica as posições dentro do array indica, na verdade, um deslocamento a partir do endereço inicial. program ponteiros; a : array [1..5] of integer; b : integer; for b := 1 to 5 do a[b] := b * ; 00c4 5d0 00c4 5d0 4 00c4 5d04 6 00c4 5d06 8 00c4 5d08 00c4 5d09 10 b 00c4 5d0a 00c4 5d0b 1 4 5 00c4 5d0c? 00c4 5d0d?
Alocação de Memória Como já foi visto, para toda iável e todo array declarado no seu programa o compilador separa um espaço na memória onde o conteúdo será armazenado. O tamanho total desse espaço deve ser conhecido no momento que o código for compilado. Sempre que o programa for carregado na memória esse espaço será reservado. Esta alocação é chamada estática. Passagem de Parâmetros Os parâmetros passados para uma função (ou procedimento) são a comunicação de quem está chamando a função para a função. Devem estar na lista de parâmetros todas as informações que a função deve receber para funcionar de forma genérica. Embora, em teoria, essa comunicação possa se dar através de iáveis globais, essa não é considerada uma boa prática pois deixa o código mais confuso e dificulta o reaproveitamento de código. Variáveis locais, que são usadas internamente para a operação da função e não tem sentido fora dela, não devem estar na lista de parâmetros. Por outro lado, os parâmetros declarados já são iáveis locais da sua função e não podem ser redeclarados no corpo dela. A comunicação de volta da função para quem a chamou costuma se dar através de um valor de retorno. No Pascal, este valor é definido em uma iável com o mesmo nome da função. Diferentemente de Pascal, a maioria das linguagens não faz distinção entre funções e procedimentos, considerando um procedimento como uma função que não retorna valor algum. Por esse motivo, as explicações serão dadas com base em funções, mas, com exceção do valor de retorno, tudo se aplica de forma idêntica aos procedimentos.
program ponteiros; function somatorio( a : array of integer; i : integer; limite : integer) : integer; i : integer; limite : integer; somatorio := 0; for i := 0 to limite -1 do somatorio := somatorio + a[i]; end; a : array[0..] of integer; b : integer; a[0] := 4; a[1] := 7; a[] := ; b := ; b := somatorio(a, b); Existem duas formas de passagem de parâmetros. A passagem por valor consiste em copiar o valor de uma iável (ou constante) para o parâmetro correspondente na função. Esta correspondência costuma se dar pela ordem dos parâmetros na declaração e na chamada da função. 4 Caso a função altere o valor dos parâmetros ela estará manipulando uma iável completamente diferente, logo, essa alteração não terá reflexo em quem chamou a função. Já na passagem por referência a função não recebe o valor da iável, mas sua referência, ou seja o local onde ela está armazenada. Desta forma, ao invés de se criar uma iável nova, se está dando apenas um novo nome para a iável que já existe. Esse nome é válido apenas dentro da função, mas permite que ela manipule diretamente a iável original. Caso a função altere o valor de um parâmetro passado por referência, a alteração acontece também onde a função foi chamada. Desta forma a passagem por referência serve também como uma comunicação indireta da função para quem a chamou. 4 Algumas linguagens trabalham com parâmetros nomeados, não importando a ordem com que eles são passados.
program ponteiros; procedure s1(x, y, z : integer); z := x + y; end; procedure s(x, y : integer; z : integer); z := x + y; end; a, b, c, d : integer; a := ; b := ; s1(a, b, c); s(a, b, d); s1.x 00c4 5d00 s1.y 00c4 5d0 00c4 5d0 s1.z 00c4 5d04 5 s.x 00c4 5d06 s.y 00c4 5d08 00c4 5d09 a 00c4 5d0a 00c4 5d0b b 00c4 5d0c 00c4 5d0d c 00c4 5d0e 00c4 5d0f? s.z d 00c4 5d10 00c4 5d11 5