# Analogicamente perfeito

Caloni, 2010-05-19 computer [up] [copy]

É possível explicar tudo no mundo da informática através de analogias? Pela minha singela experiência de professor informal, eu acredito que sim. Durante esses dois anos, explanei diversos assuntos e, em todos eles, difíceis ou não, consegui um certo grau de sucesso graças ao uso de metáforas e parábolas.

De memória (ou buscando no meu blogue) consigo lembrar alguns tópicos e a forma como os expliquei. Em alguns até fiz um artigo sobre o assunto:

  • Ponteiros 16 bits: rua e número de uma casa.
  • Typedefs: apelidos para nomes de pessoas.
  • Depuração: a ciência médica de House.
  • Passagem de argumentos por valor e por referência: e-mails com anexo e com linques.
  • Ponteiros: armário de gavetas.
  • Programadores: programadores de verdade não usam Java (brincadeira).
  • Agendamento de threads: guichê de CPUs.
  • Seções críticas: fila de threads dentro de uma sala.
  • Funções com retorno booleano: o dedo polegar dos romanos.
  • Pilha: uma pilha, só que de pratos e não bytes.
  • Binário: bichos-preguiça contando com seus únicos dois dedos.
  • Tipos: uma forma de bolo que só faz bolos com um único formato.
  • Definições x declarações de variáveis: hardware e software; OK, podemos chamar esse de meta-analogia =)
  • Depuração 2: séries de investigação forense como CSI.

Acredito ser essa a melhor forma de desmistificar esse pequeno mundinho que parece incompreensível aos outros mortais. Até porque tudo que é criado no mundo dos computadores são abstrações do mundo real, que por sua vez são abstrações da mente humana.

É por isso que sempre digo que ciência da computação é uma arte-ciência da área de humanas.


# Modificadores e qualificadores de tipo

Caloni, 2010-05-28 computer [up] [copy]

"@caloni poderia pensar em fazer um artigo sobre os modificadores de tipo em c? os mais complexo, acho eu: volatile, enum, union, extern, etc"

Uma coisa de cada vez: existem **modificadores** (ou qualificadores) de tipo e **especificadores** de tipo. _Volatile_ e _extern_ se encaixam na primeira categoria, _enum_ e _union_ na segunda. Veremos um pouco desses dois lados da linguagem em doses paliativas.

Padrão C (ISO/IEC 9899:1990)

   6.5.2.2 enum-specifier
    enum
   
   6.5.3 type-qualifier
    const
    volatile
   
   6.5.2.1 struct-or-union
    struct
    union
   
   6.5.1 storage-class-specifier
    typedef
    extern
    static
    auto
    register

Padrão C++ (ISO/IEC 14882:1998)

   type-specifier
    enum-specifier
   
   enum-specifier
    enum
   
   cv-qualifier
    const
    volatile
   
   class-key
    class
    struct
    union
   
   storage-class-specifier
    auto
    register
    static
    extern
    mutable
   
   decl-specifier
    storage-class-specifier
    typedef

Modificadores de tipo

Um modificador de tipo é opcional na definição de um tipo e deve estar sempre relacionado com a declaração de alguma variável. Ele determina, em termos gerais, qual será a função dessa variável. Ela pode ser modificada? Onde ela se encontra no programa? Como ela será modificada?

Como exemplo rápido, temos abaixo uma variável que é atualizada pelo clock do processador e uma variável que não pode ser alterada após sua primeira atribuição:

   volatile int* clockSecs = <algum-endereço-do-sistema>;
   const float pi = 3.14;

Fica meio óbvio que a primeira variável possui seu valor volátil, ou seja, muda conforme o tempo passa, e não depende do próprio código (pode mudar sem sua permissão). A segunda variável também tem um uso explícito, uma vez que o valor de pi nunca será alterado (não nesse Universo).

Especificadores de tipo

Os especificadores de tipo possuem cada um sua peculiaridade. Os mais peculiares, que veremos nos próximos artigos, serão as enumerações e as construções bizarras de structs e unions.

   enum Contador { um = 1, dois, tres, };
   union Atoms { struct { int part1; int part2; } parts; int64 total; };

