Operadores são símbolos que realizam operações sobre operandos (variáveis, constantes ou expressões). Em Go, os operadores são agrupados em categorias: aritméticos, de comparação, lógicos, bitwise, de atribuição, entre outros. Compreender seu funcionamento e precedência é essencial para escrever código correto e eficiente.

Nesta aula, abordaremos os principais operadores da linguagem Go, suas particularidades e a ordem de avaliação. Veremos exemplos práticos e discutiremos boas práticas para evitar erros comuns.

Aritméticos, comparação, lógicos

Os operadores aritméticos em Go são usados para operações matemáticas básicas. Eles funcionam com tipos numéricos (int, float, complex) e seguem as regras de tipo: operandos devem ser do mesmo tipo (exceto para constantes). Os operadores são: + (adição), - (subtração), * (multiplicação), / (divisão) e % (resto da divisão). O operador % só funciona com inteiros.

Operadores de comparação retornam um valor booleano (true ou false). São eles: == (igual), != (diferente), < (menor), > (maior), <= (menor ou igual), >= (maior ou igual). Em Go, é possível comparar apenas valores do mesmo tipo; a comparação entre tipos diferentes (ex.: int e float) causa erro de compilação.

Operadores lógicos são usados com valores booleanos: && (E lógico), || (OU lógico) e ! (NÃO lógico). Eles seguem a regra de curto-circuito: em &&, se o primeiro operando for false, o segundo não é avaliado; em ||, se o primeiro for true, o segundo não é avaliado. Isso é útil para evitar avaliações desnecessárias ou erros.

package main

import "fmt"

func main() {
    a := 10
    b := 3

    // Aritméticos
    fmt.Println("Soma:", a+b)       // 13
    fmt.Println("Diferença:", a-b)  // 7
    fmt.Println("Multiplicação:", a*b) // 30
    fmt.Println("Divisão:", a/b)    // 3 (divisão inteira)
    fmt.Println("Resto:", a%b)      // 1

    // Comparação
    fmt.Println("a == b:", a == b)  // false
    fmt.Println("a != b:", a != b)  // true
    fmt.Println("a > b:", a > b)    // true

    // Lógicos
    x := true
    y := false
    fmt.Println("x && y:", x && y)  // false
    fmt.Println("x || y:", x || y)  // true
    fmt.Println("!x:", !x)          // false
}

Bitwise

Operadores bitwise atuam diretamente sobre os bits de valores inteiros. Em Go, eles são: & (E bitwise), | (OU bitwise), ^ (XOR bitwise), &^ (E com negação, também chamado de "clear bit"), << (deslocamento à esquerda) e >> (deslocamento à direita). O operador &^ limpa os bits do primeiro operando onde o segundo operando tem bits 1.

Os deslocamentos são úteis para multiplicar ou dividir por potências de 2 rapidamente. O deslocamento à esquerda (<<) preenche com zeros à direita; o deslocamento à direita (>>) preenche com zeros à esquerda para inteiros sem sinal, e com o bit de sinal para inteiros com sinal (comportamento dependente da implementação, mas geralmente preserva o sinal).

package main

import "fmt"

func main() {
    var a uint8 = 0b1100 // 12
    var b uint8 = 0b1010 // 10

    fmt.Printf("a & b = %04b (decimal %d)\n", a&b, a&b)   // 1000 (8)
    fmt.Printf("a | b = %04b (decimal %d)\n", a|b, a|b)   // 1110 (14)
    fmt.Printf("a ^ b = %04b (decimal %d)\n", a^b, a^b)   // 0110 (6)
    fmt.Printf("a &^ b = %04b (decimal %d)\n", a&^b, a&^b) // 0100 (4)
    fmt.Printf("a << 2 = %04b (decimal %d)\n", a<<2, a<<2) // 110000 (48)
    fmt.Printf("a >> 2 = %04b (decimal %d)\n", a>>2, a>>2) // 0011 (3)
}

Precedência

