Você está na página 1de 12

CENTRO FEDERAL DE EDUCAÇÃO TECNOLÓGICA-RJ

DEPARTAMENTO DE ENSINO MÉDIO E TÉCNICO


COORDENAÇÃO DE CONSTRUÇÃO CIVIL

Disciplina: Computação Aplicada

Prof.: Emilson Damasceno de Andrade

Aluno: Joshua Sales

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

type (real_list_t), target :: my_real_list


type (real_list_t), pointer :: real_list_temp

real_list_temp => my_real_list


do
read (1,iostat=ioerr) real_list_temp%sample_data
if (ioerr /= 0) exit

30/11/2010,Rio de Janeiro
allocate (real_list_temp%next)
real_list_temp => real_list_temp%next
end do

Vetores em C são somente ponteiros para áreas consecutivas da memória. Logo:


#include <stdio.h>

int main() {
int arranjo[5] = { 2, 4, 3, 1, 5 };

printf("%p\n", arranjo); /* imprime o endereço do arranjo


*/
printf("%d\n", arranjo[0]); /* imprime o primeiro elemento do
arranjo, 2 */
printf("%d\n", *arranjo); /* imprime o primeiro inteiro do
endereço apontado pelo arranjo, que é o primeiro elemento, 2 */
printf("%d\n", arranjo[3]); /* imprime o quarto elemento do
arranjo, 1 */
printf("%p\n", arranjo+3); /* imprime o terceiro endereço após
o início do arranjo */
printf("%d\n", *(arranjo+3)); /* imprime o valor no tercero
endereço após o início do arranjo, 1 */

return 0;
}

Tal operação é chamada aritmética de ponteiros, e é usada em índices de ponteiros. O uso


dessa técnica em C e C++ é discutido posteriormente neste mesmo artigo.

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>

void alter(int *n) {


*n = 120;
}

int main() {
int x = 24;
int *endereco= &x; /* o operador '&' (leia-se "referênca") retorna
o endereço de uma variável */

printf("%d\n", x); /* mostra x */


printf("%p\n", endereco); /* mostra o endereço de x */
alter(&x); /* passa o endereço de x como referência, para
alteração */
printf("%d\n", x); /* mostra o novo valor de x */

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 soma = 0; /* armazena a soma */


int produto = 1; /* armazena o produto */

void fsoma(int valor)


{
soma += valor;
}

void fproduto(int valor)


{
produto *= valor;
}

void mapeamento_funcao_lista(lista *L, void (*funcaoptr)(int))


{
lista_no *no;
no = L->inicio;
while (no != NULL)
{
funcaoptr(no->valor); /* invoca o ponteiro de função */
no = no->proximo;
}
}

int main()
{
lista *L;

/* ... preenche a lista com valores ... */

mapeamento_funcao_lista(L, fsoma); /* calcula o somatório dos


elementos da lista */
mapeamento_funcao_lista(L, fproduto); /* calcula o produtório dos
elementos da lista */

printf("Somatorio: %d\nProdutorio %d\n", soma, produto); /*


imprime na tela os resultados */

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);

map(fn x => x mod 2, [1,2,3,4,5,6,7,8,9]);

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.

Ponteiros tipados e conversão


Em várias linguagens, ponteiros possuem a restrição adicional de apontar para objetos de um
tipo específico de dado. Por exemplo, um ponteiro pode ser declarado para apontar para
um inteiro. A linguagem tentará prevenir o programador de apontar para objetos que não são
inteiros, ou derivados de ponteiros, como números de ponto flutuante, eliminando alguns tipos
básicos de erro cometidos por programadores.

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.

Em sistemas com alocação explícita de memória, é possível tornar um ponteiro inválido


ao desalocar a região de memória apontada por ele. Esse tipo de ponteiro é perigoso e
sutil, pois um região desalocada de memória pode conter a mesma informação que
possuía antes de ser desalocada, mas também pode ser realocada e sobreescrita com
informação fora do escopo antigo. Linguagens com gerenciamento automático de
memória previnem esse tipo de erro, eliminando a possibilidade de ponteiros inválidos e
de vazamentos de memória.

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.

Como se referem ao nada, uma tentativa de utilização causa um erro em tempo de


execução que geralmente aborta o programa imediatamente (no caso do C com
uma falha de segmentação, já que o endereço literalmente aponta para uma região
fora da área de alocação do programa). Em Java, o acesso a uma referência nula
lança a exceção Java.lang.NullPointerException. Ela pode ser verificada,
ainda que a prática comum é tentar se assegurar que tais exceções nunca ocorram.

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

char *p2; // ponteiro


selvagem
printf("Endereco de p2: %p\n", p2); // valor
indefinido, pode não ser um endereço válido

// se você for sortudo, isso irá causar uma exceção de


endereçamento
printf("Valor de *p2: %c\n", *p2); // valor aleatório
em endereço aleatório

free(p1); //A função free libera o espaço de memória usado


pelo ponteiro

return 0;
}

O seguinte exemplo demonstra um ponteiro inválido por mudança de escopo:

30/11/2010,Rio de Janeiro
#include <stdio.h>
#include <stdlib.h>

int maIdeia(int **p) // p é um ponteiro para um ponteiro de


inteiro
{
int x = 1; // aloca um inteiro na pilha
**p = x; // define o valor de x para o inteiro
que p aponta
*p = &x; // faz o ponteiro que p aponta
apontar para x
return x; // após retornar x estará fora de
escopo e indefinido
}

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

// p1 agora aponta para onde x estava


// O lugar onde x estada será sobreescrito,
// por exemplo, na próxima interupção, ou na
// próxima sub-rotima, como abaixo...

// algum outro código que utiliza a pilha


p2 = (int *)malloc(5*sizeof(int));

// isso não irá abortar, mas o valor impresso é


imprevisível
printf("Valor de *p1: %p\n", *p1); // imprime o valor
onde x estava

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

// p2 ainda aponta para o local original da alocação, mas


é impossível saber o que está lá
printf("Valor de *p2: %d\n", *p2); // uso inválido
de p2

return 0;
}

Vigia de Computação em Nuvem


Pesquisadores dos centros de pesquisa da IBM em Nova York (EUA) e Zurique (Suíça) estão
desenvolvendo um sistema de “monitoramento introspectivo” para computação em nuvem, capaz de
detectar e destruir códigos maliciosos.

A tecnologia foi apresentada sexta-feira (13/11) no Workshop de Segurança de Computação em Nuvem


promovido pela Associação de Máquinas para Computação (ACM) em Chicago (EUA), que foi parte da
16.ª Conferência de Segurança em Computadores e Comunicações.

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”.

O que vem a ser computação em nuvem?

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.

Num sistema operacional disponível na Internet, a partir de qualquer computador e em


qualquer lugar, pode-se ter acesso a informações, arquivos e programas num sistema único,
independente de plataforma. O requisito mínimo é um computador compatível com os recursos
disponíveis na Internet. O PC torna-se apenas um chip ligado à Internet -- a "grande nuvem" de
computadores -- sendo necessários somente os dispositivos de entrada (teclado, mouse) e
saída (monitor).

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

Você também pode gostar