Pesquisar no blog:

Mostrando postagens com marcador C. Mostrar todas as postagens
Mostrando postagens com marcador C. Mostrar todas as postagens

segunda-feira, 22 de junho de 2015

Bucket Sort

Um exemplo de ordenação por distribuição é o algoritmo bucket sort (ordenação balde). Esse algoritmo cria “baldes” com valores atribuídos a estes, esses baldes receberão os números referente ao seu valor depois estes são “esvaziados” seguindo a ordem.
Exemplo vetor = {2,1,3,5,1,3,3,5,4,2}
Levando em consideração o exemplo acima, seria necessário a criação dos baldes 1, 2, 3, 4 e 5. Os números seriam distribuídos nos baldes da seguinte forma:

 Após isso, basta esvaziar os baldes a partir do primeiro para que os números fiquem na ordem.
Os baldes não necessariamente precisam ser numerados dessa forma, para um conjunto muito grande de números a quantidade de baldes seria muito grande e podemos ainda encontrar vetores que não possuem números repetidos, assim, a criação de um balde para cada vetor faz com que a ordenação se torne muito trabalhosa. Para organizar um conjunto grande de número que não se repetem cada balde pode ter um intervalo. Por exemplo: balde 1 receberá número de 0 a 9, balde 2 de 10 a 19, balde 3 de 20 a 29 e assim por diante. Implementação do bucket sort em C (EPPSTEIN, 1996):
#include
 #define tam_balde 100
 #define num_balde 10
 #define max 10

 typedef struct {
         int topo;
         int balde[tam_balde];
 }balde;

 void bucket_sort(int v[],int tam){
     balde b[num_balde];
     int i,j,k;
     for(i=0;i<num_balde;i++)
             b[i].topo=0;

     for(i=0;i<tam;i++){
             j=(num_balde)-1;
             while(1){
                     if(j<0)
                             break;
                     if(v[i]>=j*10){
                             b[j].balde[b[j].topo]=v[i];
                             (b[j].topo)++;
                             break;
                     }
                     j--;
             }
     }

     for(i=0;i<num_balde;i++)
             if(b[i].topo)
                     bubble(b[i].balde,b[i].topo);

     i=0;
     for(j=0;j<num_balde;j++){
             for(k=0;k<b[j].topo;k++){
                     v[i]=b[j].balde[k];
                     i++;
             }
     }
 }

void bubble(int v[],int tam){
    int i,j,temp,flag;
    if(tam)
    for(j=0;j<tam-1;j++){
        flag=0;
        for(i=0;i<tam-1;i++){
            if(v[i+1]<v[i]){
                temp=v[i];
                v[i]=v[i+1];
                v[i+1]=temp;
                flag=1;
            }
        }
        if(!flag)
        break;
    }
}

int main(int argc, char *argv[])
{
    int v[6] = {5,3,7,1,2,9}, i, j, aux, menor, pos;
    bucket_sort(v, 6);

    //mostrar vetor ordenado
    printf("\nVetor ordenado: ");
    for(i=0;i<6;i++)
        printf("%d, ", v[i]);

}
 

Observe no código acima que o bucket sort não é exatamente um algoritmo de ordenação, mas sim uma distribuição para tornar a organização mais efetiva. Por esse motivo é encontrado no código a função bubble, que é utilizada para organizar os elementos dentro de cada balde. Pode-se substituir o bubble sort por qualquer outro algoritmo de ordenação.

domingo, 21 de junho de 2015

Quick Sort

O quick sort é o método de ordenação mais eficiente pra grande maioria dos casos. Sendo seu pior caso O(n²), mas que raramente ocorre. Geralmente ele ocorre como um O(n log n). O quick sorte faz uso de um pivô, que pode ser definido de maneiras diferentes. A forma que se define o vetor influência em sua eficiência. Uma das formas que o pivô pode ser definido é pelo elemento do meio do vetor. Os elementos menores que o pivô são movidos para direita dele e os maiores para sua esquerda, dessa forma se divide o vetor em esquerda e direita. Este mesmo processo é aplicado novamente em esquerda e direita recursivamente, até que o vetor seja totalmente ordenado. Implementação do Quick Sort em C:
int quickSort(int Vet[],int inicio, int fim) {
int i, j, pivot, aux;
    i = inicio;
    j = fim;
    pivot = Vet[(inicio + fim)/2];
    do {
        while (Vet[i] < pivot && i < fim) i++;
        while (pivot < Vet[j] && j > inicio) j--;
        if (i<=j){
            if (i<j){
                aux = Vet[i];
                Vet[i] = Vet[j];
                Vet[j] = aux ;
            }
            i++;
            j--;
        }
    }while (i<=j);
    if(inicio < j) quickSort (Vet, inicio, j);
    if(i< fim ) quickSort (Vet, i, fim);
    return 0;
}

Observe que para chamar o Quick sort é necessário informar o inicio e o fim, sendo que nos outros podemos definir apenas o tamanho. Isso deve acontecer pois, quando ele for chamado recursivamente os valores do inicio e fim será diferente, nesta etapa que ocorre a divisão do vetor. Por isso é dito que ele usa o método dividir para conquistar. Abaixo, a mesma implementação com a chamada da função no main:
#include <stdio.h>
int quickSort(int Vet[],int inicio, int fim) {
int i, j, pivot, aux;
    i = inicio;
    j = fim;
    pivot = Vet[(inicio + fim)/2];
    do {
        while (Vet[i] < pivot && i < fim) i++;
        while (pivot < Vet[j] && j > inicio) j--;
        if (i<=j){
            if (i<j){
                aux = Vet[i];
                Vet[i] = Vet[j];
                Vet[j] = aux ;
            }
            i++;
            j--;
        }
    }while (i<=j);
    if(inicio < j) quickSort (Vet, inicio, j);
    if(i< fim ) quickSort (Vet, i, fim);
    return 0;
}

int main()
{
    int vet[10] = {9,8,6,1,5,2,6,4,0,6}, i;
    quickSort(vet, 0, 9);
    //imprime vetor ordenado
    printf("Vetor ordenado: ");
    for(i = 0; i < 10; i++)
        printf("%d, ",vet[i]);
}



sábado, 20 de junho de 2015

Merge Sort

A ordenação por intercalação faz a divisão do vetor para organiza-lo, ou seja, dividir o problema em subproblemas. O Merge Sort é um exemplo de algoritmo que usa essa técnica “dividir para conquistar”. Os subproblemas são resolvidos recursivamente, mas se forem suficientemente pequenos podem ser resolvidos de maneira mais simples. Após as solução dos subproblemas estes são combinados na solução do problema original. Para a implementação é necessário uma função pra fazer a divisão dos problemas e outra pra implementar a ordenação por intercalação, no algoritmo abaixo podemos ver respectivamente as funções merge e mergeSort
.
A figura abaixo mostra exatamente oque é feito pelo Merge Sort:

A parte azul da imagem representa a função de intercalação, que faz a divisão do vetor. O vetor é dividido até que fique em várias partes com um elemento. Isso por que um vetor de um elemento já está ordenado.
Na parte verde é feita a união desses "subvetores" ordenando-os durante a sua junção, essa é a ilustração da função merge.

