Operadores

  1. Operadores de atribuição
  2. Operadores aritméticos
  3. Precedência de operadores aritméticos
  4. Operadores relacionais
  5. Operadores lógicos
  6. Operadores de caracteres

Nesse tutorial, vou falar sobre operadores. Operadores podem ser classificados de várias formas. Uma delas é de acordo com o número de operandos que eles tem. Na programação, geralmente há 3 tipos: unário, binário e ternário. Os mais comuns, com certeza, são os operadores binários.

Operadores de atribuição

Os operadores de atribuição são operadores binários. O operando da esquerda é o nome de uma variável e o operando da direita é um valor literal ou outra variável ou uma expressão. O operador da atribuição sempre opera da direita para a esquerda, o que quer dizer que a associatividade dele é da direita para a esquerda. Isso quer dizer que o valor da expressão à direita do operador de atribuição é avaliado primeiro, então o resultado é atribuído ao operando na esquerda. O operando na esquerda de um operador de atribuição deve ser um nome de uma variável.

Veja abaixo alguns exemplos de atribuições válidas:

  • x = 7
  • x = 4 * 5
  • x = y
  • x = y + 8

Veja abaixo alguns exemplos de atribuições inválidas:

  • 7 * 3 = x
  • x - 8 = 10
  • x / 4 = y

Em cada um dos casos acima, o valor à esquerda do operador de atribuição não é uma variável. Por isso, as instruções são inválidas. Perceba a diferença entre expressões matemáticas e atribuições em linguagens de programação: na Matemática, as expressões acima seriam válidas. Já na programação, elas são inválidas. A programação tem uma base muito forte na Matemática, mas isso não quer dizer que ela é igual à Matemática. Há coisas que são válidas na Matemática, mas são inválidas na programação, assim como há coisas que são válidas na programação que são inválidas na Matemática.

Além do operador de atribuição composto apenas pelo =, quase todas as linguagens suportam operadores de atribuição que combinam o = com operadores aritméticos. Os que costumam ser suportados são os seguintes:

  • x+= 3 -> x = x + 3
  • x-= 3 -> x = x - 3
  • x*= 3 -> x = x * 3
  • x/= 3 -> x = x / 3
  • x%= 3 -> x = x % 3

O Portugol suporta todos esses operadores. Os quatro primeiros são usados para combinar a atribuição com adição, subtração, multiplicação e divisão, respectivamente. O último combina o operador de módulo com a atribuição comum. O operador de módulo retorna o resto da divisão do primeiro operando pelo segundo. Assim, se x for 8, a atribuição x%= 3 é igual a 2, pois o resto da divisão de 8 por 3 é 2.

Operadores aritméticos

Todos os operadores aritméticos são binários. Eles já foram discutidos brevemente na seção de Operadores de atribuição. Cinco operadores representam as quatro operações básicas da matemática e a operação de módulo, que retorna o resto da divisão do primeiro operando pelo segundo. Veja exemplos de cada uma dessas operações:

  • Adição: x = 5 + 6
  • Subtração: x = 7 - 4
  • Multiplicação: x = 5 * 3
  • Divisão: x = 10 / 5
  • Módulo: x = 12 % 5

Algumas linguagens suportam operadores aritméticos adicionais, como por exemplo, de exponenciação. Geralmente, é usado um ^ para essa operação.

Precedência de operadores aritméticos

É possível combinar operações aritméticas. Quando isso é feito, cada operador segue regras de precedência que ditam a ordem na qual as operações serão realizadas. O operador com maior nível de precedência tem a operação dele realizada primeiro.

Na prática, nas linguagens de programação, a precedência de operadores envolve outros operadores, além dos parênteses e dos operadores aritméticos, mas esses são operadores mais gerais, que são comuns às linguagens de programação. Se eu falar sobre outros operadores, aí eu vou começar a entrar em operadores mais específicos e também em conceitos mais avançados, que não são o objetivo desses tutoriais, que são voltados especialmente para iniciantes.

Veja abaixo o nível de precedência dos operadores aritméticos e dos parênteses, em ordem decrescente:

  • (): se existirem parênteses aninhados, os mais internos são avaliados primeiro
  • *, / e %
  • + e -

