Escolar Documentos
Profissional Documentos
Cultura Documentos
Prova do 1° bim.
30/11/2010,Rio de Janeiro
Apontadores: Ponteiro (programação)
Em programação, um ponteiro ou apontador é um tipo de dado de uma linguagem de
programação cujo valor se refere diretamente a um outro valor alocado em outra área da
memória, através de seu endereço. Um ponteiro é uma simples implementação do
tipo referência da Ciência da computação.
Arquitetura
Ponteiros são uma abstração da capacidade de endereçamento fornecidas
pelas arquiteturas modernas. Em termos simples, um endereço de memória, ou índice
numérico, é definido para cada unidade de memória no sistema, no qual a unidade é
tipicamente um byte ou uma Word, o que em termos práticos transforma toda a memória em
um grande vetor Logo, a partir de um endereço, é possível obter do sistema o valor
armazenado na unidade de memória de tal endereço. O ponteiro é um tipo de dado que
armazena um endereço.
Na maioria das arquiteturas, um ponteiro é grande o suficiente para indexar todas as unidades
de memória presentes no sistema. Isso torna possível a um programa tentar acessar um
endereço que corresponde a uma área inválida ou desautorizada da memória, o que é
chamado de falha de segmentação. Por outro lado, alguns sistemas possuem mais unidades
de memória que endereços. Nesse caso, é utilizado um esquema mais complexo para acessar
diferentes regiões da memória, como o de segmentação ou paginação.
Para fornecer uma interface consistente, algumas arquiteturas fornecem E/S mapeada
em memória, o que permite que enquanto alguns endereços são referenciados como
áreas de memória, outros são referenciados como registradores de dispositivos do
computador, como equipamentos periféricos.
Usos de Ponteiros
Ponteiros são diretamente suportados sem restrições em C, C++, D e Pascal, entre outras
linguagens. São utilizados para construir referências, elemento fundamental da maioria
das estruturas de dados, especialmente aquelas não alocadas em um bloco contínuo de
memória, como listas encadeadas, árvores ou grafos.
Ao lidar com arranjos, uma operação crítica é o cálculo do endereço para o elemento desejado
no arranjo, o que é feito através da manipulação de ponteiros. De fato, em algumas linguagens
(como C), os conceitos de "arranjo" e "ponteiro" são intercambiáveis. Em outras estruturas de
dados, como listas encadeadas, ponteiros são usados como referências para intercalar cada
elemento da estrutura com seus vizinhos (seja anterior ou próximo).
30/11/2010,Rio de Janeiro
Ponteiros também são utilizados para simular a passagem de parâmetros por referência em
linguagens que não oferecem essa construção (como o C). Isso é útil se desejamos que uma
modificação em um valor feito pela função chamada seja visível pela função que a chamou, ou
também para que uma função possa retornar múltiplos valores.
Linguagens como C, C++ e D permitem que ponteiros possam ser utilizados para apontar para
funções, de forma que possam ser invocados como uma função qualquer. Essa abordagem é
essencial para a implementação de modelos de re-chamada (callback), muito utilizados
atualmente em bibliotecas de rotina para manipulação de interfaces gráficas. Tais ponteiros
devem ser tipados de acordo com o tipo de retorno da função o qual apontam. Ponteiros para
função se assemelham a functores, ainda que o conceito função objeto seja mais abrangente.
Exemplos
Abaixo é mostrado o exemplo da declaração de uma lista encadeada em C, o que não seria
possível sem o uso de ponteiros:
#define LISTA_VAZIA NULL /* a lista encadeada vazia é representada por
NULL */
struct link {
void *info; /* conteúdo do nó da lista */
struct link *prox; /* endereço do próximo nó da lista; LISTA_VAZIA
se este é o último nó */
};
Note que essa definição recursiva de ponteiros é a mesma que a definida em Haskell:
data Link a = Nil {- lista vazia -}
| Cons a (Link a) {- a cons nó de um valor de tipo a e
outro nó -}
A definição com referências, por outro lado, possui checagem de tipo, sem confusão de
símbolos. Por essa razão, estruturas de dados em C geralmente são utilizadas com o auxílio de
funções de encapsulamento, que são cuidadosamente verificadas contra erros. A mesma
definição de lista encadeada pode ser codificada em Fortran utilizando ponteiros, como segue:
type real_list_t
real :: sample_data(100)
type (real_list_t), pointer :: next => null ()
end type
30/11/2010,Rio de Janeiro
allocate (real_list_temp%next)
real_list_temp => real_list_temp%next
end do
int main() {
int arranjo[5] = { 2, 4, 3, 1, 5 };
return 0;
}
Ponteiros podem ser usados para passar variáveis por referência, permitindo que seus valores
modificados tenham efeito no escopo anterior do programa, como exemplificado no código C
abaixo:
#include <stdio.h>
int main() {
int x = 24;
int *endereco= &x; /* o operador '&' (leia-se "referênca") retorna
o endereço de uma variável */
30/11/2010,Rio de Janeiro
printf("%p %p\n", endereco, &x); /* note que o endereço de x não
foi alterado */
return 0;
}
Ponteiros podem ser usados para apontar para funções, permitindo, por exemplo, a passagem
de funções como parâmetro de outras funções. O código em C abaixo demonstra tal
funcionalidade:
#include <stdio.h>
int main()
{
lista *L;
30/11/2010,Rio de Janeiro
return 0; /* retorno bem sucedido */
}
Note que, no exemplo acima, a rotina para a execução de código para cada elemento da lista
(técnica conhecida como mapeamento) é implementada somente uma vez. O mapeamento é
novamente exemplificado abaixo em uma rotina para determinar se elementos em uma lista
são pares ou ímpares, codificado em ML:
fun map(F, nil) = nil
| map(F, x::xs) = F(x)::map(F, xs);
A função map() recebe como entrada um valor e uma lista. Neste caso, o valor passado é uma
função (x mod 2, uma implementação utilizando cálculo lambda para retornar se dado valor é
par). Tal função é mapeada recursivamente, sendo executada em cada elemento da lista.
]Situações de uso
Os ponteiros são necessários para a alocação dinâmica de memória, para seqüências de
dados alocados e para a passagem ou o retorno através referência. Neste último é importante
citar a relevância no desempenho, pois é muito mais rápido alocar um ponteiro para um objeto
de memória já existente do que alocar o objeto inteiro novamente. Além do mais, alocando o
objeto novamente não podemos alterar o original.
Apesar disso, poucas linguagens definem tipagem restrita de ponteiros, pois programadores
freqüentemente se encontram em situações nas quais desejam tratar um objeto de um tipo
como se tivesse outro. Nesses casos, é possível converter o tipo de um ponteiro. Algumas
conversões são sempre seguras, enquanto outras são perigosas, possivelmente resultando em
comportamento incorreto do sistema. Apesar de geralmente ser impossível determinar
em tempo de compilação se tais conversões são seguras, algumas linguagens armazenam
informações sobre tipagem em tempo de execução (como o processo RTTI do C++), que
podem ser usadas para confirmar se tais conversões perigosas são válidas, em tempo de
execução. Outras linguagens simplesmente aceitam uma aproximação conservadora de
conversões seguras, ou apenas não aceitam conversões.
30/11/2010,Rio de Janeiro
Perigos na utilização de ponteiros
Como ponteiros permitem ao programa acessar objetos que não são explicitamente declarados
previamente, permitem uma variedade de erros de programação. Apesar disso, o poder
fornecido por eles é tão grande que existem tarefas computacionais que são difíceis de ser
implementadas sem sua utilização. Para ajudar nesse aspecto, várias linguagens criaram
objetos que possuem algumas das funcionalidades úteis de ponteiros, ainda que evitando
alguns tipos de erro.
Um grande problema com ponteiros é que enquanto são manipulados como números, podem
apontar para endereços não utilizados, ou para dados que estão sendo usados para outros
propósitos. Várias linguagens, incluindo a maioria das linguagens funcionais e linguagens
recentes, como C++ e Java, trocaram ponteiros por um tipo mais ameno de referência.
Tipicamente chamada de "referência", pode ser usada somente para referenciar objetos sem
ser manipulada como número, prevenindo os tipos de erros citados anteriormente. Índices de
vetores são lidados como um caso especial. As primeiras versões de Fortran e Basic omitiam
completamente o conceito de ponteiros.
Ponteiro selvagem
Um ponteiro selvagem (também chamado de apontador pendente) não possui
endereço associado. Qualquer tentativa em usá-lo causa comportamento indefinido, ou
porque seu valor não é um endereço válido ou porque sua utilização pode danificar partes
diferentes do sistema.
Algumas linguagens, como C++, suportam ponteiros inteligentes (smart pointers), que
utilizam uma forma simples de contagem de referências para ajudar no rastreamento de
alocação de memória dinâmica, além de atuar como referência.
Ponteiro nulo
Um ponteiro nulo possui um valor reservado, geralmente zero, indicando que ele
não se refere a um objeto. São usados freqüentemente, particularmente em C e C+
+, para representar condições especiais como a falta de um sucessor no último
elemento de uma lista ligada, mantendo uma estrutura consistente para os nós da
lista. Esse uso de ponteiros nulos pode ser comparado ao uso de valores nulos
30/11/2010,Rio de Janeiro
embancos de dados relacionais e aos
valors Nothing e Maybe em mónadas da programação funcional. Em C, ponteiros de
tipos diferentes possuem seus próprios valores nulos, isto é, um ponteiro nulo do
tipo char e diferente de um ponteiro nulo do tipo int.
Um ponteiro nulo não pode ser confundido com um ponteiro não inicializado: ele
possui um valor fixo, enquanto um ponteiro não inicializado pode possuir qualquer
valor. Uma comparação entre dois ponteiros nulos distintos sempre
retorna verdadeiro.
Exemplos
O seguinte exemplo demonstra um ponteiro selvagem:
int main(void)
{
char *p1 = (char *) malloc(sizeof(char)); // aloca memória
e inicializa o ponteiro
printf("p1 aponta para: %p\n", p1); // aponta para
algum lugar da memória heap
printf("Valor de *p1: %c\n", *p1); // valor
(indefinido) de algum lugar na memória heap
return 0;
}
30/11/2010,Rio de Janeiro
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int y = 0;
int *p1 = &y; // ponteiro
inicializado para y
int *p2 = NULL; // um bom hábito
a ser utilizado
printf("Endereco dep1: %p\n", p1); // imprime o
endereço de y
printf("Valor de *p1: %d\n", *p1); // imprime o
valor de y
y = maIdeia(&p1); // muda y e muda
p1
return 0;
}
Um problema muito comum é usar um ponteiro para a memória heap depois que a
memória foi deslocada, como no próximo exemplo:
#include <stdio.h>
30/11/2010,Rio de Janeiro
#include <stdlib.h>
int main(void)
{
int *p1 = (int *)malloc(sizeof(int)); // inicializa o
ponteiro para a memória heap
int *p2 = p1; // faz uma cópia
*p1 = 0; // inicializa o
valor
printf("Endereco de p2: %p\n", p2); // aponta para a
heap
printf("Valor de *p2: %d\n", *p2); // deve imprimir
0
free(p1); // desaloca a
memória
.... // algum outro código, possivelmente utilizando a
heap
return 0;
}
O monitoramento busca inferir o que fazem as máquinas virtuais que compõem a nuvem. Um possível
cenário de ameaça pode ocorrer, por exemplo, se um hacker conseguir instalar uma máquina virtual em
um dos servidores físicos que formam a nuvem.
Segundo a IBM, a introspecção de máquinas virtuais (VMI) poderá inspecionar e modificar o estado de
uma máquina virtual mesmo sem ter acesso a ela. As ações são efetuadas a partir de um “hipervisor”.
Com essas informações, o sistema de segurança em nuvem terá como saber se uma máquina virtual foi
“comprometida”.
30/11/2010,Rio de Janeiro
O conceito de computação em nuvem (em inglês, cloud computing) refere-se à utilização
da memória e das capacidades de armazenamento e cálculo de
computadores e servidores compartilhados e interligados por meio da Internet, seguindo o
princípio da computação em grade.
O armazenamento de dados é feito em serviços que poderão ser acessados de qualquer lugar
do mundo, a qualquer hora, não havendo necessidade de instalação de programas x ou de
armazenar dados. O acesso a programas, serviços e arquivos é remoto, através da Internet -
daí a alusão à nuvem. O uso desse modelo (ambiente) é mais viável do que o uso de unidades
física.
Bibliografia:
30/11/2010,Rio de Janeiro
→ http://pt.wikipedia.org/wiki/Ponteiro_(programa%C3%A7%C3%A3o)
→ http://www.clickweb.com.br/noticias.asp?cod=3978&pagina=2)
→ http://pt.wikipedia.org/wiki/Computa%C3%A7%C3%A3o_em_nuvem
30/11/2010,Rio de Janeiro