Implementação do merge sort em C:
#include <stdio.h>
void merge(int vet[], int tamanho) {
  int *tmp, meio, i, j, k;

  //alocando memória pro tamanho do vetor
  //exemplo: vetor tamanho 5 int = 4 bytes -> 5*4 = 20bytes alocados.
  tmp = (int*) malloc(tamanho * sizeof(int));
  //tmp armazena endereço inicial do espaço alocado

  //se o tmp for vazio sair da função e retornar 1 (falha)
  if (tmp == NULL) {
    exit(1);
  }

  meio = tamanho / 2; //meio do vetor

  i = 0;
  j = meio;
  k = 0;
  while (i < meio && j < tamanho) {
    if (vet[i] <= vet[j]) {
      tmp[k] = vet[i++];
    }
    else {
      tmp[k] = vet[j++];
    }
    ++k;
  }

  if (i == meio) {
    while (j < tamanho) {
      tmp[k++] = vet[j++];
    }
  }
  else {
    while (i < meio) {
      tmp[k++] = vet[i++];
    }
  }

  for (i = 0; i < tamanho; ++i) {
    vet[i] = tmp[i];
  }

  free(tmp); //liberar espaço alocado
}

void mergeSort(int vet[], int tamanho) {
  int meio;

  if (tamanho > 1) {
    meio = tamanho / 2;
    mergeSort(vet, meio);
    mergeSort(vet + meio, tamanho - meio);
    merge(vet, tamanho);
  }
}

int main(int argc, char *argv[])
{
    int v[10] = {9,8,6,1,5,2,6,4,0,6}, i;
    mergeSort(v, 10); //chamada a função merge

    //mostrar vetor ordenado
    printf("\nVetor ordenado: ");
    for(i=0;i<10;i++)
        printf("%d, ", v[i]);
}

sexta-feira, 19 de junho de 2015

Busca linear ou busca sequencial

A busca linear ou busca sequencial é a forma mais simples de se buscar um resultado em uma lista de dados. O vetor é percorrido comparando cada dado do vetor até encontrar o resultado desejado e retornando o índice do valor encontrado. O dado a ser encontrado é passado como parâmetro para função. O melhor caso nessa situação é se o dado que está sendo procurando é a primeira opção do vetor, e o pior resultado ocorre se este for o ultimo dado do vetor. A complexidade da busca linear é O(n).
Abaixo a implementação desse algoritmo e sua chamada:
#define TAM 10

int buscaLinear(int tamanho, int vetor[], int valor)
{
    int i;
    for(i = 0; i < tamanho; i++)
        if(vetor[i] == valor)
            return i;
        else if(i == tamanho-1)
            return -1;
}

int main(int argc, char *argv[])
{
    int indice, v[TAM] = {9,7,5,2,4,6,10,3,1,8};
    indice = buscaLinear(TAM, v, 6); //chamando a função de busca
    printf("Valor esta na posicao %d", indice);
    getch();
    return 0;
}

Veja também o artigo sobre Busca binária.

Busca binária

A busca binária tem o mesmo objetivo da busca linear, seu tempo de busca é muito mais otimizado que o da busca linear, no entanto, para seu funcionamento, o vetor deve estar ordenado.
Exemplo de vetor ordenado: vetor[10] = {0,1,2,3,4,5,6,7,8,9}; 
A busca binária localiza o meio do vetor com a fórmula (Inicio + Fim) / 2, sendo Inicio a primeira posição do vetor e Fim a ultima posição do vetor. Levando em consideração o exemplo vetor[10] teríamos: (1+10) /2 = 5,5. Em C este valor seria passado para 5, pois ao armazena-lo como inteiro (int) as casas decimais são ignoradas.
Tendo em mãos o meio (5) do vetor ordenado o algoritmo verifica se o valor procurando é ele, se não for ele verificará se é maior ou menor que ele. Sendo maior, a busca será feita apenas na parte direita da metade, se menor, a busca será realizada na parte da esquerda:


O vetor vai sendo divido até que o valor seja encontrado. Se o valor não existir a condição de parada será quando o inicio e o fim for igual e nada foi encontrado. Exemplo do algoritmo de busca binária e sua chamada:
#define TAM 10
//BUSCA BINÁRIA
int buscaBinaria(int vetor[], int tamanho, int valor)
{
    int meio, i, ini = 0, fim = tamanho-1;
    while(ini <= fim)
    {
        meio = (ini + fim) / 2; //encontrando o meio do vetor
        if(valor == vetor[meio]) //valor encontrado?
            return meio; //retorna posição do valor encontrado
        else if(valor < vetor[meio])
            fim = meio - 1;
        else
            ini = meio + 1;
    }
    return -1; //valor não enconrado
}

int main(int argc, char *argv[])
{
    int v[TAM] = {0,1,2,3,4,5,6,7,8,9}, i, indice;

    indice = buscaBinaria(v, TAM, 8);
    printf("\nO valor procurado esta na posicao %d", indice);

    getch();
    return 0;
}


Veja também o artigo sobre Busca linear ou busca sequencial.

domingo, 12 de abril de 2015

Medir tempo de execução em C

Como medir o tempo de execução de um programa ou parte dele?
Talvez você já tenha precisado disso, como eu precisei e tive certa dificuldade. Na verdade é bem simples, oque gera confusão é que muitas vezes o programa é executado tão rapidamente que recebe-se o retorno 0, principalmente se o tempo for mostrado em segundos.
Para a maioria dos casos o ideal é mostra em milissegundos.

Biblioteca time.h

Para fazer uso da função que irá retornar o tempo de execução de um programa é necessário chamar a biblioteca time.h. Aqui no blog já falei sobre ela para fazer o uso das funções rand e srand, para geração de valores aleatórios.
Para chamar essa biblioteca basta por no cabeçalho do seu programa:
#include <time.h>

Dessa vez faremos o uso da função clock, do tipo clock_t, e da macro CLOCKS_PER_SEC.


Função clock() e a macro CLOCKS_PER_SEC

A função clock retorna o tempo de execução exato do momento em que ela foi chamada. Para encontrar o tempo de execução de um programa precisamos usar ela duas vezes, uma para capturar o tempo inicial e outra para capturar o tempo final da execução.
Se fizermos o tempo final - tempo inicial teremos o tempo de execução do programa em milissegundos. Dividindo esse valor pelo CLOCKS_PER_SEC teremos este valor em segundos, pois esta constante tem o valor de 1000000. Para obter o valor em milissegundos, pode-se dividir o CLOCKS_PER_SEC por 1000.
E onde se encaixo o clock_t?
A variável que irá armazenar o valor do tempo da função clock deve ser do tipo clock_t.
Vamos a um exemplo simples usando um algoritmo de ordenção bubble sort:
#include <stdio.h>
#include <stdlib.h>
#include <time.h> //clock(), CLOCKS_PER_SEC e clock_t

#define TAM 10000 //constante para tamanho do vetor

int RandomInteger(int low, int high)
{
    int k;
    srand( (unsigned)time(NULL) );
    k = (rand() % high) + low;
    return k;
}

