7. Variáveis e
Operadores
8. Tomadas de Decisão
9. Loops
10. Comandos Desestruturadores
11. Matrizes
12. Ordenação
13. Ponteiros - Apresentação
14. Ponteiros - Conceitos
de Endereços
15. Ponteiros - Conceitos
Avançados
16. Ponteiros - Pilhas
17. Ponteiros - Conceitos
Complementares
18. Ponteiros x Matrizes
e Entradas e Saídas - Arquivos
| Variáveis e Operadores |
Basicamente, as variáveis podem ser declaradas fora das funções (globais) que valem para todas as funções do programa. Podem ser declaradas dentro de uma função (locais) sendo desconhecida no restante do programa. Além disso podem ser usadas para passagem de valores entre funções (parâmetros).
int soma; /* global */
main()
{
int cont; /* local */
soma = 0; /* soma(global) = 0 */
for (cont = 0; cont < 10; cont ++) {
total(cont);
display();
}
}
total(x)
int x; /* x é parâmetro e vale cont */
{
soma = x + soma;
}
display()
{
int cont; /* cont é local e difere de cont da função main() */
for (cont = 0; cont < 10; cont++) printf("-");
cont++; /* equivale a cont = cont + 1 */
printf("A Soma atual é %d\n",soma);
}
Resultando em 0 1 3 6 10 15 21 28 36 45
| Operador | Ação |
|
|
Adição |
|
|
Multiplicação |
|
|
Divisão |
|
|
Resto de Divisão Inteira |
|
|
Subtração o menos unário |
|
|
Decremento |
|
|
Incremento |
| Operador | Ação |
|
|
Maior que |
|
|
Maior ou igual que |
|
|
Menor que |
|
|
Menor ou igual que |
|
|
Igual a |
|
|
Diferente de |
|
|
Condição "E" |
|
|
Condição "OU" |
|
|
Não |
x = 10;
y = ++x;
então
x = 11 (pois x foi incrementado) e y = 11
porém Se
x = 10;
y = x++;
então
x = 11 e y = 10 (pois x foi atribuído a y ANTES de ser incrementado)
Se
x = 1;
y = 2;
printf("%d == %d e' %d\n",x,y,x==y);
então resultaria em 1 == 2 0 (pois a expressão é falsa)
if (10 > 4 && !(10 < 9) || 3 <= 4)
resultaria em Verdadeiro pois dez é maior que quatro E dez não é menor que nove OU três é menor ou igual a quatro
Por exemplo "sizeof(char)" resultaria em 1.
main()
{
int i=1,j=2,k=3,l=4;
i++;
k=++i;
l=j++;
++j;
printf("%d %d %d %d",i,j,k,l);
}
Constate os resultados do programa acima.
2- Elabore programa contendo 2 variáveis x e y, que atribuam valores a i e j desta forma: i = ++x e j = y++. Constate os resultados.
3- Dados 3 números, os imprima em ordem crescente usando apenas 1 comando printf.
4- Dados 2 números inteiros, imprima 1 se ambos forem positivos ou negativos, 2 se tiverem sinais opostos ou 3 se um deles for zero.
5- Observe o programa a seguir.
main()
{
int i=1,j=2,k,l;
i++;
k=++i+k;;
l=j++ +l;
++j;
printf("%d %d %d %d",i,j,k,l);
}
Processe o programa 3 vezes. Justifique
os resultados.
main()
{
int i=1,j=2,k,l;
i++;
k=++i +k;
l=j++ +l;
++j;
printf("%d %d %d %d",i,j,k,l);
}
| Tomadas de Decisão - Parte II |
if
if (t == 0)
printf("T vale Zero");
if (t == 2 || t = 4) {
printf("T vale Dois\n");
printf("ou T vale Quatro");
}
else {
printf("T nao e'2 ou 4\n");
if (t > 10)
printf("E supera 10");
else
printf("Mas nao supera 10");
}
Exemplo: Evitar-se divisões
por Zero, usando recursos do comando if.
main()
{
int a,b;
printf("Digite 2 números: ");
scanf("%d %d",&a,&b);
if (b)
printf("%f",a/b);
else
printf("Nao posso dividir por zero\n");
}
Exp1 ? Exp2 : Exp3
Se Exp1 for verdadeira, então Exp2 é avaliada tornando-se o valor de Exp1, senão Exp3 é que será avaliada e tornar-se-á o valor da expressão.
Sintaxe:
switch <variável> {
case <constante 1> :
<comandos>;
[break;]
case <constante 2> :
<comandos>;
[break;]
case <constante 3> :
<comandos>;
[break;]
[default :
<comandos>;]
}
Observe que "break" serve para terminar a seqüência de comandos em execução, por serem opcionais, se forem suprimidos permitem que o "case" a seguir seja executado, sem haver qualquer quebra na seqüência do processamento.
main()
{
int t;
for (t = 0; t < 10; t ++)
switch (t) {
case 1:
printf("Agora");
break;
case 2:
printf("e'");
case 3:
case 4:
printf("hora ");
printf("de todos os homens bons\n");
break;
case 5:
case 6:
printf("trabalharem");
break;
case 7:
case 8:
case 9:
printf("-");
}
}
Resultará em:
2- Elabore programa que imprima a seqüência de frases abaixo, usando switch.
Verde Verde Vermelho
Amarelo
Amarelo
Amarelo
Azul Branco e Preto
Branco e Preto
3- Elabore programa que 'z' termine com o valor 4 se 'a' for maior que 10 ou 5 se 'a' for menor ou igual a 10. Os comandos if e switch não poderão ser utilizados.
4- Elabore um menu de opções com 4 situações diversas, utilizando switch.
5- Elabore programa que permita a 5 pessoas escolherem sua cor favorita entre Verde, Vermelho, Amarelo, Azul, Laranja ou Roxo e exiba os resultados.
6- Elabore programa que permita a escolha
entre 1, 2 e 3 ou indique erro de escolha.
| Loops |
for
Sintaxe:
for (<início>;<condição>;<incremento>) <comando>;
Além da sintaxe vista anteriormente, "for" permite a escrita de expressões mais elaboradas, sem qualquer paralelo nas linguagens BASIC, Pascal e COBOL, como pode ser vista a seguir:
for (x=0,y=0;x+y<100;++x,y=y+x)
printf("%d",x+y);
Esta instrução inicializaria
x e y com zero, incrementando x de 1 em 1 e y receberia seu valor acrescido
do de x. O resultado a cada iteração seria impresso desta
forma: 0 (x=0 e y=0) 2 (x=1 e y=1) 5 (x=2 e y=3) 9 14 e assim sucessivamente.
main()
{
int i,j,resposta;
char feito = ' ';
for (i=1;i<100 && feito != 'N';i++) {
for (j=1;j<10;j++) {
printf("Quanto e' %d + %d? ",i,j);
scanf("%d",&resposta);
if (resposta != i+j)
printf("Errou!\n);
else
printf("Acertou!\n");
}
printf("Mais? (S/N) ");
scanf("%c",&feito);
}
}
main()
{
int t;
for (prompt();t=readnum();prompt())
sqrnum(t);
}
prompt()
{
printf("Digite um número inteiro!");
}
readnum()
{
int t;
scanf("%d",&t);
return t;
}
sqrnum(num)
int num;
{
printf("%d\n",num*num);
}
for(;;)printf("Este loop rodará eternamente!\n");
A ausência de condições de inicialização, continuidade e terminação, causarão um processo contínuo e teoricamente infinito (veremos posteriormente a intrução break, que tem a capacidade de encerrar um processo assemelhado ao exemplificado).
for(i=0;i<10;i++);A presença do ponto e vírgula finalizando o comando, força a execução do loop sem que seja executado qualquer outro comando.
for(;;) {
scanf("%d",&c);
if (c == 'A')
break; /* interrompe o que deveria ser um anel eterno */
}
printf("Fim do Loop!");
Conclusão
As características do comando "for", como pudemos notar são bem superiores as dos comandos "for" das linguagens BASIC, Clipper, Pascal e COBOL. Enquanto em algumas daquelas linguagens é recomendável que seu uso seja evitado, em C seu uso é normalmente o mais recomendado, pois substitui geralmente com vantagem seu análogo "while", como veremos a seguir.
main()
{
int i;
while (i < 10) {
printf("%d",i);
i--;
}
}
Sintaxe:
main()
{
int num;
do {
scanf("%d",&num);
} while (num < 100);
}
| Comandos Desestruturadores |
break
main()
{
int t,cont;
for (t=0;t<100;++t) {
cont = 1;
for (;;) {
printf("%d",cont);
cont++;
if (cont == 0) break;
}
}
Observe que break, quebra apenas a estrutura
"for" mais interna, a externa será processada até o final
normalmente.
continue
O comando "continue" funciona de maneira análoga ao "break", contudo ao invés de forçar o encerramento do loop, força nova iteração (semelhante a instrução "loop" da linguagem Clipper) saltando o código entre seu uso e a marca de término do loop.
main()
{
int x;
for (x=0;x<100;x++) {
if (x%2) continue;
printf("%d",x);
}
}
Desta forma toda vez que for gerado um
número ímpar "if" será executado saltando o comando
"printf". "Continue" assemelha-se ao "loop" da linguagem Clipper.
goto
De maneira geral o comando "goto" é considerado o grande vilão das linguagens de programação e geralmente seu uso é desaconselhado quando não proibido por equipes de desenvolvimento e por autores de manuais de linguagens.
Devemos dizer a priori, que uma instrução apenas não teria o poder de causar tantos estragos, porém certos programadores ...
"Goto" apesar de não ser imprescindível para escrita de programas em linguagem C e de ter seu uso restrito a condições particulares, pode ajudar não só a tornar a execução de um programa mais rápida como também mais clara (!!), conforme veremos posteriormente.
"Goto" necessita de um rótulo para poder operar. O rótulo nada mais é que um identificador válido em C seguido de dois pontos. Além disso o rótulo deverá pertencer a função onde se encontra o "goto". Um uso indevido do "goto" está exemplificado a seguir:
x = 1; loop1: x++; if (x<100) goto loop1;Preferencialmente deveríamos ter usado uma estrutura "while" ou "for" para elaboração deste trecho de programa.
feito = 0;
for (...) {
for (...) {
while (...) {
if (...) {
feito = 1;
break;
}
.
.
.
}
if (feito) break;
}
if (feito) break;
}
if (feito)
break;
Vejamos a solução desestruturada
(usando Goto)
for (...) {
for (...) {
while (...) {
if (...)
goto stop;
}
.
.
.
}
}
stop:
printf("Saída!");
Normalmente soluções não
estruturadas são mais rápidas que aquelas estruturadas, e
neste caso a solução também é muito mais clara,
justamente pela utilização adequada do "goto".
Versão 1: Algoritmo Estruturado, sem conceituação matemática:
main()
{
int i,ini,fim,n,j,nao;
char ac[80];
cls();
printf("Digite extremo inferior: "); scanf("%d",&ini);
printf("\nDigite extremo superior: ");scanf("%d",&fim);
dostime(ac, 2);
puts(ac);
for(i=ini;i<=fim;i++) {
nao = 1;
for(j=2;j<i;j++)
if(i % j == 0)
nao = 0;
if (nao || i == 2)
printf("%d ",i);
}
printf("\n");
dostime(ac, 2);
puts(ac);
}
Versão 2: Algoritmo Estruturado
com conceituação matemática.
main()
{
char ac[80];
int j,i,ini,fim,n,nao;
double r;
cls();
printf("Digite extremo inferior: "); scanf("%d",&ini);
printf("\nDigite extremo superior: ");scanf("%d",&fim);
dostime(ac, 2);
puts(ac);
for(i=ini;i<=fim;i++) {
nao = 1;
j = 2;
r = i;
r = sqrt(r);
while (j<=r) {
if(i % j == 0)
nao = 0;
j++;
}
if (nao || i == 2)
printf("%d ",i);
}
printf("\n");
dostime(ac, 2);
puts(ac);
}
Versão 3: Algoritmo com conceituação
matemática, com liberdades na estruturação
main()
{
char ac[80];
int j,i,ini,fim,n,nao;
double r;
cls();
printf("Digite extremo inferior: "); scanf("%d",&ini);
printf("\nDigite extremo superior: ");scanf("%d",&fim);
dostime(ac, 2);
puts(ac);
for(i=ini;i<=fim;i++) {
nao = 1;
if (i % 2 == 0)
nao = 0;
else {
j = 3;
r = i;
r = sqrt(r);
while (j<=r) {
if (i % j == 0) {
nao = 0;
break;
}
j =j + 2;
}
}
if (nao || i == 2)
printf("%d ",i);
}
printf("\n");
dostime(ac, 2);
puts(ac);
}
Solução:
/* Autores: Deusdeth, Luis Fernando, Ana */
int verif=0,numm,numh,ia=0,ib=1023,contm=0,conth=0,resph,palpm;
char nome[32];
main ()
{
cls ();
apresent ();
nome_hum ();
num_maq ();
while (verif == 0){
joga_hum ();
joga_maq ();
rotina_verif ();
rotina_verif1 ();
rotina_verif2 ();
}
rotina_venceu ();
}
apresent ()
{
puts ("|*********************************************************|");
puts ("|* J O G O A L O - V E J A *|");
puts ("|************* ( A P R E S E N T A C A O ) ***************|");
puts ("|* - JOGO ENTRE A MICRO E VOCE. O MICRO IRA' ESCOLHER UM *|");
puts ("|* NUMERO E VOCE OUTRO NUM INTERVALO ENTRE 1 E 1023. *|");
puts ("|* - CADA UM IRA' TENTAR DESCOBRIR O NUMERO DO OPONENTE, *|");
puts ("|* ATE' QUE UM DOS JOGADORES ADIVINHE O NUMERO DO OUTRO*|");
puts ("|* - O MICRO IRA' INFORMAR SE O SEU PALPITE FOI CORRETO, *|");
puts ("|* BAIXO OU ALTO. *|");
puts ("|* - VOCE DEVERA' FAZER O MESMO, INFORMANDO: *|");
puts ("|* (1) PARA UM CHUTE BAIXO; *|");
puts ("|* (2) PARA UM CHUTE ALTO; *|");
puts ("|* (3) PARA CERTO. *|");
puts ("|*********************************************************|");
}
nome_hum ()
{
printf ("INFORME O SEU NOME: ");
gets (nome);
}
num_maq ()
{
numm=rand()/32;
}
joga_hum ()
{
printf ("%s, tente acertar o numero que eu escolhi : ",nome);
scanf ("%d",&numh);
puts ("o resultado sera divulgado apos a jogada do micro");
conth=conth+1;
puts ("*************************************************************");
puts (" ");
}
joga_maq ()
{
palpm=(ia+ib+1)/2;
printf ("%s, acho que voce pensou no numero %d",nome,palpm);
puts (" ");
printf ("digite (1) baixo, (2) alto ou (3) certo : ");
scanf ("%d",&resph);
contm=contm+1;
puts ("*************************************************************");
puts (" ");
}
rotina_verif ()
{
if (numh == numm)
verif = verif + 1;
else
if (resph == 3)
verif = verif + 1;
}
rotina_verif1 ()
{
if (numh > numm){
puts (" ");
printf ("seu chute foi alto");
puts (" ");
}
else
if (numh < numm){
puts (" ");
printf ("seu chute foi baixo");
puts (" ");
}
}
rotina_verif2 ()
{
if (resph == 1)
ia = palpm;
else
if (resph == 2)
ib = palpm;
}
rotina_venceu ()
{
if (numh == numm)
if (resph == 3)
printf("\nOcorreu Empate! \n\n* fim do jogo.\n");
else {
puts (" ");
printf("* %s parabens, voce acertou em %d tentativas.",nome,conth);
puts (" ");
puts ("* fim do jogo.");
puts (" ");
}
else
if (resph == 3){
puts (" ");
printf("* %s o micro acertou em %d tentativas.",nome,contm);
puts (" ");
puts ("* fim do jogo.");
puts (" ");
}
}
| Matrizes |
char nome[20];
float preco[30];
int m[5][3]; /* bidimensional */
char c[3] = {'f','i','m'}; /* declarada e inicializada */
char d[3] = "fim"; /* idem */
int a[5] = {1,10,3,5,30} /* declarada e inicializada, numérica */
main()
{
int a,b,c,d,e;
scanf("%d %d %d %d %d",&a,&b,&c,&d,&e);
printf("%d %d %d %d %d",e,d,c,b,a);
}
main()
{
int i,a[5];
for (i=1;i<=5;i++) {
printf("Elemento %d: ",i);
scanf("%d",a[i]);
}
puts(" ");
for (i=5;i>0;i--)
printf("Elemento %d: ",a[i]);
}
main()
{
int a[10], i, t = 3;
for (i=0;i<11;i++)
a[i] = i * t;
for (i=0;i<11;i++)
printf("%d * %d = %d",i,t,a[i]);
}
Exercício: Retome o Exercício
da Aula de Laboratório 8, exercício 3 e o resolva com o uso
de matrizes.
| Ordenação |
leia n para i de 1 até n leia a[i] para i de 1 até n-1 faça para j de i+1 até n faça se a[i] > a[j] então m = a[i] a[i] = a[j] a[j] = m para i de 1 até n imprima a[i]Em C teríamos:
main()
{
int x,y,z,w,a[100];
do
{
traca_linha()
puts("Programa para ordenar 'n' numeros digitados.");
traca_linha();
printf("Quantos Numeros (0=fim) -> ");
x = leintf();
if (x==0) break;
traca_linha()
for (y = 0; y < x; y++) {
printf("a[%2d]= ",y);
a[y] = leintf();
}
traca_lina();
for (y=0;y<(x-2),y++)
for (z=y+1;z<(x-1);z++)
if (a[y] > a[z]) {
w = a[y];
a[y] = a[z];
a[z] = w;
}
for (y=0;y<(x-1);y++)
printf("%d\n",a[y]);
} while(1);
}
traca_linha()
{
int x;
for (x=1;x != 80; x++)
putchar('=');
putchar('\n');
}
leintf()
{
char s[20];
gets(s);
return(atoi(s));
}
| Ponteiros - Apresentação |
Ponteiros são endereços, isto é, são variáveis que contém um endereço de memória. Se uma variável contém o endereço de outra, então a primeira (o ponteiro) aponta para a segunda.
"x" o "ponteiro" aponta para o "inteiro" a
*- (Asterístico) que acessa o conteúdo de uma variável, cujo endereço é o valor do ponteiro. Não confundir com o operador aritmético de multiplicação de mesmo símbolo. Devolve o valor endereçado pelo ponteiro.
Exemplos:
main()
{
int *pont, cont, valor;
cont = 100;
pont = &cont;
val = *pont;
printf("%d",val); /* 100 */
}
main()
{
char a,b,*x;
b = 'c';
p = &a;
*p = b;
printf("%c",a); /* c */
}
main()
{
int x,y,*px,*py;
x = 100;
px = &x; /* px tem o endereco de x */
py = px; /* py tem o endereco de x */
y = *py; /* y vale 100, pois recebe o conteúdo de x */
/* , através do ponteiro py */
printf("%d %d",x,y);
}
#include "stdio.h"
main()
{
int i,k,*pi,*pk;
char a;
i = 2; k = 0;
puts("Qual sera o valor de k? ");
pk = &k;
pi = &i;
*pk = i;
printf("para *pk = i, temos k= %d\n",k);
k = *pi;
printf("para k = *pi, temos k= %d\n",k);
scanf("%c",&a);
}
main()
{
int x,y,*px,*py;
printf("Digite um valor: ");
scanf("%d",&x);
px = &x;
y = *px;
printf("digitou= %d e y= %d\n",x,y);
*px = 8;
printf("valor mudou para %d\n",x);
}
main()
{
char a,b,*p;
b = 'c';
p = &a;
*p = b;
printf("%c",a);
}
| Ponteiros - Conceitos de Endereços |
int *p, x; char *q, a;se
q = &a; p = &x;e ainda que
a-endereço 100
x-endereços 101/102
então q++ --> q "apontará" para o endereço 101
p++ --> p "apontará" para o endereço 103
Este conceito é particularmente importante no que se refere a matrizes pois como se sabe, matriz nada mais é que um conjunto de variáveis do mesmo tipo, dispostas seqüencialmente em memória.
Por conterem endereços, ponteiros permitem apenas as operações de soma e subtração. Supondo que:
int i,*pi; char c,*pc; float f,*pf;Supondo ainda que pi, pc e pf apontem para i, c e f que estariam com os seguintes endereços: 2340, 2345 e 2350.
pi = pi + 1, então pi valerá 2342 (!), pois variáveis inteiras ocupam 2 bytes.
pf = pf + 5, então pf valerá 2370 (!), pois variáveis pt. flutuante ocupam quatro bytes.
Exemplo
int i,*pi; char c,*pc; float f,*pf;Supondo ainda que pi, pc e pf apontem para i, c e f que estariam com os seguintes endereços: 2340, 2345 e 2350.
pi = pi + 1, então pi valerá 2342 (!), pois variáveis inteiras ocupam 2 bytes.
pf = pf + 5, então pf valerá 2370 (!), pois variáveis pt. flutuante ocupam quatro bytes.
main()
{
int x,y,*px,*py;
x = 100;
px = &x; /* px tem o endereco de x */
py = px; /* py tem o endereco de x */
y = *py; /* y vale 100, pois recebe o conteúdo de x, através do ponteiro py */
printf("%d %d",x,y);
}
Exemplo:
if (p > q) puts("p aponta para endereço maior que q");
| Ponteiros Conceitos Avançados |
char linha[80],*p;*p1;
p = &linha[0];
p1 = linha; /* p1 e p possuem o mesmo endereço, i.é, */
if (p==p1)
puts("iguais!"); /* apontam para o 1º elemento da matriz! */
Exemplo: Inverter os elementos de uma
string, usando ponteiros.
Solução: Pela maneira convencional teríamos:
main()
{
char str[80];
int i;
printf("Digite uma palavra: "); gets(str);
for (i=strlen(str)-1;i>=0;i--)
printf("%c",str[i]);
}
main()
{
char str[80],*p;
int i;
printf("Digite uma palavra: "); gets(str);
p = str;
for(i = strlen(str) - 1;i>=0;i--)
printf("%c",*(p+i));
}
char *p = "Imprimindo Algo\n";
main()
{
int i;
printf("%s\n - por ponteiro...\n\n",p);
for(i=0;p[i];i++)
printf("%c",p[i]);
puts("\n - por caracteres em ponteiro ");
}
main()
{
int i,*p,a[5];
for (i=0;i<=4;i++) {
printf("Digite o %d elemento",);
scanf("%d",&a[i]);
}
p = a;
for (i=0;i<=4;i++)
printf("%d %d %d\n",a[i],p[i],*(p+i));
}
| Ponteiros - Pilhas |
Exemplo: Faça com que seja criada uma pilha com no máximo 50 elementos, onde números positivos empilhem elementos na pilha, 0 tire o último elemento da pilha e -1 cause o encerramento do programa:
int pilha[50],*p1,*to;
main()
{
int valor;
p1 = pilha;
to = p1;
printf("Numero --> Pilha, 0 recupera e -1 finaliza \n");
do {
scanf("%d",&valor);
if (valor != 0)
push(valor);
else {
valor = pop();
printf("%d\n",valor);
}
} while (valor != -1);
}
push(i)
int i;
{
p1++;
if (p1 == (to + 50)) {
puts("Estouro da Pilha (superior) ");
exit(1);
}
*p1 = i;
}
pop()
{
if ((p1) == to) {
puts("Estouro da Pilha (inferior) ");
exit(1);
}
p1--;
return *(p1+1);
}
Imaginemos agora uma pilha onde não
soubéssemos antecipadamente o total de elementos de uma pilha, neste
caso precisaríamos de instruções que permitissem a
manipulação da área de memória livre entre
o programa e área de trabalho utilizada pelo programa conforme mostra
o esquema abaixo:
| Usada para Variáveis Locais e endereços de Rotinas | Do "fim" para o inicio |
| Memória Livre para Alocação | |
| Variáveis Globais | Do "inicio" para o fim |
| Programa |
Exemplo:
char x,y,*px;
main()
{
px = malloc(1000);
if (!px) {
puts("Memória Insuficiente!");
exit(1);
}
x = 'a';
push(x);
x = 'b';
push(x);
y = pop();
printf("%c\n",y);
y = pop();
printf("%c",y);
}
push(i)
char i;
{
px++;
*px=i;
}
pop()
{
px--;
return *(px+1);
}
Exemplo: Aloque uma certa quantidade de
memória e posteriormente escreva letras na área alocada,
recupere estas letras e depois libere a memória previamente alocada.
main()
{
char c,*p,*q;
int i=0,j,r;
cls();
q = malloc(1000);
p = q;
puts("Digite Letras ... para parar digite <enter>");
for(;;) {
printf("Digite a %d letra: ",i+1);
scanf("%c",&c);
if (c == ' ')
break;
*p = c;
c = ' ';
p++;
i++
}
p = q;
printf("\n\nLetras Digitadas:\n");
for(j=1;j<=i;j++) {
printf("%d Letra = %c\n",j,*p);
p++;
}
r=free(q);
if (r==0)
puts("\nLiberada Memória Utilizada!");
else
puts("\nFalha na Liberação da Memória Alocada!");
}
Exemplo: Retome o exercício anterior,
porém ao invés de armazernar letras, armazene números.
main()
{
char c,*q,*p;
int i = 0, j, r, m, n;
cls();
q = malloc(1000);
p = q;
puts("Digite Numeros ... para parar digite <enter> ");
for(;;) {
printf("Digite o %do. Numero: ", i+1);
scanf("%d",&m);
if (m==0)
break;
*p = m;
m = 0;
p=p+2;
i++;
}
p = q;
printf("\n\nLetras Digitadas: \n");
for(j=1;j<=i;j++) {
printf("%d Letra = %d\n",j,*p);
p=p+2;
}
r=free(q);
if (r==0)
puts("\nLiberada Memoria Usada!");
else
puts("\nFalha na Liberação de Memória Reservada!");
}
Nota: A característica típica
da linguagem C, que facilita a mistura de tipos, permite o endereçamento
de variáveis através de sua posição na memória,
ou seja, podemos usar um ponteiro de caracteres para apontar um número
inteiro e recuperá-lo sem qualquer tipo de inconveniente.
Devemos notar entretanto que esta característica pode acarretar grandes problemas, e caso seja utilizada de forma inadequada, pode gerar problemas se acessarmos (ou principalmente se escrevermos) alguma área de memória importante.
Outro ponto importante, é percebermos que não temos números, caracteres ou qualquer outra coisa aramazenadas na memória além de bits, portanto tudo o que necessitamos saber é onde está uma informação procurada e qual é seu formato e simplesmente acessá-la (ou substituí-la) de acordo com nossa necessidade.
É desnecessário dizer, que estas são características dos assemblers próprios de cada equipamento, portanto a Linguagem C nos desobriga a aprender o Assembler de cada microprocessador, porém não elimina a necessidade de conhecermos sua arquitetura.
int *x[10];
x[2] = &valor;
.
.
.
printf("%d",*x[2]);
ou
main()
{
int x,*p,**q;
x = 10;
p = &x;
q = &p;
printf("%d",**q); /* 10 */
}
| Ponteiros Conceitos Complementares |
strings combinado a possibilidade de representação de matriz como ponteiros,
conforme mostra o exemplo a seguir:
char *p = "Frase a demonstrar \n";
main()
{
int i;
printf("%s",p);
for (i=0;p[i];i++)
printf("%c",p[i]);
}
Atribuição de posição de memória desconhecida:
main()
{
int x,*p;
x = 10;
*p = x;
printf("%d",*p); /* valor desconhecido */
}
Observe que 10 é atribuído
a uma posição de memória desconhecida, pois o endereço
de "p" não é conhecido. Caso "p" estiver apontando para algum
endereço vital, o sistema será paralisado, causando suspeita
de mal funcionamento (hardware) ou presença de vírus (software).
Na verdade, esta ocorrência é o chamado "bug" (pequeno erro
no programa), pois somente algumas vezes causará erro. Observe serem
grandes as possibilidades deste programa funcionar perfeitamente, pois
o ponteiro provavelmente jogará o valor num local não usado.
Porém quanto maior for o programa, mais provável será
a possibilidade de encontrarmos um erro conforme descrito anteriormente.
Atribuição de valor para o ponteiro:
main()
{
int x,*p;
x = 10;
p = x;
printf("%d",*p); /* valor desconhecido */
}
Observe que não será impresso
10, mas um valor desconhecido qualquer, pois 10 será atribuído
ao ponteiro p, que supostamente contém um endereço e não
um valor. Se tivéssemos "p = &x;" aí o programa funcionaria
de forma correta.
main()
{
int a,r,x;
printf("Digite um valor: ");
scanf("%d",&a);
x = 2 * a + 3;
r = soma(a);
printf("%d, %d e %d",a,x,r);
}
soma(z)
int z;
{
int x=5;
x = 2 * x + z;
z = 0;
return(x);
}
main()
{
int a,r,x;
printf("Digite um valor: ");
scanf("%d",&a);
x = 2 * a + 3;
r = soma(&a);
printf("%d, %d e %d",a,x,r);
}
soma(z)
int *z;
{
int x=5;
x = 2 * x + *z;
*z = 0;
return(x);
}
Exemplo 3- Uso de ponteiros em funções.
main()
{
int a,b;
a = 100;
b = 20;
swapg(&a,&b);
printf("Maior = %d ",a);
printf("Menor = %d ",b);
}
swapg(c,d)
int *c,*d;
{
int t;
if (*c <= *d)
return; /* nao troca */
t = *c;
*c = *d;
*d = t;
}
2- Crie um programa de contagem com loop usando ponteiros.
3- Crie um programa com 2 variáveis,
sendo que a primeira será do tipo caracter e a segunda do tipo inteiro.
Atribua valores da primeira para a segunda e vice-versa através
de ponteiros.
| Ponteiros x Matrizes e Entradas e Saídas |
main()
{
int a[10],i;
for(i=0;i<10;i++)
a[i] = i*3;
for(i=0;i<10;i++)
printf("%d x 3 = %d \n",i,a[i]);
}
Apesar da solução apresentada
acima ser correta logicamente, peca por desconsiderar a razão principal
de um programa ser escrito em C, a velocidade. Intui-se facilmente que
se endereçarmos uma variável por seu endereço, com
certeza seremos mais velozes do que se a acessarmos pela tabela de variáveis
(como feito no exemplo acima).
Antes de resolvermos novamente este problema, voltemos aos ponteiros neste pequeno programa, que servirá para entendermos a estratégia comumente usada pelos programadores C.
Instruções 1000 1001 1002
main()
{
char c,*pc,x; declarações c pc x
c = 'A'; atribuição A ? ?
pc = &c; atribuição 1000 ?
printf("%c",*pc); exibir A
x = *pc; atribuição A
}
main()
{
int a[10],i,*p;
p = &a;
for(i=1;i<10;i++)
*(ponteiro+i) = i*3;
for(i=0;i<10;i++)
printf("%d x 3 = %d \n",i,a[i]);
}
| Instruções | Descrição |
| getchar() | lê um caracter do teclado aguardando <Enter> |
| getche() | lê um caracter do teclado e prossegue |
| getch() | lê um caracter sem eco na tela e prossegue |
| putchar() | escreve um caracter na tela |
| gets() | lê uma string do teclado |
| puts() | escreve uma string na tela |
Exemplo 1: Escrever "A" de diversas formas.
main()
{
int i;
char c;
i = 65;
c = 'A';
putchar(65);
putchar('A');
putchar(i);
putchar('\n');
}
A linguagem C é pródiga
em "confundir" dispositivos, como podemos constatar no exemplo a seguir.
main()
{
char s[30];
int c;
while ((c=getchar()) != EOF)
putchar(c);
get(s);
puts(c);
}
Revisando o comando "scanf", agora estamos
em condições de justificar o porquê devemos endereçar
as variáveis com &. O sinal % na frente dos especificadores
de entrada serve para informar o tipo de dado a ser lido. Um caracter não
branco na string faz com que "scanf" leia e desconsidere um caracter coincidente.
Por exemplo, a string de controle "%d,%d" faz com que "scanf" primeiro
leia um inteiro, depois leia e desconsidere um vírgula e, finalmente,
leia outro inteiro. Se não houver ',', "scanf" será encerrada.
O comando "scanf" deve receber valores passados por seus endereços. Devemos lembrar que C trabalha desta forma para chamadas por referência, permitindo que uma função altere o conteúdo de um argumento, como em "scanf("%d",&cont);".
Para leitura de strings (matrizes de caracteres), o nome da matriz sem índice informa o "endereço do primeiro elemento" da matriz, ou seja um ponteiro, portanto não devemos usar &, como em "scanf("%s",matriz);".
Os programadores BASIC devem ter em mente que soluções como as separações por vírgula no comando "INPUT" não funcionaram adequadamente em instruções do tipo "scanf("%d %d",&r,&c);".
No exemplo a seguir o 't' (se digitado!) será descartado, 10 ficará em x e 20 em y como em "scanf("%st%s",&x,&y);".
Outro cuidado a ser tomado pode ser constatado na expressão a seguir "scanf("%s ",dado);", somente após você digitar um caracter após um caracter branco. Isto ocorre pois "%s " instruiu "scanf()" a ler e desconsiderar espaços.
Os usuários BASIC (e alguns do Clipper) tem o seguinte costume:
INPUT"Digite um Número: ";A%Analogamente pensa-se (de forma errada) que podemos fazer:
scanf("Digite um número %d ",&a);
puts("Digite um número: ");
scanf("%d",&a);
| Código | Significado | Observações |
| %c | lê/escreve um único caracter | |
| %d | lê/escreve inteiro decimal | |
| %i | lê/escreve inteiro decimal | |
| %e | lê/escreve número com ponto flutuante | Notação Científica |
| %f | lê/escreve número com ponto flutuante | |
| %h | lê/escreve um inteiro curto | |
| %o | lê/escreve um número octal | |
| %s | lê/escreve uma string | |
| %x | lê/escreve um número hexadecimal | |
| %p | lê/escreve um ponteiro | |
| %n | receber um valor inteiro igual ao número de caracteres lidos até o momento | |
| número | especificará o total de caracteres a serem lidos para um campo qualquer | |
| %g | Usa %e ou %f, aquele que for menor | Somente printf |
| %% | Imprime % | Somente printf |
| %u | Decimal sem Sinal | Somente printf |
| h | Modificador short | |
| l | Modificador long |
%ld- especifica um inteiro longo.
Os modificadores "acompanham" o outro código de formato, modificando sua apresentação.
2- Retome exercícios anteriores,
utilizando as instruções de entrada e saída getchar(),
getche(), getch(), putchar(), gets() e puts().