quinta-feira, 6 de março de 2008

Capítulo 6 - Iniciando com constantes e macros de C

Pré-processador C: ao compilar um arquivo fonte C, o pré-processador inclui outros arquivos ou faz algumas definições.

Diretiva #define:

#define SEGUNDA 0
#define TERCA 1
#define QUARTA 2
#define QUINTA 3
#define SEXTA 4

main()
{
int dia = TERCA;

if(dia == SEGUNDA)
printf("Dia da semana e segunda.");
else if(dia == TERCA)
printf("Dia da semana e terca");
else if(dia == QUARTA)
printf("Dia da semana e quarta");
else if(dia == QUINTA)
printf("Dia da semana e quinta");
else if(dia == SEXTA)
printf("Dia da semana e terca");

getchar();
}


O que a diretiva define está fazendo é literalmente substituir as constantes no código pelos números. É como se reescrevêssemos o código apagando os dias da semana e colocando seus respectivos valores inteiros.

#define CHAPTER "Este e o capitulo 6"

main()
{
printf("%s", CHAPTER);
getchar();
}


Não é preciso dizer o poder que essa diretiva possui. É possível até mesmo criar uma nova linguagem de programação a partir dela.

#define MAX(a,b) (a > b) ? a : b

main()
{
int a = 10, b = 5;
printf("O valor maior e: %d", MAX(a,b));
getchar();
}


Podemos criar funções na diretiva define e usá-la dentro do código.

#define SQUARE(x) (x * x)

main()
{
int x = SQUARE(2 + 5)
printf("%d", x);
getchar();
}


Executa: 17
Pois o cálculo é (2 + 5 * 2 + 5). O operador de multiplicação tem prioridade sobre o de soma, então o resultado não é o que esperávamos.

#define SQUARE(x) ((x) * (x))

main()
{
int x = SQUARE(2 + 5)
printf("%d", x);
getchar();
}


Executa: "A letra em maiuscula: A." Converte qualquer letra em maiúscula. Repare na diretiva define o uso de barra invertida para continuar o comando. Sobre a função: o que parece é que estamos retirando o caractere 'a' da variável letra e inserindo 'A'. Acredito que esteja na verdade sendo feita uma operação aritmética com o código ASCII, e não propriamente substituindo os caracteres.

Diretiva #include:

Quem programa em outras linguagens sabe que determinadas constantes, funções, rotinas ou métodos podem estar dentro de outros arquivos fonte, ou até mesmo dentro de outros diretórios. Através dessa diretiva, podemos utilizar as constantes e funções de outros arquivos fonte que são padrão da linguagem C ou desenvolvidos e disponibilizados para isso. Esses arquivos têm a extensão .H.
Em algumas IDEs (Ferramentas de desenvolvimento) como o Bloodshed Dev-C++ 5, não é preciso fazer #include de alguns arquivos .H padrão, pois são automaticamente carregados na compilação. Um exemplo é a função getchar() que sempre estamos utilizando nos programas.

#include &lsaquo stdio.h &rsaquo

main()
{
char letra;

for(letra = 'A'; letra <= 'Z';\
letra++)
putchar(letra);
getchar();
}


A função putchar() imprime na tela um caractere por vez. Ela está dentro do código fonte do arquivo stdio.h. Se você estiver usando o Dev-C++ 5, vai perceber que tanto faz colocar a linha da diretiva #include, a função vai executar do mesmo jeito.

#include &lsaquo stdio.h &rsaquo

main()
{
char letra;

printf("Digite uma letra e pressione\
ENTER.\n")
letra = getchar();

printf("A letra digitada foi %c", letra);
getchar();
}


A função getchar() recebe um caractere ou string e espera o usuário pressionar ENTER para armazenar o valor na variável letra.

#include &lsaquo stdio.h &rsaquo

main()
{
char letra;
printf("Digite uma linha de texto\n");

do
{
letra = getchar();
putchar(letra);
}
while(letra != '\n');

printf("%c", letra);
getchar();
}

Capítulo 5 - Construções avançadas em C

Loopings:

main()
{
char letra = 'A';

do
printf("%c\n", letra++);
while(letra <= 'G');

getchar();
}



Executa:
A
B
C
D
E
F
G

Obs: Observe que após o comando while temos um ponto-e-vírgula.

main()
{
int count;

for(count = 0; count <= 100;count++)
if(count % 3 != 0)
printf("%d\n", count);

getchar();
}