void bubbleSort (int v[TAM]) {
    int a, b, aux;
    for (a=TAM-1; a>=1; a--) {
        for (b=0; b<a; b++) {
            if (v[b]>v[b+1]) {
                aux = v[b];
                v[b] = v[b+1];
                v[b+1] = aux;
            }
        }
    }
}

int main(){
 clock_t t; //variável para armazenar tempo
 int vetor[TAM]; //vetor com 10000 posições
 int p, r, a;
 p = 0;
 r = TAM;

  //geração aleatório dos valores do vetor
 for(a = 0; a < TAM; a++)
  vetor[a] = RandomInteger(0, TAM);

    //Verificando tempo de execução do bubble sort=> t2
 t = clock(); //armazena tempo
 bubbleSort(vetor);
 t = clock() - t; //tempo final - tempo inicial
 //imprime o tempo na tela
 printf("Tempo de execucao: %lf", ((double)t)/((CLOCKS_PER_SEC/1000))); //conversão para double
}

Observe que ao imprimir o tempo em segundos, como explicado anteriormente, o CLOCKS_PER_SEC foi dividido por 1000, para apresentar o tempo em milissegundos.

quinta-feira, 27 de junho de 2013

Linguagem C: Manipulação de Strings

A linguagem C nos permite manipular um vetor de caracteres que, em outras linguagens, são chamados de Strings. Então, podemos afirmar que a linguagem C também existe esse tipo de dado. Sendo assim, há na linguagem C diversas funções que nos auxiliam a manipular as Strings, evitando a necessidade do uso de grande quantidade de linhas de códigos.
Sendo assim, há na linguagem C diversas funções que nos auxiliam a manipular as Strings, evitando a necessidade do uso de grande quantidade de linhas de códigos.

FUNÇÃO STRLEN

A função strlen é acrônimo de String Length, e como o próprio nome sugere usamos essa função para encontrar o tamanho de uma string, ou seja, a quantidade de caracteres.
A função strlen retorna um valor inteiro referente à quantidade de caracteres da função. Essa contagem é feita passando caractere por caractere da string até chegarmos ao caractere ‘\0’ que, na linguagem C, é o nulo (NULL). É importante sabermos que todas as strings são terminadas com nulo, ou seja, enquanto o caractere nulo (‘\0’) não for encontrado é incrementado 1 (um) ao contador e é passado para o próximo caractere.
A função strlen tem várias aplicações, podemos usar como exemplo a necessidade de contar a quantidade de caracteres para verificar se o usuário informou corretamente um documento como o CPF que tem sempre 11 (onze) caracteres. Chamada da função: strlen(vetor); que o usuárioeja contar. Para melhor entendimento veja a aplicação da função strlen:
main()
{
    char *teste = "Manipulaçao de Strings";
    int tam = strlen(teste);
    printf("%d\n",tam);
    getch();
}

Esse programa conta a quantidade de caracteres de teste e imprime na tela 22.
Abaixo vemos uma função com o mesmo funcionamento da função strlen:
int tamstr(char *str)
{
    int i = 0;
    while(*(str+i) != '\0') i++; 
    return i; 
}

FUNÇÃO STRCAT

main()
{
    char teste[] = "Manipulacao de Strings", teste2[] = " - strcat";
    printf("%x\n", teste);
    strcat(teste,teste2);
    puts(teste);
    getch();
}

No programa acima concatenamos a string “teste” com a string teste2 informando a função “strcat(teste,teste2);”. A concatenação é feita no primeiro vetor (teste = “Manipulacao de Strings - strcat”) e o segundo vetor permanece com o mesmo conteúdo (teste2 = “- strcat”).

FUNÇÃO STRCMP

A função strcmp é acrônimo de String Comparison, e como o próprio nome sugere usamos essa função para comparar duas strings. A função STRCMP compara as strings, e devolve apenas 2 valores, 0 caso forem iguais (verdadeiro), um valor maior que zero que as strings não são iguais e que o primeiro caractere que não é igual entre elas é maior na primeira string, já um valor abaixo de zero o caractere diferente é maior na segunda string. A comparação termina após a função encontrar o NULO na string ou ao encontrar algum caractere diferente.
Exemplo 1:
main()
{
    char teste[] = "Manipulacao de Strings", teste2[] = "Manipulacao de Strings";
    strc== 0 ? puts("iguais") : puts("Sao diferentes");
 getch();
}
No exemplo acima o retorno é 0, ou seja, as strings são iguais.

Exemplo 2:
main()
{
    char teste[] = "Manipulacao de Strings", teste2[] = "Manipulacao de strings";
    printf("\n\n%d \n\n", strcmp(teste,teste2));
    strcmp(teste,teste2) == 0 ? puts("Sao iguais") : puts("Sao diferentes");
    getch();
}
Observe que a letra 's' de uma das string está em maiúsculo e da outra em minusculo. Sendo assim, nesse segundo exemplo o retorno é -1, por que S < s, que equivalem a a 83 e 115 na tabela ASCII respectivamente.

FUNÇÃO STRCPY

A função strcpy é acrônimo de String Copy, e como o próprio nome sugere usamos essa função para copiar o conteúdo de uma string para outra. A função pode vir a ser muito útil podendo copiar todo o conteúdo de uma string para a outra, ou podemos também copiar strings a partir de posições específicas, mas deve-se estar atento em seu tring é copiadra outro lugar, esta pode se sobrepor em outra, ou pode não ter espaço suficiente para esta, o que pode acabar ocorrendo um comportamento não definido. Em seguida veja um exemplo de seu uso:
main()
{
    char teste[] = "Manipulacao de Strings", teste2[] = "strcpy - ";
    strcpy(&teste2[9],&teste[15]);
    puts(teste2);
    getch();
}

Neste programa a string teste está sendo copiada para a string teste2, no entanto está sendo indicado de qual posição isso deve ser feito, então, observe que está sendo copiado de teste2 todo o conteúdo a partir da posição 15 para a string teste a partir da posição 9, logo o resultado deste programa é “strcpy – Strings”.

FUNÇÃO TOLOWER