Caso a precedência de operadores seja igual, é usada a associatividade para determinar qual operação é realizada primeiro. Veja alguns exemplos de expressões para entender, na prática, como a precedência e a associatividade funcionam:

  • x = 3 + 5 / 2: temos duas operações aqui: adição e divisão. Como o nível de precedência dos dois é diferente, é realizada primeiro a operação cujo operador tem maior nível de precedência. No caso, é a divisão. Considerando que x é inteiro, 5 / 2 resultará em 2. Ficamos agora com 3 + 2. Agora, só há uma operação, então basta efetuá-la.
  • x = (3 + 5) / 2: Nesse exemplo, continuamos apenas com adição e divisão, só que como a adição está entre parênteses, ela é realizada antes da divisão, porque os parênteses tem maior nível de precedência do que a divisão. Então, ficamos com 8 / 2, que resultará em 4. Veja como o uso dos parênteses alterou o resultado final da expressão.
  • x = 3 + 5 - 6 / 2: Nesse exemplo, temos agora uma adição, uma subtração e uma divisão. Não temos parênteses. Temos operadores com níveis de precedência diferentes. A divisão é o operador com maior nível de precedência, então ela é realizada primeiro. Ficamos com 3 + 5 - 3. Agora, temos adição e subtração. Duas operações com o mesmo nível de precedência. Por isso, é usada a associatividade dos operadores, que nesse caso é da esquerda para a direita. Então, a adição é realizada primeiro, e depois a subtração. Ficamos então com 8 - 3, que é igual a 5.
  • x = (2 + 3 * (4 / 2) + (2 * 4)) % 2: Essa é uma expressão mais complexa, com bem mais operações e que usa parênteses em níveis diferentes. Como ela seria interpretada? Temos aqui adição, multiplicação, divisão, módulo e parênteses. Precedências diferentes? Sim. O que tem maior precedência? Os parênteses. Temos parênteses em níveis diferentes. Dois são mais internos e um é mais externo. Qual é avaliado primeiro? Um dos internos, porque os parênteses internos são avaliados antes dos mais externos. Qual dos internos é avaliado primeiro? Para decidir isso, é usada a associatividade, que é da esquerda para a direita. Assim, é avaliado primeiro o (4 / 2). Ficamos com (2 + 3 * 2 + (2 * 4)) % 2. Em seguida, os outros parênteses são avaliados: (2 + 3 * 2 + 8) % 2. Agora, temos ainda parênteses, mas dentro dele temos três operações: duas adições e uma multiplicação. A multiplicação é feita primeiro, porque tem maior precedência: (2 + 6 + 8) % 2. Depois, é feita a primeira adição da esquerda para a direita, por causa da associatividade: (8 + 8) % 2. Depois, é feita a adição que sobrou nos parênteses: 16 % 2. Finalmente, é feito o módulo, que resulta em 0.

Como você viu, os diferentes níveis de precedência dos operadores podem levar à resultados diferentes. Por isso, quando você estiver programando, se você tiver dúvidas, aconselho a usar os parênteses ou assimilar bem a tabela com os níveis de precedência dos operadores da sua linguagem de programação.

Se você assumir que operadores vão ter um nível de precedência diferente do nível de precedência real (ou seja, você se equivocar quanto à precedência de um operador), o seu programa vai ter um erro lógico que, dependendo do porte do seu programa, pode ser bem difícil de se detectar. Exemplo: vamos supor que você queira calcular a média entre três números e faça da seguinte forma: media = a + b + c / 3. Aí, o que vai acontecer? A divisão vai acontecer primeiro, e depois as adições, resultando em um resultado totalmente diferente do correto. Um erro lógico. A solução seria colocar as adições entre parênteses, para que elas sejam realizadas primeiro: media = (a + b + c) / 3.

Operadores relacionais

Operadores relacionais só podem ter como resultado um valor booleano, que pode assumir apenas os valores verdadeiro ou falso.

Esse nome, booleano, é uma homenagem ao matemático britânico George Boole, que no século XIX, expressou seleções lógicas com símbolos algébricos comuns. Mais tarde, essas seleções lógicas foram chamadas de lógica booleana e adotadas na computação. Cada decisão que um computador toma envolve avaliar uma expressão usando essa lógica booleana.