Aqui não é um compêndio teórico sobre a linguagem. Vamos falar particularmente da programação Windows, mas esteja livre para dar seus pitacos com respeito a outros sistemas operacionais e suas implementações igualmente exdrúxulas =)


# O Escritor Fantasma

Caloni, 2010-05-28 cinema movies [up] [copy]

O Escritor Fantasma é o tipo de thriller que, diferente da enxurrada de pistas e falsas-pistas que funcionam muito bem em Os Homens que não Amavam as Mulheres, ele nos leva a desvendar o mistério da trama recriando-o gradualmente em uma série de passos sutis, mas consistentes, que revelam em seu momento final um mosaico diferente na mente de cada espectador.

Seguindo essa cartilha fascinante, a trilha sonora do começo usa sons que lembra buzina de carro (e estamos justamente na parte em que os carros tentam sair da balsa), um simples esquilo revela uma informação vital em um único quadro, a ausência de um automóvel é o que dá tom a uma perseguição e por fim, claro, a brilhante sequência do bilhete indo de encontro à primeira-dama que demonstra o uso da técnica visual para relatar uma história em seu total controle: sutil, rápido, eficiente.


# Enum

Caloni, 2010-05-31 computer [up] [copy]

Padrão C (ISO/IEC 9899:1990)

   
   6.5.2.2 enum-specifier
    enum
   Padrão C++ (ISO/IEC 14882:1998)
   
   type-specifier
    enum-specifier
   
   enum-specifier
    enum

Uma enumeração faz duas coisas: define um novo tipo, parecido com um inteiro, e cria uma **lista de constantes com nomes significativos**. A definição técnica do tipo de um enum é mais complicada, mas basicamente ele é um novo int.

Como funciona: definimos uma lista com cada elemento tendo um valor inteiro, geralmente único. Todos os nomes usados na lista passam a fazer parte do espaço de nomes atual e funcionam como constantes com o seu valor definido no início.

enum FileType // criamos o novo tipo inteiro FileType
{
   Binary = 1, // Binary é uma constante com valor igual a 1
   Text = 2, // Text é uma constante com seu sizeof igual a sizeof(FileType)
   Mixed = 3 // Todas as constantes da enumeração são do mesmo tipo
};

Obs.: Os elementos que não possuem valor definido são definidos automaticamente como o valor do elemento anterior acrescidos de um. Se for o primeiro elemento, seu valor padrão é zero.

enum Numbers
{
   zero,  // igual a zero
   one,   // igual a um
   two,   // igual a dois
   three  // igual a tres
};
enum Hexa
{
   JulioCesar = 1,
   Lucio = 3,
   Juan,                // Juan = 3 + 1 = 4
   Gilberto Silva = 6,
   Felipe Melo          // 6 + 1 = 7
}; 

_Detalhe bizarro_: você sabia que, apesar da vírgula ser usada para separar valores de enumeração, ela pode também terminar uma listagem? Por algum motivo exdrúxulo (se alguém quiser explicar), um valor de enumeração foi definido de tal forma que sempre poderá existir uma vírgula terminando ele:

enum VirgulaSafada { 
   um = 1, 
   dois, 
   tres, // o que essa vírgula no final tá fazendo aqui?
}; 

Uso prático

Geralmente usamos enumerações para definir valores únicos (tag) em um argumento de função, ou, mais moderno, como substituto daqueles antigos defines em C para mapas de bits. Nesse último caso não usamos o tipo da enumeração, pois ele pode conter apenas um valor único definido, e não um conjunto deles:

enum ModoDeServir
{
   assado,
   cozido,
   frito,
   cru
};
void Cook(Prato p, ModoDeServir ms);
main()
{
   Cook(frango, cozido);
}
enum FileOpenMode
{
   fomRead   = 0x0001,
   fomWrite  = 0x0002,
   fomOver   = 0x0004,
   fomDel    = 0x0008,
};
void OpenFile(DWORD fileOpenMode);
main()
{
   OpenFile(fomRead | fomWrite);
} 