A precedência de operadores determina a ordem de avaliação em expressões com múltiplos operadores. Em Go, a tabela de precedência é (do maior para o menor):

  • Operadores unários: !, -, +, ^, &, *, <-
  • Multiplicativos: *, /, %, <<, >>, &, &^
  • Aditivos: +, -, |, ^
  • Comparação: ==, !=, <, <=, >, >=
  • Lógicos E: &&
  • Lógicos OU: ||

Operadores de mesma precedência são avaliados da esquerda para a direita (exceto unários). É recomendável usar parênteses para tornar a intenção clara, mesmo que a precedência seja conhecida. Exemplo: a + b * c é equivalente a a + (b * c).

package main

import "fmt"

func main() {
    x := 2
    y := 3
    z := 4

    resultado := x + y*z   // 2 + 12 = 14
    fmt.Println(resultado) // 14

    // Com parênteses, fica explícito
    resultado2 := (x + y) * z // 5 * 4 = 20
    fmt.Println(resultado2)   // 20
}

Particularidades

Go possui algumas particularidades importantes sobre operadores:

  • Incremento e decremento: Go oferece os operadores ++ e --, mas eles são statements (instruções), não expressões. Isso significa que você não pode usá-los dentro de uma expressão como x = y++. Eles só podem ser usados como instruções separadas: x++ ou x--.
  • Atribuição curta (short variable declaration): O operador := declara e atribui uma variável, inferindo o tipo. Ele não é um operador de atribuição tradicional; é uma declaração.
  • Operador de atribuição composta: Go suporta +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, &^=. Eles são statements, não expressões.
  • Curto-circuito: Como mencionado, && e || usam curto-circuito. Isso é especialmente útil para evitar erros: if ptr != nil && ptr.val == 10 é seguro.
  • Operador de endereço e ponteiro: & obtém o endereço de uma variável; * desreferencia um ponteiro. Eles são operadores unários.
package main

import "fmt"

func main() {
    // Incremento e decremento como statements
    i := 0
    i++
    fmt.Println(i) // 1
    i--
    fmt.Println(i) // 0

    // Atribuição composta
    a := 5
    a += 3
    fmt.Println(a) // 8

    // Curto-circuito
    var s *string = nil
    // if s != nil && *s == "Go" { } // seguro
    _ = s
}

Boas práticas

  • Sempre use parênteses para expressões complexas, melhorando a legibilidade.
  • Evite usar ++ e -- dentro de expressões; use-os como statements separados.
  • Prefira a atribuição curta := para declarar variáveis dentro de funções, mas lembre-se de que ela não pode ser usada fora de funções.
  • Em comparações, lembre-se de que Go não permite conversão implícita entre tipos; converta explicitamente se necessário.

Referências

Exercícios

  1. Qual é o resultado da expressão 10 % 3 em Go? E 10.0 % 3? Explique.
  2. ✓ Resposta: 10 % 3 resulta em 1, pois 10 dividido por 3 dá 3 com resto 1. 10.0 % 3 não compila, pois o operador % só é definido para inteiros em Go.
  3. Escreva uma expressão que verifique se um número inteiro x é ímpar usando operador bitwise.
  4. ✓ Resposta: x & 1 == 1. Se o bit menos significativo for 1, o número é ímpar.
  5. Dado: a := 10; b := 20; c := 0; Qual o valor de c após c = a + b * 2? E após c = (a + b) * 2?
  6. ✓ Resposta: c = a + b * 2: primeiro b*2 = 40, depois a+40 = 50. c = (a + b) * 2: primeiro a+b = 30, depois 30*2 = 60.
  7. Explique por que o código a seguir não compila: var x int = 5; x++ == 6?
  8. ✓ Resposta: x++ é um statement, não uma expressão. Ele não retorna um valor, portanto não pode ser usado em uma comparação. O correto seria x++; x == 6.
  9. Qual a diferença entre os operadores & e && em Go?
  10. ✓ Resposta: & é o operador E bitwise, que opera em inteiros bit a bit. && é o operador E lógico, que opera em valores booleanos e usa curto-circuito.