domingo, 30 de dezembro de 2012

Vetores


Os dados que usaremos possuem o tipo compostos e estruturados, ou seja, são formados das estruturas mais simples que estão disponíveis. O estudo dessas estruturas envolve uma analise de como se combinam de modo a se formar a composição e de como extrair um componente especifico, começaremos pelo vetor e sua forma mais simplória, o vetor unidimensional, cuja definição será de um conjunto finito e ordenado de elementos homogêneos, entendemos que finito é o numero determinado de elementos no vetor, ordenado é a forma em que os elementos do vetor estão organizados, como elemento 1, 2, 3,... até seu tamanho e homogêneo onde os elementos são do mesmo tipo.

As duas operações básicas que acessam um vetor são o armazenamento e a extração, como podemos ver a operação de armazenamento recebe um vetor a e um índice i, junto de um elemento x, para extração basta apenas vetor a e índice i para recuperar o elemento.

No exemplo abaixo, foi incluído as funções store e extract para indicar a ação:


#define ARRAY_SIZE                 100

void store(char [], int, char);
char extract(char [], int);

void main(void)
{
      char Array[ARRAY_SIZE];
      int i;

      for (i = 0; i < ARRAY_SIZE; i++)
      {
            store(Array, i, i);
      }

      for (i = 0; i < ARRAY_SIZE; i++)
      {
            printf(" %i", extract(Array, i));
      }
}

void store(char a[], int i, char x)
{
      a[i] = x;
}

char extract(char a[], int i)
{
      return a[i];
}



No entanto, a linguagem C permite seu uso de forma muito mais simples, bastando declarar como mostrado a seguir com resultados iguais:


void main(void)
{
      char a[100];
      int i;

      for (i = 0; i < 100; i++)
            a[i] = i;

      for (i = 0; i < 100; i++)
            printf(" %i", a[i]);
}


Isso serve para comprovar o quão simples um vetor pode ser incluindo a forma de usa-lo. Basta declarar “char a[100];” para se ter implementado um vetor unidimensional de 100 posições sucessivas. Na linguagem C, a variável vetor é implementada como variável ponteiro, a diferença entre declarações como char *a, e char a[100] é que a ultima reserva 100 posições começando em a, ou seja, o elemento mostrado em a[i] é igual a *(a+i). Apesar de em C todos os elementos de um vetor ter tamanho fixo e predeterminado, podemos criar um método para um vetor de tamanho variável reservando um conjunto contiguo de posições na memória, cada uma das quais armazenando um endereço. Cada posição da memória é o endereço do vetor, de tamanho variável, em alguma outra parte da memória, semelhante à forma com que as strings em C é tratadas, uma string é implementada como um vetor, de modo que um vetor de strings, é na verdade um vetor de vetores, vetor bidimensional. Vejamos duas técnicas que resultam num vetor de strings de tamanho variável.

Podemos incluir no primeiro byte o tamanho da string:


->
4
H
E
L
L
O
...
...
...

->
4
W
O
R
L
D
...
...
...

->
2
M
Y
...
...
...
...
...
...

->
4
F
I
R
S
T
...
...
...

->
6
P
R
O
G
R
A
M
...






  
Usar um terminador nulo:


->
H
E
L
L
O
\0
...
...
...

->
W
O
R
L
D
\0
...
...
...

->
M
Y
\0
...
...
...
...
...
...

->
F
I
R
S
T
\0
...
...
...

->
P
R
O
G
R
A
M
\0
...






  
Claro que tudo isto envolve um nível adicional de acesso, que reduz a eficiência, mas plenamente justificável pela conveniência de manter tal característica.

quarta-feira, 26 de dezembro de 2012

Estruturas de dados básicas



Algoritmos são formas pelas quais controlamos dados como meta para atingir um objetivo, geralmente usados em problemas que manipulam informações simples em formas complexas. Como exemplo, um compilador traduz texto simples e caracteres em produtos binários para serem executados em maquinas especificas. Programas são extremamente dependentes do formato pelo qual os algoritmos irão manipular os dados de uma forma previsível. Consequentemente fica impossível falar de algoritmos sem compreender as rotinas primitivas para tratamento de dados. O passo inicial para um desenvolvedor se tornar fluente no uso de algoritmos consiste em aprender a controlar os dados para no próximo estágio transformar os mesmos de acordo com a necessidade. 

               A orientação algorítmica dos dados está direcionada principalmente em técnicas para representar a informação na memória. Como os dados podem ser armazenados, acessados e alterados de forma eficiente para se resolver um problema? Maioria destes problemas pode ser resolvida através do uso de estruturas de dados básicas. Sendo que as mais simples estruturas são o vetor, a lista ligada, a pilha e a fila.

terça-feira, 18 de dezembro de 2012

ANSI extern



               Ao tentar manter um mínimo de portabilidade do código para os diferentes compiladores que podem ser usados, um caso interessante surge, quando se pretende usar o recurso de uma variável em mais de um arquivo utilizando extern, com isto o item terá sua ligação com os diversos módulos que fazem referencia. Os compiladores modernos conseguem ignorar este cuidado e fazem o recurso funcionar sem que você tenha essa preocupação em declarar uma variável externa. Como esta característica não é padronizada, não há garantias de seu correto uso, o padrão ANSI deve ser usado para manter compatível com os diversos casos.

               Uma forma de garantir o comportamento da variável é garantir que seja declarada em um modulo e indicada como extern nos demais. Podemos conseguir este efeito com o auxilio do pré-processador de macros usando um arquivo cabeçalho. No modo tradicional temos:

MAIN.C                    DUMMY1.C                       DUMMY2.C
char letra;

extern char letra;

extern char letra;




Os macros usados a seguir têm como objetivo facilitar a declaração e o uso de variáveis globais nos vários módulos do programa.

MAIN.H
#ifdef __MAIN_C__
      #define EXTERN
#else
      #define EXTERN      extern
#endif

EXTERN char g_cLetra;









Usando a macro EXTERN, no primeiro momento pode parecer confuso o uso do macro, mas a ideia consiste em usar o pré-processador de macros para fazer o trabalho de declaração. Inicialmente temos a seguinte apresentação:

MAIN.C                             DUMMY1.C                           DUMMY2.C
#include "main.h"

#define __MAIN_C__

#include "main.h"

#include "main.h"








Quando o pré-processador executar a macro, o arquivo que estiver definido como __MAIN_C__ ira incluir a variável 'EXTERN char g_cLetra', como 'char g_cLetra', os demais arquivos incluem a variável como 'extern char letra', o resultado ficou igual ao do modo tradicional.

 A principio pode parecer mais trabalhoso e seria caso o numero de variáveis globais compartilhada em vários módulos fossem apenas uma ou duas, quando o numero  de variáveis globais e módulos passa a crescer, a vantagem fica obvia com um só local para declaração tornando mais fácil compreender, organizar e manter o código.