Note que usamos uma enumeração nesse último caso para termos um nome significativo para uma flag, além desse nome fazer de fato parte dos nomes do programa, e não um define que, para o compilador, não existe.

Boas práticas

Como os tipos da enumeração passam a pertencer ao namespace atual, eles podem se misturar facilmente com todos os nomes daquele namespace. Dessa forma, é útil e bem organizado definir um prefixo para os nomes, que pode ser formado pelas iniciais do nome da enumeração, como no exemplo acima (fom = **F**ile**O**pen**M**ode).

O surgimento do enum veio como evolução de uma prática já consagrada pelo uso na linguagem C, que eram as listas de valores constantes criados através de defines com algum prefixo em comum (FILE_SHARE_*, SW_SHOW_*, etc). Portanto, sempre que se encontrar em uma situação para criar esse tipo de lista, a enumeração é o caminho atualmente ideal.

// A listagem abaixo pode virar um enum...
#define FOM_READ   0x0001
#define FOM_WRITE  0x0002
#define FOM_OVER   0x0004
#define FOM_DEL    0x0008
// ... como este aqui!
enum FileOpenMode
{
   FOM_READ   = 0x0001,
   FOM_WRITE  = 0x0002,
   FOM_OVER   = 0x0004,
   FOM_DEL    = 0x0008,
};
// esse pedaço de código abaixo...
int main()
{
   OpenFile(path, FOM_WRITE);
}
// ... vira isso após ser pré-processado...
int main()
{
   OpenFile(path, 0x0002);
}
// ... mas isso se fossem usados enums...
int main()
{
   OpenFile(path, FOM_WRITE); // FOM_WRITE faz parte da linguagem
}
 

Atualização: e qual a diferença?

Perguntado por um leitor sobre qual a diferença prática do último exemplo, onde temos praticamente o mesmo resultado entre usar defines e enumerações, imaginei que a mesma dúvida pode ter surgido para várias pessoas, porque é uma boa dúvida. Dá a entender que o autor deste artigo está se atentando a preciosismos da linguagem (e está mesmo!), mas à vezes as aparências enganam.

Para ilustrar melhor fiz um mais elaborado. Aqui, estamos lendo pedaços de dados que tiveram que ser alinhados com alguma "gordura".

// alinhamento obrigatório pelo leiaute dos dados
#define CHUNKSZ_BASE 0x5000
#define CHUNKSZ_TINY   0x1000 + CHUNKSZ_BASE
#define CHUNKSZ_SMALL  0x2000 + CHUNKSZ_BASE
#define CHUNKSZ_MEDIUM 0x4000 + CHUNKSZ_BASE
#define CHUNKSZ_HUGE   0x8000 + CHUNKSZ_BASE
// alinhamento obrigatório pelo leiaute dos dados
static const int chunkSizeBase = 0x5000;
enum ChunkSize
{
   chunkszTiny   = 0x1000 + chunkSizeBase,
   chunkszSmall  = 0x2000 + chunkSizeBase,
   chunkszMedium = 0x4000 + chunkSizeBase,
   chunkszHuge   = 0x8000 + chunkSizeBase,
};
// Fonte original
int main()
{
    // lendo quadro pedaços de dados (tamanho médio)
   ReadChunkFromFile(file, CHUNKSZ_MEDIUM * 4);
    // lendo quadro pedaços de dados (tamanho médio)
   ReadChunkFromFile(file, chunkszMedium * 4);
}
// Pós-processado
int main()
{
    // lendo sei lá o que (perde alinhamento)
   ReadChunkFromFile(file, 0x4000 + CHUNKSZ_BASE * 4);
    // lendo quadro pedaços de dados (tamanho médio)
   ReadChunkFromFile(file, chunkszMedium * 4);
}
 

_Aviso para os programadores mais calejados, eu omiti propositalmente os parênteses obrigatórios para qualquer define que tenha cálculos matemáticos, para ilustrar que muitas vezes o que vemos **antes** não é o que aparece **depois.**_


[2010-04] [2010-06]