A função tolower pa uma string uum para minúsculo desde que este pertença as letras de A-Z.
A função retorna o caractere convertido para minúsculo. Essa função pode ser usada para padronizar os dados de um programa tornando qualquer caractere maiúsculo digitado pelo usuário em um caractere minúsculo. Exemplo:
main()
{
    char teste[] = "MANIPULACAO DE STRINGS";
    int i;
    puts(teseste[i] != '\0+)
        putchar(tolower(teste[i])); 
    getch();
}

A saída desse programa será: manipulação de strings.
A implementação dessa função é simples e se faz com o auxílio da tabela ASCII:
char Mtom(char st){
    if((st >= 65)&&(st <= 90)){
        return(st + 32);
    return(st);
    }
}

FUNÇÃO STRLWR

Além da função tolower, existe outra função com o objetivo semelhante. A função strring inteira nvés de fazer caractere por caractere.
Exemplo:
main()
{
    char str[] = "MANIPULACAO DE STRINGS!";
    strlwr(str);
    puts(str);
    getch();
    return 0;
}

A saída será "manipulacao de strings!".

FUNÇÃO TOUPPER

A função toupper passa caracteres de uma string um a um para maiúsculo desde que este pertença as letras de a-z.
A função retorna o caractere convertido para maiúsculo. Essa função pode ser usada para padronizar os dados de um programa tornando qualquer caractere minúsculo digitado pelo usuário em um caractere maiúsculo. Exemplo:
main()
{
    char teste[] = "manipulacao de strings";
    int i;
    puts(teste);
    for(i=0;teste[i] != '\0';i++)
        putchar(tolower(teste[i])); 
    getch();
}

FUNÇÃO STRUPR

Além da função toupper, existe outra função com o objetivo semelhante. A função strupr converte a string inteira para maiúsculo ao invés de fazer caractere por caractere.
Exemplo:
main()
{
    char str[] = "manipulacao de strings!";
    strupr(str);
    puts(str);
    getch();
    return 0;
}

A saída será "MANIPULACAO DE STRINGS!".

FUNÇÃO STRSTR

strstr(string1, string2);
A função strstr deve ser chamada com 2 parâmetros do tipo string, e procura a primeira ocorrência de string1 em string2. Se não for encontrado é retornado NULL e se encontrado é retornado um ponteiro apontando para a primeira ocorrência de string2 em string1.
Exemplo:
#include 
main()
{
    char str1[] = "strings", str2[] = "Manipulacao de strings!";
    if(strstr(str2, str1) == NULL)
        printf("Nao ha ocorrencia.\n");
    else
        printf("Endereco HEX: %x\n",strstr(str2, str1));
    getch();
    return 0;
}

Linguagem C: Laço de repetição do / while

A diferença do "do/while" para o laço "while" é que a condição e verificada no final de sua estrutura. Dessa forma ele sempre vai executar, pelo menos, uma vez.
Exemplo:
main()
{
      int i = 10;
      do{
          printf("O laco do-while eh executado pelo menos uma vez.");
      }while(i < 10);
      getch();
      return 0;
}

Observe no código acima que mesmo o contador "i" já estando em 10 o laço ainda imprimiu a linha uma vez (Observe sua condição no final da estrutura na linha 6).
Um exemplo muito útil é um menu em linguagem C. O menu deve aparecer na tela independente da condição, obviamente, e o usuário escolhe a seguir a opção que deseja executar:
main()
{
      char op;
      do{
          system("cls"); //limpar tela (windows)
          printf("1- Opcao 1 \n2- Opcao 2 \n3- Opcao 3 \n0- Sair\n");
          op = getch();
          switch(op)
          {
               case '1': 
                    system("cls");
                    printf("Voce escolheu a opcao 1\n");
                    getch(); break;
               case '2': 
                    system("cls");
                    printf("Voce escolheu a opcao 2\n"); 
                    getch(); break;
               case '3': 
                    system("cls");
                    printf("Voce escolheu a opcao 3\n"); 
                    getch(); break;
               case '0': return 0;
               default: printf("Opcao invalida!\n");
          }
      }while(op != 0);
      getch();
      return 0;
}
Obviamente as dentro de cada caso do switch deveríamos substituir pelas instruções da opção escolhida pelo usuário. Faça o teste: substitua no código acima as instruções para criar uma calculadora que faça soma, divisão, subtração e multiplicação. Crie as variáveis necessárias e substitua o nome das opções.

quarta-feira, 26 de junho de 2013

Linguagem C: Laço de repetição while

O laço de repetição "while" é semelhante ao laço for. A diferença é que em sua sintaxe informamos apenas a condição:
main()
{
    int i = 0;
    while(i < 10)
    {
        printf("%d, ", i);
        i++;
    }
    getch();
    return 0;
}

Devemos ter cuidado para não criar laços de repetição que nunca atingirão a condição.
Observe que esse simples laço de repetição acima pode ser criado também com for.
Então, quando usar o "while" e quando usar o "for"?
Como observamos, no "while" não definimos o valor de um contador e seu fim. Isso será definido através dos comandos e cálculos que serão executados dentro de sua estrutura ou que serão informados pelo usuário. Por ter essa característica, podemos usa-lo quando não conhecemos o fim da execução do laço.
Exemplo:
Criar um programa que diga para o usuário quantos meses ele precisará para guardar uma determinada quantia de dinheiro.
main()
{
    float qnttotal, qntmes;
    int totalmeses = 1;
    printf("Informe o total que deseja guardar: ");
    scanf("%f",&qnttotal);
    printf("Informe quanto ira guardar por mes: ");
    scanf("%f",&qntmes);
    while((qntmes * totalmeses) < qnttotal)
        totalmeses++;
    printf("Sera necessario %d meses", totalmeses);
    getch();
    return 0;
}

segunda-feira, 10 de junho de 2013

Linguagem C: Laço de repetição for

Com o laço de repetição "for" podemos criar um bloco que será repetido enquanto uma determinada condição não for alcançada.
Podemos dessa forma evitar digitar código extensos. Imaginem, por exemplo, que você precisa digitar 20 vezes a mesma linha. Digitar 20 vezes o printf() com a linha que quer imprimir não é a melhor solução. Aumente isso para 50, pra 100, nada viável digitar tantas linhas para fazer a mesma coisa.
Podemos fazer com que o mesmo bloco repita pela quantidade de vezes que desejamos:
main()
{
    int i;
    for(i = 0; i < 20; i++)
        printf("Linha %d\n", i+1);
    getch();
    return 0;
}
No exemplo acima sera impresso:
"Linha 1"
Como o contador ainda não chegou em seu limite é incrementado 1 ao valor de i e impresso:
"Linha 2"
"Linha 3"
...
"Linha 20"
No momento da impressão é somando 1 ao valor do contador i por que ele começa em 0. O laço é executado de 0 a 19.
O laço for é formado por três partes:
for(inicio da variável ; enquanto <condição> ; incremento)
Podemos ler o for(i = 0; i < 20; i++) da seguinte forma:
para( i começando em 0 ; enquanto i for menor que 20 ; incrementar i de 1 em 1)
Deve-se tomar muito cuidado ao usar laços de repetições, pois pode-se criar laços infinitos. Ou seja, nunca será atingida a condição para parada do laço.
Quando o for possuir mais de uma linha em sua estrutura há a necessidade de inserir chaves para definir o inicio e o fim da estrutura do for. Exemplo:
main()
{
    int i;
    for(i = 0; i < 20; i++)
    {
        if((i+1) % 2 == 0)
            printf("Linha %d\n", i+1);
    }
    getch();
    return 0;
}

Laço for aninhado

For aninhado é o uso de um for dentro de outro. Quando isso ocorre o laço que está mais interno é executado até o fim para depois voltar a mais externo e repetir o laço interno novamente. De modo mais simples, oque estaremos fazer é repetir uma repetição.
Exemplo:
main()
{
    int i, j;
    for(i = 0; i < 5; i++)
        for(j = 0; j < 3; j++)
            printf("i=%d E j=%d\n", i, j);
    getch();
    return 0;
}
Ao entrar no primeiro for o i é 0 (i=0) logo em seguida o programa já entra em outro laço de repetição onde o j é 0 (j=0) e imprime:
i=0 E j=0
E volta para o for mais interno e incremento 1 no j (j=1) e imprime:
i=0 E j=1
Observe que não saímos da primeira repetição do for do contador i. Será feita toda repetição do for do contato j para depois incrementar em e i e novamente fazer o for do contador j de 0 a 2 e assim até finalizar as 5 repetições do primeiro for.
Veja o resultado:

Bastar multiplicar a quantidade de repetições do primeiro laço (5) com a quantidade de repetições do segundo (3) e descobrimos quantas vezes será executada a instrução: 5 * 3 = 15.

domingo, 9 de junho de 2013

Estrutura condicional switch / case

Além da estrutura condicional if/else podemos utilizar o switch/case para avaliar condições.
Essa estrutura trabalha com uma variável, que, para cada valor vai gerar um caso (case).
Exemplo:
main()
{
    int op = 1;
    switch(op)
    {
        case 1: printf("Um"); break;
        case 2: printf("Dois"); break;
    }
    getch();
}
Na linha 4 é passada a variável que será avaliada e entre as chaves na linha 6 e 7 vemos 2 casos possíveis.
Caso "op" tenha o valor 1 será impresso "Um", caso "op" tenha o valor 2 será impresso "Dois". Devemos colocar o break ao final de cada caso.
Se "op" estiver com um valor não tratado nos casos nada será executado. Podemos tratar isso colocando o default que é executado se o valor não existe em nenhum dos casos.
main()
{
    int op = 3;
    switch(op)
    {
        case ((1) || (2)): printf("Um ou dois"); break;
        case 0: printf("Zero"); break;
        default: printf("Invalido.");
    }
    getch();
}

Observe que essa estrutura switch/case é muito parecida com o if/else, porém, mais organizado.
Porém, switch/case trata uma única variável, sendo assim, o if/else não é dispensado, devemos analisar qual das duas estruturas deve ser utilizada.d
Observe que neste segundo exemplo ainda foi utilizado na linha 6 o operador lógico OR (||). Caso seja 1 ou 2 será impresso "Um ou dois".

Calculadora de operações básicas com switch / case:


int main()
{
    float a, b, r;
    char op;
    printf("+: soma \t-: subtracao \t/: divisao \t*: multiplicacao\n");
    printf("Digite a operacao: ");
    scanf("%f %c %f", &a, &op, &b);
    switch(op)
    {
        case '+' : r = a + b; break;
        case '-' : r = a - b; break;
        case '*' : r = a * b; break;
        case '/' :
            {
                if(b != 0)
                    r = a / b;
                else
                {
                    printf("#ERRO# DIVISAO POR 0.");
                    return 1;
                }
                break;
            }
    }
    printf("Resultado: %.2f", r);
    getch();
    return 0;
}

terça-feira, 26 de março de 2013

Estrutura condicional if, else, else if (Linguagem C)

A estrutura condicional if é conhecida em todas as linguagens de programação. Sua estrutura muda pouco de uma linguagem para outra, e, desde que você tenha uma boa lógica, é fácil usa-lá.
A estrutura condicional está no seu dia-a-dia. Antes de sair de casa, por exemplo, podemos olhar pela janela verificar como está o tempo: se o tempo estiver nublado e com nuvens carregadas, levarei o guarda-chuva.
É exatamente dessa mesma forma que funciona a estrutura condicional de uma linguagem de programação. Posso através dele verificar se um determinado valor é igual (==) ao valor de outra variável, se é maior (>), se é menor, maior igual (>=) ou menor igual (<=). A verificação que farei vai depender do valor que eu desejo que seja retornado.
Antes de usar a estrutura condicional if é importante que você entenda os operadores relacionais e os operadores lógicos.


if - Estrutura simples:


A estrutura simples contém apenas uma condição. Se está condição for verdadeira (1) o programa executa sua(s) instrução(ões).
Exemplos:
main()
{
 int a, b;
 printf("Informe 2 valores inteiros: ");
 scanf("%d %d", &a, &b);
 if(a>b)
  printf("A variavel a eh maior que b\n");
 system("pause");
}
Nesse código o usuário entra com os valores da variável a e da variável b e dentro da estrutura condicional é comparado se a é maior que b. Se verdadeiro o programa retorna a mensagem: "A variável a eh maior que b", ou continua em system("pause");. Se houvesse mais linhas abaixo da estrutura condicional o programa continuaria a execução. Exemplo:
main()
{
 int a, b;
 printf("Informe 2 valores inteiros: ");
 scanf("%d %d", &a, &b);
 if(a>b)
  printf("A variavel a eh maior que b\n");
 printf("Nesta estapa o programa ja esta fora da estrutura condicional.\n");
 printf("Pressione uma tecla...\n\n");
 getch();
 printf("O programa encerrara.\n");
 system("pause");
}
Nesse exemplo a estrutura condicional executa uma única instrução. Quando há mais de uma instrução a ser executada devemos fazer uso das chaves:
if(condição)
{
instrução1;
instrução2;
...;
 instruçãoN;
}
Obs.: se a estrutura condicional possuir apenas uma instrução também é permitido o uso da chave.
Exemplo:
main()
{
 int a, b, aux;
 printf("Informe 2 valores inteiros: ");
 scanf("%d %d", &a, &b);
 if(a>b)
 {
  aux = a;
  a = b;
  b = aux;
 }
 printf("Os números foram ordenados: %d, %d\n\n", a, b);
 printf("O programa encerrara.\n");
 system("pause");
}
No código acima se a primeira variável (a) for maior que a segunda (b) o programa inverte seus valores para imprimi-las na ordem.

if, else - Composta:

A estrutura composta faz uso do else (senão), indicando outra(s) condição(ões) caso a primeira não seja executada.
Exemplo:
main()
{
 char cor;
 printf("Escolha uma cor: \n0: Preto\n1: Azul\n2: Verde\n");
 scanf("%c", &cor);
 if(cor == '0')
 {
  system("color 0");
  printf("Voce escolheu a cor preta.\n");
    }
 else if(cor == '1')
 {
  system("color 1");
  printf("Voce escolheu a cor azul.\n");
    }
 else if(cor == '2')
 {
  system("color 2");
  printf("Voce escolheu a cor verde.\n");
    }
 else
  printf("Invalido.\n");
 printf("\nO programa sera encerrado.\n");
 system("pause");
}

No código acima é feita a primeira condição if(cor == '0'), se esta não for verdadeira então é passada para a segunda condição else if(cor == '1'), se esta não for verdadeira é passado para a terceira else if(cor == '2') e por fim colocamos apenas um else que indica que se não for nenhuma da alternativas anteriores será dada a mensagem "Invalido".
Apenas UMA das condições pode ser executada, e assim que uma delas for executada o programa é jogado para fora da estrutura condicional. Observe que toda a estrutura condicional do código acima faz parte de uma unica estrutura, outra seria aberta se iniciássemos outro if.

if, else - Estrutura aninhada:

A estrutura aninhada é, basicamente, um if dentro de outro. Ou seja, se a primeira condição for verdadeira (1) poderei ter uma segunda condição, terceira, quarta, etc.
Exemplo:
main()
{
 int a = 0, b = 2, c = 0, d = 10, e = -8;
 if((a == 0) && (b == 2))
 {
  if(c > 0)
   printf("Comando 1\n");
  else if(d >= 10)
  {
   if(e < 0)
    printf("Comando 2\n");
   else
    printf("Comando 3\n");
  }
        }
    printf("O programa sera encerrado.\n");
    system("pause");
}

main()
{
 int a = 0, b = 2, c = 0, d = 10, e = -8;
 if((a == 0) && (b == 2))
 {
  if(c > 0)
   printf("Comando 1\n");
  else if(d >= 10)
  {
   if(e < 0)
    printf("Comando 2\n");
   else
    printf("Comando 3\n");
  }
        }
    printf("O programa sera encerrado.\n");
    system("pause");
}
Observe que neste código a 3 estruturas condicionais uma dentro da outra. A estrutura azul está dentro da estrutura verde que está dentro da estrutura vermelha. Sendo assim a estrutura na cor verde só é executa se a estrutura vermelha for verdadeira e a estrutura azul só é executada se a primeira condição da estrutura verde (if(c > 0)) for falsa e a segunda (else if(d >= 10)) verdadeira.
Os operadores lógicos podem ser usados para simplificar seu código fonte, não havendo necessidade de criar estruturas condicionais aninhadas tão complexas.
É importante você testar todos os possíveis fluxos do seus dados, evitando erro de lógica. Para isso podemos usar a depuração (Debugging).
Há ainda a estrutura switch / case, que faz uma função parecida com a da estrutura if, mais em alguns casos pode ser mais adequada e mais legível.

segunda-feira, 25 de março de 2013

Operadores lógicos - Linguagem C

Os operadores lógicos são de extrema importância para qualquer programador. Eles são muito utilizados em conjunto dos operadores relacionais, então, se você ainda não sabe como usa-los acesse: Operadores relacionais.
Operadores lógicos:
Operador
Leitura
&&
And (E)
||
Or (Ou)
!
Not (Não)


&&: o operador And (E) pode verificar diversas comparações feitas com os operadores relacionais. Ele retornará Verdadeiro (1) sempre que TODAS as comparações forem verdadeiras, se uma das expressões for falsa o valor retornado será Falso (0).
Ou seja:
Verdadeiro E Verdadeiro = Verdadeiro
Verdadeiro E Falso = Falso
Falso E Falso = Falso
Exemplos:
a) (5<=5)&&(2<4) = 1
Resolução a) (Verdadeiro)&&(Verdadeiro) = Verdadeiro

