Publicidade:

terça-feira, 22 de setembro de 2015

Arduino - Dicas de programação 08 - int x unsigned int

Arduino é uma plataforma de prototipação que fez sucesso devido a facilidade com que junta eletrônica e programação e apresenta aos interessados nessas áreas. Tanto pessoas com experiência como sem experiências podem começar a implementar seus projetos sem ter que se preocupar muito com os fundamentos dessas duas áreas de conhecimento. Claro que isso é bom, pois democratiza o conhecimento e permite com que ideias saiam do papel e se tornem projetos, sejam simples ou mais complexos, mas isso não significa que não devemos nos aprofundar nos conhecimentos dessas duas áreas de conhecimento.

A IDE padrão do Arduino, utiliza C/C++, e o que eu quero mostrar aqui são algumas pegadinhas em que as vezes programadores menos experientes podem cair.

C/C++ é uma linguagem Fortemente Tipada. Isso significa que sempre que formos declarar uma variável, precisamos definir a que tipo de dados ela pertence.

Os principais tipos de variáveis são:

void
boolean
char
unsigned char
byte
int
unsigned int
word
long
unsigned long
short
float
double
string - char array
String - object
array

Minha intenção não é apresentar e explicar os tipos de dados utilizados para programar o Arduino, mas mostrar alguns detalhes que devemos prestar atenção na hora de programar.

Se olharmos os tipos acimas, veremos que alguns deles, como int, char e long, possuem o tipo unsigned, por exemplo, tem int, e tem unsigned int.

Mas qual a diferença entre esses dois tipos? Para exemplificar vamos pegar os tipos int e unsigned int. Tanto um como outro irá reservar 16 bits de memória para cada variável declarada. A diferença é que int armazena números que vão de  -32.768 a 32.767, ou seja, aceita números negativos. Enquanto que unsigned int, armazena números de 0 a 65.535.

Agora vamos imaginar o seguinte, iremos declarar duas variáveis, uma variável i do tipo int, e uma variável u do tipo unsigned int e inicializamos cada uma com o valor 0.

Se agora fizermos um if verificando se i é igual a u, obviamente o if será verdadeiro, pois ambas possuem valor 0.

Agora supomos que o valor das duas variáveis sejam decrementadas em 1 (i-- e u--). Vamos analisar então o que acontece com cada uma das variáveis.

Se i era igual a 0, agora ela será igual a -1. Já no caso de u, é diferente, pois u não pode armazenar valores negativos. Nesse caso, u-- var resultar no seguinte valor: 65.535. Isso mesmo. O valor assumido é o último valor dos possíveis valores para o tipo unsigned int. Isso sempre acontece quando extrapolamos os valores aceitos por esses tipos de dados, seja no início ou no fim.

Pois bem, considerando então que i é igual a -1 e u igual a 65.535, o que aconteceria se novamente verificássemos se i é igual a u. Apesar de não ser tão óbvio agora, a comparação continuaria resultado verdadeiro. Sim. -1 seria igual a 65.535.

O que acontece aqui é o seguinte: Quando uma comparação entre dois tipos é feita, é preciso que o compilador converta os tipos de dados para um mesmo tipo, e nessa conversão, os dados resultantes da conversão dos tipos de dados vão ser os mesmos, ou seja iguais. O que, dependendo da lógica que está sendo programada, pode levar a erros. E o programador menos experiente pode ficar doidinho tentando entender o que está acontecendo e não conseguir achar o problema.

Sketch com o exemplo acima


int          i = 0;  /*  -32.768 .. 32.767 */
unsigned int u = 0;  /*        0 .. 65.535 */

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.print("i = ");
  Serial.print(i);
  Serial.println("");
  
  
  
  Serial.print("u = ");
  Serial.print(u);
  Serial.println("");

  
  
  if (i == u) {
    Serial.println("iguais");
  } else {
    Serial.println("diferentes");
  }
  
  i--;
  u--;

  
  delay(5000);
  
  Serial.println("");
  Serial.println("");
}





Vamos imaginar agora um exemplo parecido, mas ao invés de decrementarmos as duas variaveis, vamos decrementar apenas i e iremos verificar se i é maior, menor ou igual a u.

int          i = 0;  /*  -32.768 .. 32.767 */
unsigned int u = 0;  /*        0 .. 65.535 */

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.print("i = ");
  Serial.print(i);
  Serial.println("");
  
  
  
  Serial.print("u = ");
  Serial.print(u);
  Serial.println("");

  
  
  if (i == u) {
    Serial.println("iguais");
  } else {
    
    if (i > u) {
      Serial.println("i maior que u");
    } else {
      Serial.println("u maior que i");
    }
    
  }
  
  i--; //apenas i é decrementado

  
  delay(5000);
  
  Serial.println("");
  Serial.println("");
}





Veja que mesmo i sendo negativo, o programa entende que i é maior que zero. Isso acontece pelo mesmo motivo mostrado anteriormente. O compilador precisa converteri, que é int, em unsigned int. pois só pode comparar variáveis do mesmo tipo, e nessa conversão de um tipo para outro, o valor  que era -1, passa a ser 65.535, ou seja, após a conversão, i de fato passa a ser maior que u.

Conclusão

Sempre que for declarar o tipo de dado, pense exatamente o que aquele dado vai representar, se de fato pode ou não haver valores negativos. E se haver, pense nos problemas que podem ocorrer. E procure não misturar os tipos de dados na hora de fazer comparações ou outras operações entre eles. E o principal: Sempre teste seu código com todas as possibilidades de valores.


Nenhum comentário:

Postar um comentário