Perdidos no espaço…

Tenho acompanha, já há algum tempo, uma confusão entre os novos desenvolvedores em linguagens C/C++ sobre o tipo “void”. Especialmente no que concerne a declaração de funções. É comum encontrar coisas como essa:

int main()
{
  // ... faz algo aqui ...

  return 0;
}

O objetivo, é claro, é dizer ao compilador que a função main não toma parâmetros. Acontece que, em C, não é isso o que essa construção diz! O código abaixo não gera quaisquer erros e imprimirá o valor 1 corretamente:

#include <stdio.h>

int f() { return 1; }

void main(void) 
{
  int x = f(1,2,3); 
  printf("%d\n", x);
}

Ao declarar a assinatura de uma função sem parâmetros você diz ao compilador que ela aceita qualquer número deles e simplesmente os ignorará. Isso pode parecer ter o mesmo efeito, mas considere o caso onde você precisa obter um erro se passar algum parâmetro para uma função… Esse erro só pode ser garantido, do ponto de vista do compilador, se você declarar a assinatura da função com void. Coloque void entre os parêntesis na declaração da função f() e você obterá o erro abaixo:

$ gcc -o test test.c
test.c: In function ‘main’:
test.c:7:3: error: too many arguments to function ‘f’
   int x = f(1,2,3); 
   ^
test.c:3:5: note: declared here
 int f(void) { return 1; }
     ^

Recomendo que, a não ser que vocẽ esteja mexendo com construtores default e destrutores de classes, em C++, use sempre o “tipo” void para dizer às suas funções que elas não tomam parâmetros!

Ponteiros void

Esses tipos de ponteiros são especiais porque podem apontar para qualquer coisa. Ou seja, o ponteiro contém um endereço, como qualquer ponteiro, mas o tipo apontado não é definido! O GCC permite a realização de operações aritméticas com ponteiros void graças a uma extensão do compilador e, confesso, uso muito essa extensão porque a acho útil. Mas, a especificação da linguagem é clara: Aritmética com ponteiros void não deveria ser possível!

Então, se você tem um ponteiro void e quer acessá-lo como se esse apontasse para bytes, use um “alias”, um apelido, criando um ponteiro do tipo unsigned char:

void dosomething(void *ptr)
{
  /* Assim como "void *" pode apontar para qualquer lugar, 
     ele também pode ser copiado para qualquer outro tipo 
     de ponteiro sem usar 'casting'! */
  unsigned char *bptr = ptr;

  // ...
}

Isso é um recado para mim mesmo, inclusive: Não use aritmética de ponteiros do tipo void se quiser ter um código mais confiável.

Anúncios

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s