Você está na página 1de 7

Sumário

! Introdução à compilação de programas em C


! Endereçamento
  Definições
! Ponteiros
  Definições
  Alocação de memória
  Causas de erros
  Aritmética de ponteiros
! Tabelas/Arrays
LINGUAGEM C   Definições

- PONTEIROS E ARRAYS - ! Arrays VS Ponteiros


! Princípios de alocação dinâmica de memória

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

Programa em C espalhado por ficheiros Compilação : Overview


#include<stdio.h>
Ficheiro main.c

int IntroduzFaltas(); ! O compilador converte C em código máquina (string


int main(){
the 0s e 1s) que é específico da arquitectura.
int total=15, faltas;   Diferente do Java que converte para um bytecode
faltas=IntroduzFaltas(); independente da arquitectura (máquina virtuais).
printf("Vai entao assistir a %d aulas \n",total-faltas);
}   Diferente do Python que interpreta o código permitindo
interactividade.
#include<stdio.h>   Para o C a geração do executável passa normalmente por
Ficheiro intro.c

duas etapas principais:


int IntroduzFaltas(){   A compilação, que converte ficheiros .c (código fonte) em
int tmp;
ficheiros .o (código objecto).
printf("Quantas faltas vai dar? ");
scanf("%d",&tmp); gcc -c main.c
return(tmp); gcc -c intro.c
}
  A linkagem, que junta os ficheiros .o num executável final
gcc -o final.exe main.o intro.o

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

Anatomia de um Computador Endereço vs. Valor

Endereços ! Considere a memória como sendo um grande array:


Controlo (e. g.   Cada célula do array tem um endereço associado
interrupcões)
Modem
  Cada célula do array contém um valor
Portos de I/O
Memória

GPU

Sound ! Não confundir o endereço, que referencia uma


Blaster determinada célula de memória, com o valor
Keyboard armazenado nessa célula de memória.
Mouse
! Não é correcto dizer que vocês e o vosso endereço
de correio são a mesma coisa !
DADOS
101 102 103 104 105 ...!
...! 23! 42! ...!

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

1
Ponteiros (revisão) Ponteiros (revisão)

! Um endereço referencia uma determinada zona da int *p, x;


memória. Por outras palavras, aponta para essa zona p! ?! x! ?!
de memória.
x = 3;
! Ponteiro: uma variável que contém um endereço de p! ?! x! 3!
memória
p =&x;
p! x! 3!
zona (endereço)!
*p = 5;
101 102 103 104 105 ...! p! x! 5!
...! 23! 42! 104! ...!
x! y! p! ! Operador & : obtém o endereço da variável
Nome ! ! Operador *: dá acesso ao valor apontado, tanto para fins de leitura,
da variável! como escrita.
printf( p points to %d\n ,*p);
SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

Ponteiros e Passagem de Parâmetros (revisão) Sintaxe do C: Função main (revisão)

! Em C a passagem de parâmetros é sempre feita por valor


! Para a função main aceitar parâmetros de entrada
passados pela linha de comando, utilize o seguinte:
void addOne (int x) { void addOne (int *p) {
x = x + 1; *p = *p + 1; int main (int argc, char *argv[])
} }
int y = 3; int y = 3;
addOne(y); ! O que é isto significa?
addOne(&y);
y é ainda = 3
  argc indica o número de strings na linha de comando (o
y é agora = 4 executável conta um, mais um por cada argumento
adicional).
  Example: unix% sort myFile
  argv é um ponteiro para uma array que contém as strings da
linha de comando (ver adiante).

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

Concluindo ... Ponteiros & Alocação (1/2)

! As declarações são feitas no inicio de cada função/ ! Depois de declararmos um ponteiro:


bloco.
! Só o 0 e o NULL são avaliados como FALSO. int *ptr;
! Os dados estão todos em memória. Cada célula/zona
de memória tem um endereço para ser referenciada e ptr não aponta ainda para nada (na realidade aponta
um valor armazenado (não confundir endereço com para algo … só não sabemos o quê!). Podemos:
valor).
! Um ponteiro é a "versão C" de um endereço.   Fazê-lo apontar para algo que já existe (operador &), ou
* segue" um ponteiro para obter o valor apontado.   Alocar espaço em memória e pô-lo a apontar para algo novo
… (veremos isto mais à frente)
& obtém o endereço de uma variável.
! Os ponteiros podem referenciar qualquer tipo de
dados (int, char, uma struct, etc.).

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

2
Ponteiros & Alocação (2/2) Atenção aos Ponteiros !!!

