fp registros
DESCRIPTION
FPTRANSCRIPT
-
Fundamentos de ProgramacaoTipos de Dados Compostos Heterogeneos
Depto. de Estatstica e Matematica AplicadaUniversidade Federal do Ceara
Semestre 2015.1
Profssor: Tiberius O. Bonates ([email protected])
1 Registros
O registro (ou tipo composto heterogeneo) e um recurso da linguagem que permite a criacao deum novo tipo de dados, alem dos tipos primitivos (por exemplo, bool, float) e do tipo compostohomogeneo (vetor). O registro permite ao programador agregar duas ou mais variaveis (cujos tipospodem ser tipos basicos da linguagem ou outros tipos compostos) que possuem alguma relacao logicaentre si. Em geral, um registro e utilizado com o proposito de armazenar conjuntamente duas oumais variaveis que possuem uma relacao logica entre si. Como exemplo, considere o armazenamentode dois numeros reais que representam um par ordenado. Uma opcao e proceder da seguinte forma:
int main() {
float x; // valor da abcissa
float y; // valor da ordenada
return 0;
}
Contudo, neste caso, as duas variaveis nao estao formalmente associadas entre si no programa.A unica associacao existente entre as mesmas e uma associacao informal, feita mentalmente peloprogramador. Este tipo de situacao e uma porta de entrada para erros no programa, especialmentequando temos muitas variaveis e um codigo longo. Afinal, apos algumas paginas de codigo, pode serdifcil lembrar que x e y nao eram variaveis individuais, mas duas variaveis que estavam intrinsica-mente ligadas para formar um par ordenado.
O registro e um recurso de C/C++ que oferece uma alternativa para essa situacao. O registro eum novo tipo de variavel que passa a estar disponvel para o programador, da mesma forma que umint, um float, etc. No caso de um par ordenado, podemos chamar este novo tipo de ParOrdenado,ja que este nome remete diretamente ao conceito que desejamos representar. O registro e umaentidade unica, que contem duas variaveis de ponto flutuante como partes integrantes. Eis suadefinicao e uma declaracao de variavel do novo tipo:
// Definicao do novo tipo:
struct ParOrdenado {
float x; // valor da abcissa
float y; // valor da ordenada
};
1
-
int main() {
// Declaracao de uma variavel do novo tipo ParOrdenado
ParOrdenado par; // "par" e uma variavel do tipo ParOrdenado
return 0;
}
Naturalmente, ao se criar um registro e desejavel se acessar as variaveis individuais que o consti-tuem, que sao chamadas de campos ou atributos do registro. Este acesso acontece por meio deum deslocamento relativo a` posicao inicial onde o registro esta armazenado, similar ao que acontecedurante o acesso a`s posicoes de um vetor. Na definicao de um registro, os identificadores e os tiposde todos os campos que compoem o registro sao especificados. Na manipulacao de um registro, oscampos constituintes podem ser acessados por meio de seus identificadores. Em C/C++, a sintaxee: .. A seguir, mostramos uma versaomais elaborada do exemplo acima:
struct ParOrdenado {
float x; // valor da abcissa
float y; // valor da ordenada
};
int main() {
ParOrdenado par; // "par" e uma variavel do novo tipo ParOrdenado
par.x = 5.2; // identificador da variavel: "par"
// identificador do campo: "x"
par.y = 3.01; // identificador da variavel: "par"
// identificador do campo: "y"
cout
-
Embora nao seja possvel realizar operacoes de soma, produto, etc, automaticamente com regis-tros, e sempre possvel realizar explicitamente a operacao desejada, utilizando-se de acesso direto aoscampos do registro. No codigo a seguir, por exemplo, mostramos como armazenar em um registroParOrdenado P3 o resultado da soma de dois outros registros do tipo ParOrdenado, P1 e P2. Nesteexemplo, estamos considerando que a soma de dois pares ordenados, p1 e p2, e um par ordenado p3obtido a partir da soma das coordenadas correspondentes de p1 e p2.
struct ParOrdenado {
float x; // valor da abcissa
float y; // valor da ordenada
};
int main() {
ParOrdenado P1, P2, P3;
P1.x = 5.2; P1.y = 3.01;
P2.x = 0.7; P2.y = 12.0;
P3.x = P1.x + P2.x;
P3.y = P1.y + P2.y;
cout
-
1.1 Inicializacao
Ate agora, atribuimos valores aos campos de um registro utilizando o operador ponto (.), ou porintermedio do operador de atribuicao (=). E possivel fazer a inicializacao de um registro de umaso vez, fornecendo uma lista de valores, de forma similar ao que aprendemos a fazer com vetores.Os valores de tal lista sao atribudos aos campos do registro em questao na mesma ordem em que oscampos sao definidos, conforme exemplo a seguir. Note que a linguagem verifica se os valores em tallista sao compatveis com os tipos dos respectivos campos do registro. Portanto, e importante teratencao a` ordem em que os campos sao especificados na definicao do registro e aos tipos dos valoresque colocamos nesta lista. A inicializacao via lista de valores funciona apenas no ato da criacao davariavel; nao depois. O exemplo da variavel beltrano abaixo ilustra esse caso.
struct Pessoa {
int idade;
string sobrenome;
};
int main() {
Pessoa fulano = {19, "Andre"};
Pessoa ciclano = {"Barnabe", 20}; // Isso resulta em erro!
Pessoa beltrano;
beltrano = {19, "Andre"}; // Isso tambem resulta em erro!
return 0;
}
1.2 Registros Auto-Referenciados
Um registro pode conter, como um de seus campos, outro registro. No entanto, em C++ nao epermitida a declaracao de um registro que contenha um campo do mesmo tipo do registro (ou umcampo de um tipo que contenha uma variavel do tipo do registro). A razao para este fato e quenao haveria uma forma razoavel de se representar uma estrutura desta natureza em memoria. Noentanto, e possvel se construir registros que possuem ponteiros para registros do mesmo tipo.
Um registro com esta caracterstica e conhecido como um registro auto-referenciado. Registrosauto-referenciados sao muito comuns nas definicoes de estruturas de dados dinamicas, tais comolistas encadeadas, arvores, grafos, etc. A seguir, fornecemos um exemplo de um registro que poderiaser utilizado para a implementacao de uma lista simplesmente encadeada, conforme veremos maisadiante.
struct Objeto {
int dado;
Objeto * proximo;
};
O campo proximo possui uma variavel do tipo ponteiro que aponta para outro registro do tipoObjeto. Desta forma, embora o registro nao possua, de fato, uma variavel do tipo Objeto dentrode si, ele possui uma referencia a um registro Objeto externo. Este artifcio permite a definicaode uma sequencia de registros, na qual um registro faz referencia a outro registro, que por sua vezreferencia outro registro, e assim por diante. Varias estruturas de dados sao definidas por meio douso de registros auto-referenciados similares a Objeto.
4
-
No caso de um registro X possuir um membro do tipo registro Y, e de o registro Y conter comomembro um ponteiro para um registro do tipo X, temos uma situacao conhecida como referenciacircular, onde X precisa conhecer a definicao de Y, e Y precisa conhecer a declaracao1 de X. Isto e, Ynao precisa conhecer os campos de X, mas precisa saber que existe um tipo de registro com o nomeX. Isto e feito atraves da declaracao do nome de um registro de forma antecipada a` sua definicao.Desta maneira, durante a definicao do registro Y o compilador ja conheceria o nome do registro Xpara o qual Y conteria um ponteiro. O codigo abaixo exemplifica a situacao:
struct X; // declaracao antecipada
struct Y {
int dado;
X *ponteiro;
};
struct X {
int informacao;
Y outro;
};
Note aqui a diferenca entre a inclusao de um membro que e um registro de certo tipo e a inclusaode um ponteiro para um registro daquele tipo. No primeiro caso, o compilador precisa conhecera exata definicao do registro para reservar o espaco de memoria necessario para a criacao de umavariavel daquele tipo de registro. No segundo caso, nao e necessario conhecermos exatamente adefinicao do tipo (apenas a declaracao de seu nome), ja que ponteiros possuem um tamanho fixo,independente do tipo de estrutura para onde apontam.
2 Enumeracoes (leitura opcional)
Uma enumeracao e um tipo de dado que contem um conjunto de valores nominais. O proposito dese utilizar uma enumeracao e o de permitir a escolha de um valor constante dentre um conjuntolimitado de opcoes. Como exemplo, considere o conjunto de naipes em um baralho. O seguinteexemplo ilustra o conceito:
enum Naipe { OUROS, PAUS, COPAS, ESPADAS };
int main() {
Naipe x = COPAS;
}
Internamente, cada item em um tipo enumeracao e representado como um valor inteiro, comecandoa partir do valor 0 (zero). Desta forma, no codigo acima, OUROS e igual a 0, PAUS e igual a 1, eassim por diante. O seguinte codigo imprimiria a mensagem COPAS igual a 2 na tela:
Naipe x = COPAS;
if (x == COPAS)
cout
-
Um valor do tipo enumeracao Naipe pode ser utilizado como inteiro para proposito de atribuicoes,comparacoes ou mesmo operacoes aritmeticas.
enum Naipe { OUROS = 1, PAUS = 3, COPAS = 5, ESPADAS = 7 };
int main() {
Naipe x = OUROS;
int y = x + 4;
if (y == COPAS) cout