b) (2<4)&&(1>5) = 0
Resolução a) (Verdadeiro)&&(Falso) = Falso


c) (5<=5)&&(2<4)&&(2>4) = 0
Resolução a) (Verdadeiro)&&(Verdadeiro)&&(Falso) = Falso

||: o operador Or (Ou) pode verificar diversas comparações feitas com os operadores relacionais. Ele retornará Verdadeiro (1) se pelo menos uma das expressões for verdadeira e Falso (0) quando todas as expressões forem falsas.
Ou seja:
Verdadeiro Ou Verdadeiro = Verdadeiro
Verdadeiro Ou Falso = Verdadeiro
Falso Ou Falso = Falso
Exemplos:
a) (5<=5)||(2<4) = 1
Resolução a) (Verdadeiro)||(Verdadeiro) = Verdadeiro

b) (2<4)||(1>5) = 1
Resolução a) (Verdadeiro)||(Falso) = Verdadeiro

c) (3<3)||(2<4)||(2>4) = 0
Resolução a) (Falso)||(Verdadeiro)||(Falso) = Verdadeiro

d) (3<3)||(3<1)||(2>4) = 0
Resolução a) (Falso)||(Falso)||(Falso) = Falso

!: o operador Not (Não / Negação) inverte um valor Verdadeiro (1) para Falso (0) ou Falso (0) para Verdadeiro (1). Simples assim: Não verdadeiro é Falso e Não falso é Verdadeiro.
Exemplos:
a) !(5<=5) = 0
Resolução a) Observe que a expressão (5<=5) é verdadeira, mais o operador Not (!) fora da expressão está fazendo sua negação. Temos então: Não(Verdadeiro) = Falso.