Os operadores de comparação mais comuns (todos binários), que estão presentes na maioria das linguagens de programação, são:

  • igual: ==
  • diferente: !=
  • maior que: >
  • menor que: <
  • maior ou igual a: >=
  • menor ou igual a: <=

É importante ressaltar que algumas linguagens, como o PHP, também usam outros tipos de operadores de comparação.

Os símbolos acima podem variar de acordo com as linguagens de programação. E é muito comum que em pseudocódigos e fluxogramas sejam usados outros símbolos para os operadores de igualdade e diferença. No caso do operador de igualdade, é comum usar apenas um =. No caso do operador de diferença, algumas variações são o <> e o =/=.

Veja abaixo um exemplo onde todos os operadores de comparação citados são usados:

inteiro x = 2

escreva(x == 2, "\n")
escreva(x != 2, "\n")
escreva(x > 2, "\n")
escreva(x < 2, "\n")
escreva(x >= 2, "\n")
escreva(x <= 2, "\n")

se (x > 0 ) {
    escreva("x é positivo")
}
senao se (x < 0) {
    escreva("x é negativo")
}
senao {
    escreva("x é nulo")
}
escreva("\n")

Veja que os operadores de comparação podem ser usados tanto dentro de estruturas de controle, como fora delas, em alguns casos. Eles retornam uma valor lógico. Assim, eles podem ser usados em qualquer lugar que aceitar um valor lógico.

Operadores lógicos

Operadores lógicos servem para conectar duas expressões ou para negar uma expressão. Os operadores lógicos mais usados são o e, o ou e o nao. Há também o operador xor.

O operador e

O operador e (and nas linguagens de programação) serve para conectar duas expressões, de modo que as duas precisam ser verdadeiras para que a expressão seja verdadeira. Exemplos: você poderia querer verificar quais empresas com até 3 anos tem faturamento anual superior à R$ 3.000.000. Você poderia expressar isso em pseudocódigo da seguinte forma:

se (idadeEmpresa <= 3 e faturamentoAnualEmpresa > 3000000) {
    escreva("Essa empresa é nova, mas possui faturamento superior a R$ 3.000.000.\n")
}

Outro exemplo: você poderia querer verificar se um aluno teve nota no ENEM superior a 700, nota em Matemática maior que 800 e nota em Redação de pelo menos 650:

se (notaEnem > 700 e notaMatematica > 800 e notaRedacao >= 650) {
    escreva("Aprovado!\n")
}

Note que três expressões foram unidas. Por isso, foram usados dois operadores e. Se fosse em uma linguagem de programação real, logicamente, não seria possível usar o e. Ao invés disso, geralmente se permite usar o and ou o &&. Exemplo em C++:

if (notaEnem > 700 and notaMatematica > 800 && notaRedacao >= 650) {
    ...
}

Como o e só é verdadeiro quando as duas expressões que ele conecta são avaliadas como verdadeiras, então se a primeira expressão for falsa, a segunda nem é avaliada. Por isso, uma dica de performance é colocar a expressão que tem mais chance de ser falsa primeiro.

O operador ou

O operador ou também é binário assim como o e, só que ele precisa que pelo menos uma das duas expressões que ele liga seja verdadeira, e não que ambas sejam necessariamente verdadeiras, assim como o e. Mas se as duas expressões forem verdadeiras, o operador vai retornar verdadeiro também. Ele só vai retornar falso quando as duas expressões forem falsas. O ou é representado como or ou || em muitas linguagens, como C++. Veja abaixo um exemplo em C++ que verifica se uma equipe ganhou o último Campeonato Brasileiro ou a última Copa do Brasil:

if (campeaoUltimoCampBras == true || campeaoUltimaCopaBras == true) {
    ...
}

Essa expressão só vai retornar falso se a equipe não tiver ganho o Campeonato Brasileiro nem a Copa do Brasil. Veja um exemplo em pseudocódigo:

se (campeaoUltimoCampBras == verdadeiro ou campeaoUltimaCopaBras == verdadeiro) {
    ...
}

Também dá pra mesclar os operadores e e ou:

se (campeaoUltimoCampBras == verdadeiro ou campeaoUltimaCopaBras == verdadeiro e dividas == 0) {
    ...
}

