Alguns “macetes” que você pode achar interessantes…

Eis mais um daqueles posts be-a-bá, mas que podem ser interessantes para os inexperientes… Já devem ter notado que não costumo escrever código assim:

int x;

x = f();
while (x != 0)
{
  ... faz alguma coisa aqui ...
  x = f();
}

Acho feio esse negócio de fazer a mesma coisa duas vezes (atribuir o retorno de f() a x fora e dentro do loop). Então, costumo escrever de maneira diferente:

int x;

while (x = f())
{
  ... faz alguma coisa aqui ...
}

Se f() retornar 0 saimos do loop, caso contrário, ficamos nele… Só que esse código pode gerar um problema: Será que eu quis fazer uma atribuição e depois um teste ou quis fazer só um teste, com ‘==’ e fiz uma atribuição por engano? Isso pode gerar confusão para alguém que pegue meu código e tente modificá-lo!

Bang! Bang!:

Felizmente C tem o operador ‘!’ que pode ser usado para inverter o sentido binário de um valor… Se quisermos saber se o resultado de algo deu zero, podemos usar ‘!’ que, neste caso, avaliará a expressão e retornará 1 (TRUE). Mas, e se avaliarmos duas vezes? Se usarmos ‘!!’, se um valor não zero aparecer do lado direito destes operadores, a expressão retornará 1 (TRUE)! É como perguntar “isto não não é falso?” (com dois ‘não’ mesmo!). O código fica assim:

int x;

while (!!(x = f()))
{
  ... faz alguma coisa aqui ...
}

E, embora pareça confuso (parece algo vindo de Perl, não parece?) a intenção é clara: Quero atribuir o resultado de f() a x e testar se ele não é zero!

Forçando uma função inline que não retorne nada:

Quem gosta de C++, e das versões dos padrões mais recentes de C, está acostumado com a palavra reservada inline. Ao escrever uma função ou declará-la, se usarmos o prefixo inline o compilador tenderá a incorporar a função naquela que a chamou. Por exemplo:

void f(int x)
{ ... faz algo... }

void g(int x) 
{ f(x); }

A função g() não chamará a função f(), mas a incorporará. O mesmo efeito pode ser obtido declarando ambas as funções no mesmo módulo (arquivo com extensão .c) e, neste caso, o modificador inline torna-se supérfluo. Especialmente porque ele é uma dica para o compilador, que pode ignorá-la!

Existe um jeito de forçar o comportamento inline. E o macete é este: Não usá-lo! Ao invés disso podemos declarar uma pseudo-função na forma de um macro usando uma definição do pré compilador. Um exemplo:

#define f(x) do { ... faz algo ... } while (0)

O comportamento é, essencialmente, o mesmo. Tudo o que estiver dentro do bloco terá escopo local e o do…while(0) garante que o bloco será executado apenas uma vez e o uso de f() dentro de seu programa exige uma ‘;’ no final.

O fato desse macro ser equivalente ao uma função com retorno void é justamente pelo escopo local do “loop”.

A vírgula também é um operador!

O operador ‘,’ simplesmente avalia a expressão do lado esquerdo e depois do lado direito, só isso! Essa “avaliação” não faz nada de especial. Não é uma comparação como a efetuada pelo operador ‘==’. Não é uma atribuição, com o operador ‘=’. Não é uma operação lógica como a do operador ‘&’. É uma operação nula… O único detalhe é que o resultado dessa operação é a expressão à direita.

Expressões como a do exemplo abaixo são perfeitamente válidas:

y = (x += z, x * w);

O que você mandou o compilador fazer é: “acrescente z a x, depois multiplique x por w e finalmente atribua a y. O operador ‘=’ recebe, de seu lado direito, o que está do lado direito do operaodr ‘,’. Ou seja, o que você fez foi:

x += z;
y = x * w;

É por isso que palavras reservadas como for funcionam com múltiplas inicializações:

for (a=b, c=z; a > c; a++, c++) ...

Existem 3 expressões no for: inicialização, comparação e atualização. Todas elas podem ser agrupadas via operador ‘,’. Inclusive a comparação! Embora possa parecer estranha, a declaração abaixo é perfeitamente válida:

for (i=0; j=g(i),k<j; i++) ...

No bloco de comparações a expressão ‘j=g(i)’ será executada primeiro e depois a ‘k<j’, retornando a condição booleana esperada por esse bloco do for. O mesmo macete, é claro, pode ser usado por loops com while e do…while

Atenção! Se fizéssemos ‘y = x += z, x * w;‘, a expressão ainda será válida e o compilador não te dará nenhum aviso, mas isso é completamente diferente do que a expressão anterior faz. O código equivalente é:

x += z;
y = x;
x * w;

A última expressão pode causar estranheza porque não há nenhuma atribuição, mas é uma expressão perfeitamente válida. O compilador ignorará a expressão porque ela não produz resultados e não reclamará! Isso pode ser usado como artifício para escrevermos corpos de loops vazios. Os dois loops à seguir são equivalentes:

while (++i < 10);

while (++i < 10)
  NULL;

Aliás, você pode escrever qualquer expressão inóquoa no lugar de NULL. Substitua NULL pela string “não faz nadica de nada aqui!”, por um número (3, por exemplo) ou por uma expressão como ‘(10*2+3)/2’ e acontece a mesma coisa. Todos os loops abaixo são equivalentes:

while (++i < 10);

while (++i < 10)
  "Não faz nadica de nada aqui!";

while (++i < 10)
  3;

while (++i < 10)
  (10*2+3)/2;

/* String "Fred rules!" em hexa! */
while (++i < 10)
  "\x46\x72\x65\x64\x20" /* não falta ',' */
  "\x72\x75\x6c\x65\x73\x21",
  736232*i,
  NULL;
Anúncios