! Apontar algo que já existe: ! Declarar um ponteiro somente aloca espaço para
int *ptr, var1, var2; guardar um endereço de memória - não aloca nenhum
var1 = 5; espaço a ser apontado.
ptr = &var1; ! As variáveis em C não são iniciadas, elas podem
var2 = *ptr; conter qualquer coisa.

! var1 e var2 têm espaço que foi implicitamente


alocado (neste caso 4 bytes) void f()
{
int *ptr; DESASTRE
*ptr = 5;
ptr! ? var1! ?!
5 var2! ?
5! }

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

Tabelas/Arrays (1/5) Tabelas/Arrays (2/5)


! Declaração:
! Arrays são (quase) idênticos a ponteiros
int ar[2];
  char *string e char string[] são declarações muito
declara uma tabela de inteiros com 2 elementos. Uma semelhantes
tabela/array é só um bloco de memória (neste caso de   As diferenças são subtis: incremento, declaração de
8 bytes). preenchimento de células, etc
! Declaração:
int ar[] = {795, 635}; ! Conceito Chave: Uma variável array (o "nome da
declara e preenche uma tabela de inteiros de 2 tabela") é um ponteiro para o primeiro elemento..
elementos.
! Acesso a elementos:
ar[num];
devolve o numº elemento (atenção o primeiro
elemento é acedido com num=0).
SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

Tabelas/Arrays (3/5) Tabelas/Arrays (4/5)


! Array de dimensão n; queremos aceder aos elementos
! Consequências: de 0 a n-1, usando como teste de saída a comparação
  ar é uma variável array mas em muitos aspectos comporta- com o endereço da "casa" depois do fim do array.
se como um ponteiro int ar[10], *p, *q, sum = 0;
  ar[0] é o mesmo que *ar ...
  ar[2] é o mesmo que *(ar+2) p = &ar[0]; q = &ar[10];
  Podemos utilizar aritmética de ponteiros para aceder aos while (p != q)
elementos de uma tabela de forma mais conveniente.
sum += *p++; /* sum = sum + *p; p = p + 1; */
! O C assume que depois da tabela continua a ser um
endereço válido, i.e., não causa um erro de bus ou um
segmentation fault
! O que aconteceria se acrescentassemos a seguinte
instrução?
*q=20;

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

3
Tabelas/Arrays (5/5) Segmentation Fault vs Bus Error?

! Erro Frequente: Uma tabela em C NÃO sabe a sua


! Retirado de
própria dimensão, e os seus limites não são http://www.hyperdictionary.com/
verificados automaticamente!
  Consequência: Podemos acidentalmente transpôr os limites
! Bus Error
  A fatal failure in the execution of a machine language
da tabela. É necessário evitar isto de forma explicita instruction resulting from the processor detecting an
  Consequência: Uma função que percorra uma tabela tem que anomalous condition on its bus. Such conditions include
invalid address alignment (accessing a multi-byte number at
receber a variável array e a respectiva dimensão. an odd address), accessing a physical address that does not
correspond to any device, or some other device-specific
hardware error. A bus error triggers a processor-level
! Segmentation faults e bus errors: exception which Unix translates into a SIGBUS signal
which, if not caught, will terminate the current process.
  Isto são "runtime errors" muito difíceis de detectar. É preciso
ser cuidadoso! (Nas práticas veremos como fazer o debug ! Segmentation Fault
usando gdb…)   An error in which a running Unix program attempts to access
memory not allocated to it and terminates with a
segmentation violation error and usually a core dump.

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

Boas e Más Práticas Aritmética de Ponteiros (1/4)


! Um ponteiro é simplesmente um endereço de memória.
! Má Prática
Podemos adicionar-lhe valores de forma a percorrermos
int i, ar[10];
for(i = 0; i < 10; i++){ ... } uma tabela/array.
! p+1 é um ponteiro para o próximo elemento do array.
! Boa Prática ! *p++ vs (*p)++ ?
#define ARRAY_SIZE 10   x = *p++ ⇒ x = *p ; p = p + 1;
int i, a[ARRAY_SIZE];
for(i = 0; i < ARRAY_SIZE; i++){ ... }   x = (*p)++ ⇒ x = *p ; *p = *p + 1;
! O que acontece se cada célula da tabela tiver uma
! Porquê? SINGLE SOURCE OF TRUTH dimensão superior a 1 byte?
  Evitar ter múltiplas cópias do número 10.  O C trata disto automáticamente. Na realidade p+1 não
adiciona 1 ao endereço de memória, adiciona sim o
tamanho de cada elemento da tabela. (por isso é que
associamos tipos aos ponteiros)
SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

Aritmética de Ponteiros (2/4) Aritmética de Ponteiros (3/4)

! O C sabe o tamanho daquilo que o ponteiro aponta


! Quais são as operações válidas?
(definido implicitamente na declaração) – assim uma
  Adicionar inteiros a ponteiros.
adição/subtracção move o ponteiro o número
  Subtrair 2 ponteiros no mesmo array (para saber a dua
adequado de bytes.
distância relativa).
  1 byte para char, 4 bytes para int, etc.
  Comparar ponteiros (<, <=, ==, !=, >, >=)
  Comparar o ponteiro com NULL (indica que o ponteiro não
aponta para nada). ! As seguintes instruções são equivalentes:
int get(int array[], int n)
! ... tudo o resto é inválido por não fazer sentido {
  Adicionar 2 ponteiros return (array[n]);
  Multiplicar 2 ponteiros /* OR */
  Subrair um ponteiro de um inteiro return *(array + n);
}

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

4
Aritmética de Ponteiros (4/4) Representação ASCII the caracteres
! Podemos utilizar a aritmética de ponteiros para "caminhar" ! Os caracteres são
ao longo da memória: representados através
de bytes
! Existem várias
void copy(int *from, int *to, int n) { codificações: ASCII,
int i; unicode, etc
for (i=0; i<n; i++) { ! É tudo um questão de
*to++ = *from++; interpretação ...
} char a='A';
} a=a+3;
puts(&a);
O que aparece?

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

C Strings Arrays bi-dimensionais (1/2)


! Uma string em C é um array de caracteres. MEMÒRIA
char string[] = "abc"; #define ROW_SIZE 3
#define COL_SIZE 2
6
! Como é que sabemos quando uma string termina? ...
char Mat[ROW_SIZE][COL_SIZE]; 5
  O último caracter é seguido de um byte a 0 (null terminator)
char aux=0; 4
int strlen(char s[]) int i, j;

Endereços
for ( i=0; i<ROW_SIZE; i++) 3
{
for ( j=0; j<COL_SIZE; j++) {
int n = 0; Mat[i][j]=aux; 2
while (s[n] != 0) n++; aux++; 1
return n; }
} ... 0 Mat
0 1
! Um erro comum é esquecer de alocar um byte para o Mat = 2 3
terminador 4 5

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

Arrays bi-dimensionais (2/2) Arrays vs. Ponteiros

! O C arruma um array bi-dimensional empilhando as linhas ! O nome de um array é um ponteiro para o primeiro
umas a seguir às outras. elemento da tabela (indíce 0).
! Um parâmetro tabela pode ser declarado como um
! O espaço total de memória ocupado é array ou um ponteiro.
ROW_SIZExCOL_SIZE
int strlen(char s[]) int strlen(char *s)
{ {
! Temos que: int n = 0; int n = 0;
Mat[2][1] é o mesmo que Mat[2*COL_SIZE+1] while (s[n] != 0) while (s[n] != 0)
n++; n++;
return n; return n;
} }
Pode ser escrito:

while (s[n])!
SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

5
QUIZ - Aritmética de Ponteiros Concluindo …

! Ponteiros e arrays são virtualmente o mesmo


! How many of the following are invalid?
I.  pointer + integer ptr + 1 #invalid
II.  integer + pointer 1 + ptr 1 ! O C sabe como incrementar ponteiros
2
III.  pointer + pointer ptr + ptr 3
IV.  pointer – integer ptr - 1 4
5 ! O C é uma linguagem eficiente com muito poucas
V.  integer – pointer 1 - ptr
VI.  pointer – pointer ptr - ptr
6 protecções
7
VII.  compare pointer to pointer ptr1 == ptr2 8   Os limites das arrays não são verificados
9   As variáveis não são automaticamente iniciadas
VIII.  compare pointer to integer ptr == 1 (1)0
IX.  compare pointer to 0 ptr == NULL
X.  compare pointer to NULL ptr == NULL ! (Atenção) O custo da eficiência é um "overhead"
adicional para o programador
  C gives you a lot of extra rope but be careful not to hang
yourself with it! (tirado de K&R)

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

Alocação dinâmica de memória (1/4)


! Em C existe a função sizeof() que dá a dimensão em bytes do tipo ou
variável que é passada como parâmetro.

! Partir do príncipio que conhecemos o tamanho dos objectos pode dar


origem a erros e é uma má prática, por isso utilize sizeof(type)
  Há muitos anos o tamanho de um int eram 16 bits, e muitos
programas foram escritos com este pressuposto.
  Qual é o tamanho actual de um int?

! sizeof determina o tamanho para arrays:


int ar[3]; // Or: int ar[] = {54, 47, 99}!

LINGUAGEM C sizeof(ar) ⇒ 12
  …bem como para arrays cujo tamanho é definido em run-time:
- ALOCAÇÃO DINÂMICA - int n = 3;!
int ar[n]; // Or: int ar[fun_that_returns_3()];!
sizeof(ar) ⇒ 12!

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

Alocação dinâmica de memória (2/4) Alocação dinâmica de memória (3/4)


! Para alocar memória para algo novo utilize a função ! Depois do malloc() ser chamado, a memória
malloc() com a ajuda de typecast e sizeof: alocada contém só lixo, portanto não a utilize até ter
definido os valores aí guardados.
ptr = (int *) malloc (sizeof(int));
  ptr aponta para um espaço algures na memória com tamanho ! Depois de alocar dinâmicamente espaço, deverá
(sizeof(int)) bytes.
libertá-lo de forma também dinâmica:
  (int *) indica ao compilador o tipo de objectos que irá ser
free(ptr);
guardado naquele espaço (chama-se um typecast ou
simplesmente cast).
! Utilize a função free()para fazer a limpeza
! malloc é raramente utilizado para uma única variável   Embora o programa liberte toda a memória na saída (ou
quando o main termina), não seja preguiçoso!
ptr = (int *) malloc (n*sizeof(int));
  Nunca sabe quando o seu código será re-aproveitado e o
  Isto um array de n inteiros.
main transformado numa sub-rotina!

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

6
Alocação dinâmica de memória (4/4) Diferença súbtil entre arrays e ponteiros

! As seguintes acções fazem com que o seu programa "crash" ou void foo() {
se comporte estranhamente mais à frente. Estes dois erros são int *p, *q, x, a[1]; // a[] = {3} also works here
bugs MUITO MUITO difíceis de se apanhar, portanto atenção: p = (int *) malloc (sizeof(int));
q = &x;
  free()ing a mesma zona de memória mais do que uma vez
  chamar free() sobre algo que não foi devolvido por malloc() *p = 1; // p[0] would also work here
*q = 2; // q[0] would also work here
*a = 3; // a[0] would also work here
! O runtime não verifica este tipo de erros
  A alocação de memória é tão crítica para o desempenho que printf("*p:%u, p:%u, &p:%u\n", *p, p, &p);
printf("*q:%u, q:%u, &q:%u\n", *q, q, &q);
simplesmente não há tempo para fazer estas verificações
printf("*a:%u, a:%u, &a:%u\n", *a, a, &a);
  Assim, este tipo de erros faz com que as estruturas internas de }
gestão de memória sejam corrompidas 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 ...!
  E o problema só se manifesta mais tarde numa zona de código que ...! ?!! 2?!! 3?!!
?! ! 32
52 1!
? ...!
não tem nada a ver …! p q x a! unnamed-malloc-space!

*p:1, p:52, &p:24


*q:2, q:32, &q:28
*a:3, a:36, &a:36
SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

QUIZ QUIZ

Which are guaranteed to print out 5?!


{ char a= 0xFF;
I: main() { 

int *a-ptr; *a-ptr = 5; printf( %d , *a-ptr); }! I II III

unsigned char b=0xFF;
II: main() {
 0: - - -
 printf(" %d %d \n", a, b);
int *p, a = 5; 

1: - - YES

p = &a; ...

2: - YES -

...
/* code; a & p NEVER on LHS of = */ 

printf( %d , a); }! 3: - YES YES

III: main() {
 4: YES - - 

int *ptr;
 5: YES - YES

ptr = (int *) malloc (sizeof(int));
 6: YES YES -
   O que é que aparece no ecrãn?
*ptr = 5;
 7: YES YES YES!
printf( %d , *ptr); } !

Nota: LHS significa "Left Hand Side"

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11 SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11

Para saber mais ...

! K&R - The C Programming Language


  Capítulo 5

! Links úteis para Introdução ao C


  http://man.he.net/ (man pages de Unix)
  http://linux.die.net/man/ (man pages de Unix)

  http://www.lysator.liu.se/c/bwk-tutor.html
  http://www.allfreetutorials.com/content/view/16/33/ (vários
tutoriais)

SMP- MiEEC & AC-LEI - Ano Lectivo 2010/11