Nesse caso, temos dois operadores lógicos diferentes. Qual será avaliado primeiro? Será que há alguma precedência de operadores lógicos? Sim, há precedência. Vou falar disso mais tarde.

Como o ou é verdadeiro se uma das duas expressões for verdadeira, se a primeira expressão for verdadeira, a segunda nem é avaliada. Então, uma dica de performance é colocar como primeiro operando aquele que tem mais chances de retornar verdadeiro.

O operador xor

O xor é bem parecido com o ou. A única diferença é que pra que o resultado dele seja verdadeiro, só uma das expressões deve ser verdadeira. Na prática, ele é verdadeiro quando uma das expressões é verdadeira e a outra é falsa, pois ele precisa que pelo menos uma seja verdadeira, mas não ambas. Ele não é suportado no Portugol, nem no C++. Veja um exemplo em PHP:

if ($campeaoUltimoCampBras == true xor $campeaoUltimaCopaBras == true) {
    ...
}

O operador nao

Ao contrário dos operadores e e ou, o nao é um operador unário, o que quer dizer que ele contém apenas um operando. Ele é muito simples: ele serve apenas para negar uma expressão, invertendo o valor lógico dela. Em Portugol, ele é simbolizado pelo nao. Exemplo em pseudocódigo:

inteiro x = -2
se (nao (x > 0)) {
    escreva("x é nulo ou negativo")
}

Você também pode usar esse operador em uma atribuição, para inverter o valor de uma variável lógica:

logico maiorIdade = falso
inteiro idade = 20
se (idade >= 18) {
    maiorIdade = nao maiorIdade
}
escreva(maiorIdade)

Em outras linguagens, como C++, é possível usar o !, que é a versão mais usada, ou o not:

if (!(x > 0)) {
    ...
}

Tabela verdade

A tabela verdade é uma tabela que coloca expressões ou subexpressões nas colunas da primeira linha, e o valor true (ou verdadeiro) ou false (ou falso) ou ainda T (ou V) ou F (essas versões mais curtas são mais usadas) em cada uma das colunas das linhas seguintes, para determinar a veracidade ou falsidade do que está sendo avaliado.

A tabela verdade pode ajudar bastante a entender os operadores que nós vimos, porque ela mostra quando cada um é verdadeiro e quando é falso. Segue a tabela verdade com os operadores que vimos, para duas expressões hipotéticas, x e y:

x y not x not y x and y x or y x xor y
V V F F V V F
V F F V F V V
F V V F F V V
F F V V F F F

Precedência de operadores lógicos

Os operadores lógicos e e ou tem precedências diferentes. Assim, quando eles estão juntos em uma mesma condição, é executado primeiro o que tem maior precedência, que é o e, a menos que parênteses sejam usados para evitar isso. Veja um exemplo em Portugol que demonstra isso:

inteiro idade = 15
logico epocaEleicao = falso
se (idade < 16 ou idade > 70 e epocaEleicao == verdadeiro) {
    escreva("É época de eleição, mas você não pode ou não precisa votar.")
}

Esse algoritmo tem um erro lógico. Como o e é executado primeiro, apesar de estar após o ou, por ter precedência maior que ele, então se a pessoa tiver menos de 16 anos, não importa se é época de eleição ou não, ele vai exibir a mensagem. Isso não é o desejado. Para exibir a mensagem, é necessário que seja época de eleição e que a pessoa não possa ou não seja obrigada a votar. Para corrigir, é necessário usar parênteses para forçar a execução do operador ou antes do operador e:

se ((idade < 16 ou idade > 70) e epocaEleicao == verdadeiro) {

Se a precedência dos operadores fosse igual, seria usada a associatividade para determinar quais operadores seriam avaliados primeiro.

Operadores de caracteres

Vou falar sobre o operador de caractere +. Ele é um operador de concatenação de strings (que são cadeias de caracteres), quando é usado com dois valores (variáveis ou constantes) que são um caractere ou uma cadeia de caracteres. Em algumas linguagens, esse operador é representado por outro símbolo. Por exemplo, no PHP, ele é representado por um ponto. O Portugol suporta o operador de concatenação. Veja um exemplo:

cadeia nome = "Lorraine"
cadeia sobrenome = "Santos"
cadeia nomeCompleto = nome + " " + sobrenome
escreva(nomeCompleto)