Em todos os tutoriais usarei a IDE Code::Blocks. É mil vezes melhor do que o popular Dev C/C++ e é igualmente gratuito, além de ter a enorme vantagem de ser “cross-platform”, ou seja, você pode instalar ele em mais de um sistema operacional. No caso do Code::Blocks ele pode ser instalado no Windows, no Linux e no Mac OS X.
Link para download: http://www.codeblocks.org/downloads
Não vou dar detalhes sobre a instalação, talvez em um post futuro. Também está no projeto uma explicação de como usar o debugger, que no Code::Blocks funciona efetivamente.
Criando um projeto no Code::Blocks:
Clique no botão
New e em seguida em Project…:

Em seguida aparece uma janela para a seleção do tipo de projeto, na maioria dos tutoriais usaremos “Console Application”, selecione esta opção e clique em Go. Também pretendo fazer tutoriais sobre a biblioteca gráfica Gtk+.
Um assistente para criação do projeto irá abrir, clique em Next. Na tela de seleção de linguagem, selecione C. No próximo passo coloque o nome do projeto e escolha o local onde os arquivos serão salvos, recomendo criar uma pasta só para o projeto.
Na próxima tela deixa as opções como na imagem:

Clique em Finish e pronto, o projeto está criado e já com um arquivo main.c.
Ponteiros
Como quase todo tutorial aqui vai ser na linguagem C, é de fundamental importância o pleno entendimento dos ponteiros, tão detestados pelos iniciantes, e tão fundamentais.
Como o próprio nome diz, um ponteiro é um tipo de dado que aponta para uma posição da memória, ou seja, a informação que ele guarda é um endereço de memória, não um dado em si.
Falando grosseiramente sobre arquitetura de computadores, cada posição de memória tem um número que a identifica, por exemplo, se você está trabalhando com uma memória de 1 gb, a primeira posição será a 1 e a última será a 1.073.741.824 (muita coisa hein?), e é exatamente essa identificação que um ponteiro guarda.
Notação:
Um ponteiro pode ser de qualquer tipo, abaixo estão algumas declarações de ponteiros:
int *p;
float *f;
char *s;
Essa é a forma de declarar um ponteiro, que não tem nada haver com a forma de se usar um ponteiro. Vamos supor que declaramos um ponteiro para inteiro (int *p).
Observação: Em C quando declaramos uma variável, seja ela de que tipo for, ela não é zerada, o que significa que a posição de memória que foi alocada para ela tem o que chamamos de lixo, uma informação que não foi o seu programa que criou, simplesmente foi largada na memória por outro programa.
O operador & e o operador *:
Quando você aplica o operador & sobre uma variável, você recebe o endereço de memória onde ela está armazenada, vejamos o seguinte trecho de código:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| #include <stdio.h>
int main(void)
{
int *p=NULL; /* Declaramos p inicialmente apontando para nada */
int a=10;
printf("Valor do ponteiro: %p",p); /* Aqui o valor do ponteiro será NULL */
printf("\nEndereço de a: %p",&a);
p = &a; /* O ponteiro p recebe o endereço de memória de a */
printf("\nValor do ponteiro: %p",p);
printf("\nConteudo do ponteiro: %d",*p); /* O operador * recupera a informação armazenada no endereço de memória apontado por p */
return 0;
} |
Como comentado no código, o operador * recupera a informação armazenada no endereço de memória apontado por p. No exemplo acima, p aponta para a, que armazena o valor 10, portando *p será 10.
Agora vamos analisar o seguinte código:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| #include <stdio.h>
int main(void)
{
int *p=NULL; /* Declaramos p inicialmente apontando para nada */
int a=100,b=200,c=300,d=400,e=500;
p = &a; /* p aponta para a */
printf("\n*p = %d",*p);
*p = c; /* O conteudo de p recebe c, mas como p aponta para a, a recebe c */
printf("\n*p = %d",*p);
printf("\n a = %d\n",a);
p = &b; /* p aponta para b */
printf("\n*p = %d",*p);
*p = d; /* O conteudo de p recebe d, mas como p aponta para b, b recebe c */
printf("\n*p = %d",*p);
printf("\n b = %d\n",b);
p = &e; /* p aponta para e */
printf("\n*p = %d",*p);
*p = 600; /* O conteudo de p recebe o valor 600, mas como p aponta para e, e recebe c */
printf("\n*p = %d",*p);
printf("\n e = %d\n",e);
e = 700; /* e recebe 700, como p aponta para e, *p será 700 */
printf("\n*p = %d",*p);
return 0;
} |
Na linha 8 p recebe o endereço de a, ou seja, p aponta para a:
Na linha 11 o conteúdo de p recebe c, mas como p aponta para a, a recebe c:
Na linha 15 p recebe o endereço de b, ou seja, p aponta para b:
Na linha 18 o conteúdo de p recebe d, mas como p aponta para b, b recebe d:
Na linha 22 p recebe o endereço de e, ou seja, p aponta para e:
Na linha 25 o conteúdo de p recebe o valor 600, mas como p aponta para e, e recebe o valor 600:
Na linha 29 e recebe o valor 700, portanto *p será 700 também.
Observação: Veja como o modo de declarar ponteiros é diferente do modo de usá-los. Quando declaramos um ponteiro, se fizermos int *p = 2341, significa que o ponteiro está apontando para a posição 2341 da memória, mas se fizermos *p = 2341 em outra parte, significa que o conteúdo de p recebe 2341.
Curiosidade: Quando um vetor é declarado, por exemplo, int a[10], “a” contem o endereço do primeiro elemento do vetor, logo a == &a[0].
Em breve postarei algumas aplicações práticas de ponteiros, como listas encadeadas.