# 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:
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.
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
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
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).
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? };
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.
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 }
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.**_