b) !((3==2)&&(7>5)) = 1
Resolução b) 
Observe que neste caso usamos uma expressão com o operador And (&&) e fora dele fizemos a negação (!). Acompanhe nas duas linhas abaixo a resposta:
Não((Falso)&&(Verdadeiro))
Não(Falso) = Verdadeiro

Como você observou podemos usar vários operadores dentro da mesma expressão, e isso é muito comum.
Apesar de algumas expressões parecerem complicadas, elas podem facilitar muito seu código fonte.
Lembre-se de sempre fazer o uso dos parenteses para indicar as expressões que devem ser lidar primeiro.
Veja alguns exemplos de expressões com vários operadores lógicos e acompanhe suas resoluções para praticar:
a)((2==2)&&(9>0))||((6>=6)&&(!(0<=8)))
((Verdadeiro)E(Verdadeiro)) Ou ((Verdadeiro)E(Não(Verdadeiro)))
(Verdadeiro) Ou ((Verdadeiro)E(Falso))
(Verdadeiro) Ou (Falso) = Verdadeiro

b) !(((4==2)||(-9<1)||((2<2)&&(4<8)))&&(!((6==6)&&(2==2))))
Não(((Falso)Ou(Verdadeiro)Ou(Falso))) E (Não((Verdadeiro)E(Verdadeiro))))
Não((Verdadeiro) E (Não(Verdadeiro)))
Não((Verdadeiro) E (Falso)) = Verdadeiro

Veja também:

domingo, 24 de março de 2013

Operadores Relacionais - Linguagem C

Os operadores relacionais são utilizados para fazer comparações entre valores. E o computador retornará 0 (falso) ou 1 (verdadeiro). São muito utilizados dentro de estruturas condicionais para definir o fluxo dos dados.
Operadores relacionais:
Operador 
Leitura
==
Igual (comparação)
< 
Menor
<=
Menor ou igual
> 
Maior
>=
Maior ou igual

Ao fazer alguma comparação a linguagem de programação retornará FALSO ou VERDADEIRO, ou melhor, 0 ou 1.
Muita atenção no uso do operador relacional == (comparação), pois existe ainda o operador = (atribuição).
Exemplo:
main()
{
 int a = 5, b = 10;
 printf("%d\n", a==b);
 b = 5;
 printf("%d\n", a==b);
 system("pause");
}

Observe no código acima a diferença entre os operadores.
Na primeira linha dentro da função main usei o operador de atribuição duas vezes, definindo um valor para a (5) e um valor para b (10).
Depois dentro da função printf pedi a impressão de a==b, substituindo seus valores dentro do primeiro printf teriamos: 5==10, ou seja, 0 (falso).
Logo abaixo do primeiro printf usei o operador de atribuição outra vez, dizendo que a variável b passará a ter o valor 5.
Após trocar os valores usei outra vez, na próxima linha, a comparação entre a e b. Neste segundo printf temos a comparação 5==5, ou seja, 1 (verdadeiro).