Traduzindo: declarei uma variável chamada count, mas não inicializei.
Para count que recebe o valor 0 e enquanto count for menor ou igual a 100, some 1 no valor de count após essa linha de comando.
Se o resto da divisão de count por 3 for DIFERENTE de zero, imprima count, senão não imprime NADA.
Ou seja, serão impressos todos os valores de 1 a 100 menos aqueles que a divisão por 3 dá resto 0.
Agora um programa que gera o mesmo resultado, através do comando continue.


main()
{
int count;

for(count = 0; count <= 100; count++)
{
if(count % 3 == 0)
continue;
printf("%d\n", count);
}

getchar();
}


O comando continue interrompe a sequencia de comandos dentro das chaves {} da função for(), ou seja, não imprime o valor de count quando a divisão por 3 dá zero.

main()
{
char letra;

for(letra = 'A'; letra <= 'Z'; letra++)
if(letra == 'A')
printf("%c\n", letra);
else if(letra == 'E')
printf("%c\n", letra);
else if(letra == 'I')
printf("%c\n", letra);
else if(letra == 'O')
printf("%c\n", letra);
else if(letra == 'U')
{
printf("%c\n", letra);
break;
}

getchar();
}


Executa: AEIOU
A única coisa que o break faz aqui é impedir que sejam testadas as letras de V a Z.

main()
{
int count;
char letra;

for(count = 1; count <= 5; count++)
{
printf("\n%-2d", count);
/* Exibe count à esquerda da tela e
* com dois espaços para digitos
*/
for(letra = 'A'; letra <= 'Z'; letra++)
printf("%c", letra);
}

getchar();
}


Tradução: cria uma variável count do tipo inteiro e outra variável letra do tipo char. Para count de um até 5: incremente 1 e imprima count (formatado).
Para letra de A a Z, incremente 1 e imprima letra (formatado).
Cada for() imprime o conteúdo de sua variável e incrementa 1 na variável.

Executa:
1 ABCDEFGHIJKLMNOPQRSTUVWXYZ
2 ABCDEFGHIJKLMNOPQRSTUVWXYZ
3 ABCDEFGHIJKLMNOPQRSTUVWXYZ
4 ABCDEFGHIJKLMNOPQRSTUVWXYZ
5 ABCDEFGHIJKLMNOPQRSTUVWXYZ

main()
{
char letra;

for(letra = 'A'; letra <= 'Z'; letra++)
switch(letra)
{
case 'A': printf("%c", letra);
case 'E': printf("%c", letra);
case 'I': printf("%c", letra);
case 'O': printf("%c", letra);
case 'U': printf("%c", letra);
}

getchar();
}


Executa: AAAAAEEEEIIIOOU
Após testar se letra == A, ele efetua o comando na linha e todos abaixo dela. O mesmo ocorre com as outras letras.

main()
{
char letra;

for(letra = 'A'; letra <= 'Z'; letra++)

switch(letra)
{
case 'A': printf("%c", letra);
break;
case 'E': printf("%c", letra);
break;
case 'I': printf("%c", letra);
break;
case 'O': printf("%c", letra);
break;
case 'U': printf("%c", letra);
break;
}

getchar();
}


Executa: AEIOU

main()
{
char letra;
for(letra = 'A'; letra <= 'Z'; letra++)
switch(letra)
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U': printf("%c", letra);
}

getchar();
}


Executa: AEIOU

Obs: não é possível usar uma variável após o comando case, sempre é usada uma constante.


main()
{
char letra;
int vogais = 0;
int consoantes = 0;

for(letra = 'A'; letra <= 'Z'; letra++)
switch(letra)
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U': vogais++; break;
default: consoantes++;
}

printf("Vogais: %d\t Consoantes:\
%d", vogais, consoantes);
getchar();
}


O mesmo caso do exemplo anterior, só que em vez de exibir o valor da variável incrementa vogais em 1. Se não for vogal, a opção será default, onde incrementa consoantes em 1.

Comando goto:

main()
{
char letra = 'A';

loop:
printf("%c\n", letra++);

if(letra <= 'Z')
goto loop;

getchar();
}




Goto significa "Ir para". É totalmente intuitivo.

Operador condicional:


main()
{
int max, min, a = 10, b = 5;

max = (a > b) ? a : b;
min = (a < max =" %d\t" min =" %d">

getchar();
}




Tradução: declaro variáveis max e min, e constantes a e b, todos do tipo int, a = 10 e b = 5. Se a variável "a" for maior que "b", então max = a, senão max = b. Se a variável "a" for menor que "b", então min = a, senão min = b. Exiba max e min. Essa operação poderia ser feita com funções while, do while, for, etc. Essa é uma forma de simplificar o código, mas dependendo do caso pode complicar. Exemplo: outra pessoa ter que fazer manutenção no seu código fonte.
Essa operação poderia ter sido feita dentro de printf().

Strings longas:

main()
{
printf("Primeiro valor: %d Segundo valor: %d Soma: %d", \
5, 10, 5+10);
getchar();
}



Obs: Observe a barra invertida para "quebrar" o comando em outra linha.

Capítulo 4 - Estruturas de controle em C


main()
{
if(2)

printf("Qualquer valor diferente de zero\
e verdadeiro");

getchar();
}


Nesse código fonte a string dentro de printf será exibida. Isso porque a função if() considera como falso apenas se entrarmos com o número zero entre os parênteses. se entrarmos com qualquer outro número, a condição é verdadeira e é executada a linha abaixo de if().

main()
{
char upper, lower;
printf("Maiuscula\tMinuscula\n");

for(lower = 'a', upper = 'A'; upper <= 'F'; upper++, lower++)
printf("%c\t\t%c\n", upper, lower);

getchar();
}


São mostradas as letras maiúsculas (até que upper seja igual ou menor que F) e as respectivas em minúsculas. Ou seja, quando incrementamos a variável do tipo char, ela modifica o conteúdo da variável para a próxima letra na sequência de caracteres ASCII.

quarta-feira, 5 de março de 2008

Capítulo 3 - Variáveis, Tipos e Operadores

Tipos de variáveis:

1. int: armazena números inteiros.
2. float: armazena ponto flutuante com 6 ou 7 dígitos de precisão.
3. char: caracteres ASCII (padrão do teclado).
4. double: ponto flutuante com 13 ou 14 dígitos de precisão.

Analisando: O valor limite de int varia de compilador para compilador. No Dev-C++ esse limite é muito maior do que o exposto.
O tipo float será mais do que suficiente para aplicações tradicionais, em oposição ao double.
Usaremos char para criar e manipular strings, além de receber valores unitários (ex. masculino = m e feminino = f).

main()
{
int sum;
sum = 3 + 5;
printf("Sum = %d", sum);
}


Executa: 7
A variável é definida como do tipo inteiro com o nome "sum".

main()
{
unsigned int num;
num = 60000;
printf("Sum = %u", num);
}


Executa: 60000
Qualificar int como unsigned aumenta o tamaho do limite dos números inteiros positivos em int. Perceba que utilizei o especificador de formato %u.

main()
{
long int distancia = 93000000;
printf("A distancia e de: %ld", distancia);
}


Executa: 93000000.
Qualificar int como long aumenta o tamanho do limite dos inteiros tanto com sinal positivo quanto negativo, ainda maior que unsigned. Usamos o especificador de formato %ld.
Podemos usar unsigned long int, que aumenta ainda mais o tamanho dos inteiros positivos de long.

main()
{
short int val = 32767 + 1;
printf("O valor e: %d", val);
}


Executa: -32768.
O que aconteceu aqui? O qualificador short impoe ao int valores entre -32768 a 32767. Quando ultrapassamos o limite positivo, ele mostra como valor padrão o limite negativo. Se ultrapassamos o limite negativo, ele mostra o limite positivo.

Existe também o qualificador register, que diz ao compilador para armazenar a variável em um registrador (memória do processador), aumentando a performance. No entanto, não sei se esse qualificador realmente funciona. Geralmente usado para variáveis em loopings (for, while, etc.).

Incremento e Decremento:


main()
{
int val = 0;
printf("Valor de pos-incremento: %d\n", val++);
val = 0;
printf("Valor de pre-incremento: %d",++val);
getchar();
}


Fazer val++ ou ++val é o mesmo que val = val + 1. Acontece que no pos-incremento de val (val++), primeiro é executada a linha de comando (função printf()) com o valor padrão de val (val = 0). Somente "depois" acrescenta-se 1. Na linha de comando abaixo defino val como sendo zero de novo. Na segunda chamada a printf(), é feito o pré incremento, ou seja, na "mesma" linha de comando, alteramos o valor de val para 1.

Obs: getchar() Recebe um caractere. Apertando ENTER ele recebe o caractere e a tela fecha.
Obs: comentários no código:

// comentário

/*comentário*/

/*
*
* comentário
*
*/

Operadores BIT a BIT:

main()
{
int num = 255; /* É igual a 0000 0000 1111 1111
em decimal */
num = num >> 3; /* Desloca 3 bits para a direita,
* ou seja, 0000 0000 0001 1111 em
* binário, o que equivale a 31
* em decimal
*/

printf("%d", num);
getchar();
}


Executa: 31
O número 255 em decimal é 1111 1111. O operador de deslocamento para a direita >> desloca 3 bits à direita, o que equivale a 31 em decimal.

main()
{
int num = 255; /* É igual a 0000 0000 1111 1111
em decimal*/

num = num << 1; /* Desloca 1 bit para a esquerda,
* ou seja, 0000 0001 1111 1110 em
* binário, o que equivale a 510
* em decimal
*/
printf("%d", num);
getchar();
}

Executa: 510

main()
{
int num1 = 5; // 0101 em binário
int num2 = 10; // 1010 em binário
int total = num1^num2;

/* O operador ^ é chamado OR exclusivo, vale 1
* para cada dígito binário diferente, e zero
* para dígitos binário iguais
* 5 = 0101
* 10 = 1010
* -----------
* 15 = 1111
*/

printf("%d", total);
getchar();
}


Executa: 15

main()
{
int num1 = 5; // 0101 em binário
int num2 = 10; // 1010 em binário
int total = num1num2;

/* O operador é chamado OR, vale 0 quando os
* dois digitos binários em comparação forem zero.
* 4 = 0100
* 10 = 1010
* -----------
* 14 = 1110
*/

printf("%d", total);
getchar();
}


Executa: 14

main()
{
int num1 = 5; // 0101 em binário
int num2 = 10; // 1010 em binário
int total = num1&num2;
/* O operador & é chamado AND, vale 1 quando os
* dois digitos binários em comparação forem 1.
* 5 = 0101
* 10 = 1010
* -----------
* 0 = 0000
*/
printf("%d", total);
getchar();
}


Executa: 0

Função sizeof():

main()
{
int tamanho;
tamanho = sizeof(int);
printf("O compilador usa %d bytes para armazenar um int na memória.", tamanho);
getchar();
}


Executa: O compilador usa 4 bytes para armazenar um int na memória. (Lembre-se, o tamanho depende do compilador).
1 byte representa 8 bits, então são 8x4 bits = 32 bits. Ou seja, são 31 bits(espaços) para armazenar dígitos e 1 bit para armazenar o sinal.
Da mesma forma, onde você digitou int (entre os parênteses da função sizeof() - depois explicarei como isso funciona), pode digitar outros tipos de dados, como:

- char, long int, unsigned char, unsigned int, unsigned long int, float e double.
Obs: nos especificadores unsigned só são armazenados dígitos, o sinal é sempre positivo.

Capítulo 2 - Iniciando com printf()

As sequências de escape e os especificadores de formato são úteis para a formatação das informações de saída na tela do MS-DOS. São utilizados no comando de impressão formatada, ou seja, a função printf().

1. Sequências de Escape:

\b - Backspace
\t - Tabulação
\r - Return (retorna o cursor no início da pan style="font-weight: bold;">%c - Caractere
%s - String de caracteres com terminador nulo (\0)
%p - Valor de ponteiro
%% - Porcentual

Obs: mostrando apenas comandos úteis.
Obs: para expressar ponto flutuante, recomendo %g ou %G que exibe qualquer formato.

Exemplos de programas:

main()
{
printf("O numero %d e um numero par.", 2);
printf("O numero %g e um ponto flutuante.", 3.14);
printf("O numero %o representa o numero 10 em octal");
printf("O letra %c e a primeira do alfabeto.", A);
printf("Meu nome e: %s", Andre\0);
printf("Meu conhecimento aumentou em 120%%");
}


Obs: o prompt do DOS tem problemas com acentuação. Para mostrar alguns caracteres acentuados temos que usar a tabela ASCII. Deixei de acentuar para facilitar o entendimento.
Obs: no número de ponto flutuante, utilizamos ponto para separar as casas decimais pois é o padrão americano. Se usarmos vírgula, teremos um erro de compilação.
Obs: ao receber uma string com o especificador de formato %s, temos que colocar o chamado terminador nulo (\0) "barra zero" depois do último caractere, sinalizando que é o último.
Obs: o especificador de formato %p terei que explicar depois.

3. Especificadores de precisão:

Obs: Entenda underline como espaço em branco na execução dos programas.

main()
{
printf("%d%2d%3d", 7, 8, 9);
}


Executa: 7_8__9 (Entenda underline como espaço em branco).
O 7 será exibido com 1 espaço, pois é padrão que quando digitamos um número o espaço reservado para ele seja o número de dígitos que colocamos. Como colocamos só um dígito (7), foi reservado apenas um espaço para ele. O 8 é exibido com 1 espaço adicional à esquerda, pois reservamos 2 espaços, um para o número e outro que ficou desocupado. O 9 é exibido com 2 espaços adicionais à esquerda.

main()
{
printf("%f", 535.45);
}


Executa: 535.450000
Por que esses 4 zeros à direita? Por que o limite imposto ao número ponto flutuante em casas decimais à direita é 6. Só 6 casas depois da vírgula (ponto em notação americana). Então o compilador automaticamente preenche com zeros.

main()
{
printf("%16f", 535.45);
}


Executa: ______535.450000
Os quatro zeros a direita se mantém. No total são 10 digitos contando com o ponto, mais 6 espaços à esquerda. Ou seja, são reservadas 16 posições para exibir o número ponto flutuante, que especificamos entre o "%" e o "f" no programa.

main()
{
printf("%6.2f", 12.3456);
}


Executa: _12.35
O número 6 (entre % e f) na função printf() indica que serão reservadas 6 posições. São 5 dígitos contando com o ponto e mais 1 espaço à esquerda. O numero 2 após o ponto, dentro da função printf() indica que serão reservadas apenas 2 posições após a vírgula (ponto em americano). Perceba que o compilador executa .35, então ele faz um arredondamento quando o dígito à direita é maior que 5.

Obs: Em todos esses programas, perceba que os valores estão sempre se alinhando pela direita, deixando espaços vazios à esquerda. Podemos alinhar à esquerda colocando um sinal de menos
(-) antes do número que define a quantidade de posições:

main()
{
printf("%-16f", 535.45);
}


Executa: 535.450000______

Capítulo 1 - Criando seu primeiro programa em C

As sequências de escape e os especificadores de formato são úteis para a formatação das informações de saída na tela do MS-DOS. São utilizados no comando de impressão formatada, ou seja, a função printf().

1. Sequências de Escape:

\b - Backspace
\t - Tabulação
\r - Return (retorna o cursor no início da linha atual).

2. Especificadores de formato:

%c - Caractere
%s - String de caracteres com terminador nulo (\0)
%% - Porcentual

Exemplos de programas:

main()
{
printf("O numero %d e um numero par.\n", 2);
printf("O numero %g e um ponto flutuante.\n", 3.14);
printf("O numero %o representa o numero 10 em octal\n", 12);
printf("O letra %c e a primeira do alfabeto.\n", 'A');
printf("Meu nome e: %s\n", "Andreas\0");
printf("Meu conhecimento aumentou em 120%%");
}



Obs: no número de ponto flutuante, utilizamos ponto para separar as casas decimais pois é o padrão americano. Se usarmos vírgula, teremos um erro de compilação.
Obs: ao receber uma string com o especificador de formato %s, temos que colocar o chamado terminador nulo (\0) "barra zero" depois do último caractere, sinalizando que é o último.

3. Especificadores de precisão:


main()
{
printf("%d%2d%3d", 7, 8, 9);
}


Executa: 7_8__9 (Entenda underline como espaço em branco).
O 7 será exibido com 1 espaço, pois é padrão que quando digitamos um número o espaço reservado para ele seja o número de dígitos que colocamos. Como colocamos só um dígito (7), foi reservado apenas um espaço para ele. O 8 é exibido com 1 espaço adicional à esquerda, pois reservamos 2 espaços, um para o número e outro que ficou desocupado. O 9 é exibido com 2 espaços adicionais à esquerda.

main()
{
printf("%f", 535.45);
}


Executa: 535.450000
Por que esses 4 zeros à direita? Por que o limite imposto ao número ponto flutuante em casas decimais à direita é 6. Só 6 casas depois da vírgula (ponto em notação americana). Então o compilador automaticamente preenche com zeros.

main()
{
printf("%16f", 535.45);
}


Executa: ______535.450000
Os quatro zeros a direita se mantém. No total são 10 digitos contando com o ponto, mais 6 espaços à esquerda. Ou seja, são reservadas 16 posições para exibir o número ponto flutuante, que especificamos entre o "%" e o "f" no programa.

main()
{
printf("%6.2f", 12.3456);
}


Executa: _12.35
O número 6 (entre % e f) na função printf() indica que serão reservadas 6 posições. São 5 dígitos contando com o ponto e mais 1 espaço à esquerda. O numero 2 após o ponto, dentro da função printf() indica que serão reservadas apenas 2 posições após a vírgula (ponto em americano). Perceba que o compilador executa .35, então ele faz um arredondamento quando o dígito à direita é maior que 5.

Obs: Em todos esses programas, perceba que os valores estão sempre se alinhando pela direita, deixando espaços vazios à esquerda. Podemos alinhar à esquerda colocando um sinal de menos
(-) antes do número que define a quantidade de posições:

main()
{
printf("%-16f", 535.45);
}


Executa: 535.450000______