Exemplos dos outros operadores relacionais:
Maior:
5>5 = 0 Leitura: (5 é maior que 5?) = Falso
5>7 = 0 Leitura: (5 é maior que 7?) = Falso
5>2 = 1 Leitura: (5 é maior que 2?) = Verdadeiro

Maior ou igual:
5>=5 = 1 Leitura: (5 é maior ou igual a 5?) = Verdadeiro
5>=7 = 0 Leitura: (5 é maior ou igual a 7?) = Falso
5>=2 = 1 Leitura: (5 é maior ou igual a 2?) = Verdadeiro

Menor:
5<5 = 0 Leitura: (5 é menor que 5?) = Falso
5<7 = 1 Leitura: (5 é menor que 7?) = Verdadeiro
5<2 = 0 Leitura: (5 é menor que 2?) = Falso

Menor ou igual:
5<5 = 1 Leitura: (5 é menor ou igual a 5?) = Verdadeiro
5<7 = 1 Leitura: (5 é menor ou igual a 7?) = Verdadeiro
5<2 = 0 Leitura: (5 é menor ou igual a 2?) = Falso

Lembre-se: 
falso: 0
verdadeiro: 1

Veja também:

quarta-feira, 13 de março de 2013

Depuração (Debugger) no Code::Blocks

Depuração é uma ferramenta disponibilizada pela maioria das IDEs para facilitar o teste de um código fonte.
Com ele você pode executar seu programa "passo-a-passo" e observar o fluxo dos seus dados dentro das estruturas if/else, switch/case, a quantidade de vezes que se está executando um  laço de repetição (for, while, do/while), verificar o valor das variáveis, acompanhar funções, etc.
Dessa forma você consegue descobrir erros lógicos. Pequenos detalhes que podem fazer seus programas não funcionar como deveria.
A depuração é de extrema importância para qualquer programador, até mesmo para os iniciantes, já que acompanhar o código linha-a-linha e fazendo seus cálculos e comparações lógicas no "dedo", em um papel ou com sua própria memória seria muito mais difícil.

Como utilizar a depuração no Code::Blocks

A depuração no Code::Blocks é bem simples de se usar. A unica coisa que você deve fazer é salvar o seus código fonte como um projeto. Apenas dessa forma você pode depurar seu programa.
Então se você já tem algum código fonte você deverá copia-lo para um arquivo que faça parte de um projeto.

IMPORTANTE: Ao criar o projeto desative a opção "Release" no assistente de novo projeto.
IMPORTANTE: O arquivo com a função principal deve se chamar "main.c" ou a depuração não funcionará!
#define TAM 10
int main(int argc, char *argv[])
{
    int i, j, aux, troca, v[TAM] = {7,0,5,1,4,2,6,3,-8,-1};
    for(i = 0; i < TAM; i++)
    {
          troca = 0;
          for(j = 0; j < (TAM-1)-i; j++)
          {
                if(v[j] > v[j+1])
                {
                    aux = v[j];
                    v[j] = v[j+1];
                    v[j+1] = aux;
                    troca = 1;
                }
          }
          if(troca == 0)
             break;
    }
    printf("\n\nOrdenado: \n");
    for(i = 0; i < TAM; i++)
    {
         printf("%d, ", v[i]);
    }
    system("pause");
    return 0;
}
Este código é uma das soluções propostas para ordenação de valores. Sendo este o mais simples de todas as soluções propostas (Bubble Sort).
Para que a depuração pare em determinados pontos você pode utilizar os breakpoints clicando na barra cinza ao lado do código fonte próximo aos números das linhas:






Para iniciar a depuração no Code::Blocks você pode utilizar a opção Debug/Start ou o atalho F8.

Observe que ao iniciar a depuração é feita uma seta amarela na linha onde o programa esta parado:


Você pode controlar a depuração através de opções oferecidas pela IDE:
Stop debugger: Para a execução do programa.
Continue: executa até o próximo breakpoint.
Next Line: executa a próxima linha.
Step into: entrar em uma função.
Step out: sair de uma função.
Start: Iniciar.
Debugging windows: Você pode acessar a opção "Debugging windows" através do meneu Debug ou pela barra de ferramentas:

Através dela você tem acesso a algumas janelas que auxiliam na visualização de Breakpoints (pontos de paradas), CPU Registers (Registradores do processador), Watcher (valores das variáveis), etc.

sexta-feira, 8 de março de 2013

Depuração (Debugger) no Dev-C++

Depuração é uma ferramenta disponibilizada pela maioria das IDEs para facilitar o teste de um código fonte.
Com ele você pode executar seu programa "passo-a-passo" e observar o fluxo dos seus dados dentro das estruturas if/else, switch/case, a quantidade de vezes que se está executando um  laço de repetição (for, while, do/while), verificar o valor das variáveis, acompanhar funções, etc.
Dessa forma você consegue descobrir erros lógicos. Pequenos detalhes que podem fazer seus programa não funcionar como deveria.
A depuração é de extrema importância para qualquer programador, e mesmo para os iniciantes já se há esse habito de depuração desde o inicio, acompanhando o código linha-a-linha e fazendo seus cálculos e comparações lógicas no dedo, ou em um papel ou com sua própria memória.

Como utilizar a depuração no Dev-C++

Antes de usar a depuração no Dev-C++ devemos configurar algumas coisas. Siga esses passos:
1- Abra o Dev-C++ e acesse Tools/Compiler Options
2- Ative a opção "Add the following commands when calling compiler" e digite o argumento -O0
3- Ative a Guia Settings opção Linker e marque para Yes a opção "Generate debugging information"
Obs.: antes de depurar seu programa é necessário que ele seja compilado.

Vou usar como exemplo o seguinte código fonte:
int main()
{
int n1,n2,n3,i,r;
n1 = 1;
n2 = 1;
for (i = 0; i < 20; i++)
{
  if (i < 2)
   printf("%d ",n1);
  else
            {
   n3 = n1 + n2;
   n1 = n2;
   n2 = n3; 
   printf("%d ",n3);
            }
}
getch();
return (0);
}
Este código é uma solução minha proposta para o problema de Fibonacci. Para saber mais sobre Fibonacci acesse este artigo do tecmundo de Wikerson Landim, muito interessante!
De forma resumida é: cada elemento, a partir do terceiro é obtido somando-se os dois anteriores.
Para que a depuração pare em determinados pontos você pode utilizar os breakpoints clicando na barra preta ao lado do código fonte:

Para iniciar a depuração no Dev-C++ você pode utilizar a opção Debug/Debug ou o atalho F8.
Você pode ainda controlar a depuração através de opções oferecidas pela IDE:
Next Step: executa a próxima instrução.
Step into: entrar em uma função.
Continue: executa até o próximo breakpoint.
Run to Cursor: executa até onde o cursor estiver posicionado.
Debug: Iniciar/Parar Depuração
Stop Execution: Para a execução do programa.
Add Watch: adiciona em um painel a esquerda do código as variáveis que você deseja visualizar o valor. Para adicionar você só precisar digitar seu nome. 
Exemplo:

Painel com valores das variáveis:
Observe que enquanto você executa a depuração seu programa fica em execução, e todo seu conteúdo aparece de acordo com os passos que são executados.
É importante que você acompanhe os valores das variáveis e o fluxo dos dados para verificar se não existem erros de lógica no seu programa.
Você pode trocar o valor de uma variável enquanto seu programa roda para testar seu funcionamento clicando com o botão direito sobre ela:

Veja também como depurar seu código em linguagem C com o Code::Blocks.

sábado, 2 de março de 2013

Compilador GCC

GNU Compiler Collection (GCC) é um conjundo de compiladores desenvolvido por Richard Stallman também fundador do projeto GNU.
Esse compilador é utilizado por várias plataformas: Windows, Linux, Unix, etc
Para desenvolver um programa em C você só precisará então de um editor de texto como o bloco de notas (Windows) ou VI (Linux). O programa escrito nesses editores devem ser salvos com a extensão ".c".
Exemplo: meu_programa.c
E este código fonte deve ser compilado pelo GCC com a seguinte linha de comando:
Windows:
Compilar: gcc -c meu_programa.c
Ao compilar é gerado um objeto que devemos "linkeditar": gcc -o meu_programa_executavel meu_programa.o
Podemos ainda gerar o executável direto: gcc -o meu_programa_executavel meu_programa.c
Linux:
Geração direta do executável: $ gcc meu_programa.c -o meu_programa_compilado

Obs.: o argumento "-o" linka o objeto gerado para o executável e logo a frente é informado o nome do programa executável.
O GCC é um compilador escrito para Unix/Linux, para instalar no Windows usaremos o MinGW (Minimalist GNU for Windows). Faça o download aqui.
Após instalar o MinGW você poderá encontrar o GCC dentro de seu diretório na pasta bin (C:\MinGW\bin).
Para facilitar a compilação e linkedição do seu programa salve seus programa nessa pasta.
Como exemplo vamos compilar o programa abaixo:
main()
{
      printf("Hello World \n");
      system("pause");
}
Salvei este programa no C:\MinGW\bin com o nome meu_programa.c
Agora para executar o programa utilizei os seguintes comando no CMD do Windows:
1- cd C:\mingw\bin
2- gcc -o meu_programa meu_programa.c

Assim gerei o executável (meu_programa.exe) dentro da pasta C:\MinGW\bin. Acesse a pasta e execute seu programa:

Apesar dessa possibilidade, hoje em dia temos ótimas IDE (Integrated Development Enviroment) para desenvolvermos na linguagem C. Como exemplo temos: Dev-C++ e Code::Blocks que já vem acompanhados de diversos compiladores, sendo o padrão o GCC.
No Dev-C++, por exemplo, se acessamos o menu Tools/Compiler Options e ativamos a guia programs nos deparamos com os principais compiladores utilizados por ela, e o primeiro que vemos na lista é o GCC:

segunda-feira, 7 de janeiro de 2013

Ponteiros - Linguagem C

Ponteiros são variáveis que permitem que você acesse outras variáveis sem referência-las diretamente.
Como o nome sugere, seu objetivo é apontar para uma variável. Mais especificamente para o endereço onde a variável apontada se encontra.
Os ponteiros dão a linguagem C grandes possibilidades para manipular endereços de memória e é representado por um asterisco (*) antes do nome da variável. Exemplo: *var

Como funcionam os ponteiros?
Exemplo: temos a variável x = 5 e a variável ponteiro *y = &x.
A variável x é uma variável comum com o valor 5 armazenado. Já a variável *y esta apontando para o endereço da variável x (&x).
O operador "&" na frente de uma variável sempre retornará seu endereço de memória, por isso que ao ler uma variável com a função "scanf" utilizamos as variáveis com este sinal na frente.
Neste caso, então, *y tem o endereço de x. Isso significa que se eu fizer a seguinte operação:
*y = 10
Estarei mudando o valor de x que era 5 para 10.

Exemplo:
main()
{
      int a = 3, b = 7, *pa = &a, *pb = &b, aux;
      printf("a = %d\nb =%d\n",a,b);
      printf("Invertendo...\n");
      system("pause");
      aux = a;
      *pa = b;
      *pb = aux;
      printf("a = %d\nb =%d\n",a,b);
      system("pause");
      return 0;
}

A tabela a seguir representa o conteúdo das variáveis usadas nesse simples programa.
Endereço
Variável
Conteúdo
0x0100
a
3
0x0101
b
7
0x0102
pa
0x0100
0x0103
pb
0x0101
0x0102
*pa
3
0x0103
*pb
7

Observe que o conteúdo dos ponteiros sem o asterisco (*) são endereços.
Mais quando colocamos o asterisco (*) estou indicando que ela deve apontar para o endereço. Então quando informo que *pa = b é a mesma coisa que dizer que o endereço de memória 0x0100 = b ou que a variável a = b.

Como ou quando usar os ponteiros?
Ponteiros são utilizado principalmente em funções, quando há a necessidade de retornar mais valores para o programa principal.
Se você não conhece funções na linguagem C acesse este link.
Sabemos que funções retornam apenas um valor por função. Sabemos também que as variáveis de uma função são destruídas depois que a função é executada. Então a solução para isso é mandar para uma função o endereço da variável que você deseja alterar, assim você estará alterando a variável do programa principal.

Exemplo: função para inverter valores de variáveis.
void troca(int *px, int *py)
{
     int aux;
     aux = *px;
     *px = *py;
     *py = aux;
}

main()
{
      int x = 3, y = 7;
      printf("x = %d\ny =%d\n",x,y);
      printf("Invetendo...\n");
      system("pause");
      troca(&x,&y);
      printf("x = %d\ny =%d\n",x,y);
      system("pause");
      return 0;
}

Na função "troca" foi criado dois ponteiros apontando para os endereços das variáveis do programa principal (x e y) e usando esses ponteiros invertemos os valores das variáveis do programa principal.
Essa inversão é muito utilizada em muitos algoritmos e essa seria uma solução para não precisar repetir essas linhas sempre que precisar inverter variáveis.
As matrizes são ponteiros representados de uma maneira diferente.
Se você não sabe o que são matrizes acesse esse link.
As matrizes são armazenadas em sequencia nas células de memórias. Sabemos que cada célula possui 1 Byte (8 bits), então se tivermos um valor do tipo inteiro armazenado em um ponteiro e somarmos 1 a esse endereço ele irá pular 4 células, ou seja, 4 Bytes onde está armazenado o valor da variável.
Aqui temos um programa que comprova isso:
main()
{
      int i, a[5] = {2000,2001,2002,2003,2004}, *pa = &a;
      for(i = 0; i < 5; i++)
           printf("Endereço atual: %d\nValor: %d\n",&a[i],*pa+i);
      system("pause");
      return 0;
}

Criamos um vetor (a) simples com 5 posições e percorremos ele com um ponteiro (*pa) sendo incrementado por i.
A cada vez que o ponteiro é incrementado é mostrado o endereço do vetor e seu valor, oque comprova que ao incrementar o ponteiro ele pula as 4 células (4 Bytes).
Veja o resultado na imagem abaixo: