Operações com variáveis4.1
Nesta aula, você verá as diversas operações que podem ser realizadas com variáveis na linguagem C. Ao concluir este capítulo, espera-se que você seja capaz de definir o valor contido em uma variável, realizar operações matemáticas fundamentais e conduzir comparações entre diferentes variáveis. Além disso, você aprenderá como executar operações lógicas, manipular dados em nível de bits (bitwise) e utilizar as formas de operações simplificadas oferecidas pela linguagem. Por fim, estudaremos a ordem de precedência necessária para que você compreenda a sequência correta em que as operações são processadas pelo computador.
- Definir o valor contido em uma variável.
- Realizar operações matemáticas com suas variáveis.
- Realizar operações de comparação entre suas variáveis.
- Realizar operações lógicas entre suas variáveis.
- Realizar operações em nível de bits com suas variáveis.
- Conhecer as operações simplificadas.
- Saber a ordem em que as operações são realizadas.
O operador de atribuição "="4.1.1
Uma das operações mais fundamentais e utilizadas na programação é a atribuição, representada pelo símbolo "=". Sua função principal é armazenar um determinado valor em uma variável. Na linguagem C, o uso deste operador segue uma forma geral padronizada:
nome_da_variável = expressão;
Neste contexto, uma expressão pode ser qualquer combinação de valores diretos, variáveis, constantes ou chamadas de funções que utilizem operadores matemáticos, como soma (+), subtração (−), multiplicação (*), divisão (/) e resto (%), desde que o resultado final seja do mesmo tipo de dado da variável destino. Observe o funcionamento detalhado através do codigo a seguir:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
const int z = 9;
int main(){
float x;
// declara y e atribui um valor inicial
float y = 3;
// atribui um valor direto a x
x = 5;
printf("x = %f\n", x);
// atribui o valor de uma constante a x
x = z;
printf("x = %f\n", x);
// atribui o resultado de uma expressão matemática a x
x = y + 5;
printf("x = %f\n", x);
// atribui o resultado de uma função (raiz quadrada) a x
x = sqrt(9);
printf("x = %f\n", x);
system("pause");
return 0;
}
/* Saída do programa:
x = 5.000000
x = 9.000000
x = 8.000000
x = 3.000000
*/
Conforme demonstrado, o operador de atribuição pode ser empregado no momento da declaração da variável, permitindo que ela já inicie com um valor definido. É crucial compreender que o fluxo de dados ocorre sempre da direita para a esquerda, o valor ou o resultado da expressão localizada à direita do operador é calculado e, somente então, armazenado na variável especificada à esquerda.
O computador avalia primeiro o que está à direita do sinal de "=" e atribui o resultado à variável da esquerda. Tentar realizar o inverso resultará em erro de compilação.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
const int z = 9;
int main(){
float x;
float y = 3;
// Correto: a variável recebe o resultado da soma
x = y + 5;
// ERRADO: não se pode atribuir um valor a uma expressão matemática
// y + 5 = x;
// Correto: a variável recebe o valor constante
x = 5;
// ERRADO: não se pode atribuir um valor a uma constante numérica direta
// 5 = x;
system("pause");
return 0;
}
A linguagem C também suporta o que chamamos de múltiplas atribuições em uma única linha. No exemplo abaixo, o valor 5 é inicialmente copiado para a variável z. Em seguida, o conteúdo de z é copiado para y e, por fim, o valor de y é transferido para x.
#include <stdio.h>
#include <stdlib.h>
int main(){
float x, y, z;
x = y = z = 5;
printf("x = %f\n", x);
printf("y = %f\n", y);
printf("z = %f\n", z);
system("pause");
return 0;
}
Além disso, a linguagem permite a atribuição entre tipos básicos distintos. O compilador realiza uma conversão automática do valor do lado direito para o tipo da variável situada ao lado esquerdo. Entretanto, o programador deve estar atento, pois esse processo de conversão pode ocasionar a perda de informação, como a supressão de casas decimais ou o truncamento de valores.
Ao atribuir um número real (float) a uma variável inteira (int), apenas a parte inteira será preservada, descartando-se toda a precisão decimal.
#include <stdio.h>
#include <stdlib.h>
int main(){
int x = 65;
char ch;
float f = 25.1;
// ch recebe os 8 bits menos significativos de x, convertendo para a tabela ASCII
ch = x;
printf("ch = %c\n", ch);
// x recebe apenas a parte inteira de f (perda de precisão)
x = f;
printf("x = %d\n", x);
// f recebe o valor de ch convertido para real
f = ch;
printf("f = %f\n", f);
// f recebe o valor atual de x
f = x;
printf("f = %f\n", f);
system("pause");
return 0;
}
Operadores Aritméticos4.1.2
Os operadores aritméticos são ferramentas fundamentais que atuam sobre valores numéricos, sejam eles constantes, variáveis ou retornos de funções , resultando sempre em um novo valor numérico. Na linguagem C, trabalhamos com cinco operadores principais, conforme detalhado na tabela abaixo:
| Operador | Significado | Exemplo |
|---|---|---|
| + | Adição de dois valores | z = x + y |
| - | Subtração de dois valores | z = x - y |
| * | Multiplicação de dois valores | z = x * y |
| / | Quociente (divisão) de dois valores | z = x / y |
| % | Resto de uma divisão inteira | z = x % y |
Geralmente, esses operadores são utilizados em conjunto com o operador de atribuição, uma vez que o resultado de um cálculo precisa ser armazenado em algum lugar. No entanto, o resultado também pode ser passado diretamente para funções, como o printf().
#include <stdio.h>
#include <stdlib.h>
int main(){
int x = 10, y = 20, z;
// Atribui o resultado da multiplicação a z
z = x * y;
printf("z = %d\n", z);
// Atribui o quociente da divisão a z
z = y / 10;
printf("z = %d\n", z);
// Exibe o resultado da soma diretamente sem usar uma variável intermediária
printf("x+y = %d\n", x + y);
system("pause");
return 0;
}
Precedência e Parênteses4.1.2.1
Em expressões complexas, a linguagem C segue uma ordem de prioridade: as operações de multiplicação (*), divisão (/) e resto (%) são executadas sempre antes da adição (+) e da subtração (-). Para alterar essa ordem natural e forçar a execução de um cálculo específico, utilizamos os parênteses.
Considere os dois cenários abaixo:
- Em
z = x * y + 10;, primeiro multiplica-se x por y e depois soma-se 10 ao resultado. - Em
z = x * (y + 10);, a soma entre y e 10 é realizada primeiro, e o resultado final é multiplicado por x.
Operadores Unários4.1.2.2
Embora a maioria dos operadores aritméticos seja binária (precisam de dois valores), os sinais de adição e subtração também podem atuar como operadores unários. Isso significa que eles podem ser aplicados a um único valor, como no caso da inversão de sinal: na expressão x = -y;, a variável x recebe o valor de y multiplicado por -1.
Divisão e o Operador de Resto4.1.2.3
Outro ponto importante no assunto de operações com variáveis diz respeito ao comportamento da divisão. Se ambos os números envolvidos forem inteiros, o compilador retornará apenas a parte inteira do quociente, descartando as casas decimais. Para obter um resultado real (com casas decimais), ao menos um dos números deve ser tratado como ponto flutuante.
#include <stdio.h>
#include <stdlib.h>
int main(){
float x;
// Divisão entre inteiros: o resultado será 1.000000
x = 5 / 4;
printf("x = %f\n", x);
// Divisão com um número real (4.0): o resultado será 1.250000
x = 5 / 4.0;
printf("x = %f\n", x);
system("pause");
return 0;
}
O operador de resto da divisão (%) é exclusivo para tipos de dados inteiros, como int e char. Tentar utilizá-lo com números reais (float ou double) causará um erro.
Operadores Relacionais4.1.3
Os operadores relacionais são utilizados para comparar dois valores, sejam eles variáveis, constantes, expressões ou retornos de funções. Eles verificam a relação de magnitude (quem é maior ou menor) e a igualdade entre os elementos comparados.
Na linguagem C, existem seis operadores relacionais fundamentais, conforme descritos na tabela abaixo:
| Operador | Significado | Exemplo |
|---|---|---|
| > | Maior do que | x > 5 |
| >= | Maior ou igual a | x >= 10 |
| < | Menor do que | x < 5 |
| <= | Menor ou igual a | x <= 10 |
| == | Igual a | x == 0 |
| != | Diferente de | x != 0 |
Ao contrário das operações aritméticas que retornam um valor calculado, os operadores relacionais retornam um valor booleano (lógico) representado por inteiros:
- O valor UM (1): se a expressão for verdadeira (true).
- O valor ZERO (0): se a expressão for falsa (false).
É importante notar que não existem os operadores "=<", "=>" ou "<>". Os símbolos devem seguir a ordem estrita: <= e >=. O símbolo "<>" pertence a outras linguagens, como Pascal; em C, utiliza-se obrigatoriamente != para representar "diferente de".
Um dos erros mais frequentes em programação C é a confusão entre o operador de atribuição = e o operador de comparação ==. O símbolo = serve para guardar um valor, enquanto == serve para testar a igualdade. Se você utilizar o operador de atribuição acidentalmente em um teste lógico, o programa poderá compilar, mas apresentará um comportamento lógico incorreto. Abaixo, apresentamos o funcionamento prático dessas expressões:
#include <stdio.h>
#include <stdlib.h>
int main(){
int x = 5;
int y = 3;
// x é maior que 4? Verdadeiro (1)
printf("Resultado: %d\n", x > 4);
// x é igual a 4? Falso (0)
printf("Resultado: %d\n", x == 4);
// x é diferente de y? Verdadeiro (1)
printf("Resultado: %d\n", x != y);
// x é diferente de (y + 2)? Falso (0, pois 5 != 5 é falso)
printf("Resultado: %d\n", x != y + 2);
system("pause");
return 0;
}
Operadores Lógicos4.1.4
Em certas situações, os operadores aritméticos e relacionais não são suficientes para modelar a lógica necessária. Um exemplo comum é verificar se uma variável x está dentro de um intervalo específico, como na expressão matemática $0 < x < 10$. Para resolver esse tipo de problema, a linguagem C oferece três operadores lógicos, que permitem combinar múltiplas expressões relacionais em uma única estrutura composta.
| Operador | Significado | Exemplo |
|---|---|---|
| && | Operador E (AND) | (x >= 0 && x <= 9) |
| \|\| | Operador OU (OR) | (a == 'F' || b != 32) |
| ! | Operador NEGAÇÃO (NOT) | !(x == 10) |
Esses operadores funcionam com base na lógica booleana, devolvendo sempre 1 para verdadeiro e 0 para falso:
- Operador E (&&): A expressão resultante só será verdadeira se todas as condições individuais forem verdadeiras. No caso de
(x >= 0 && x <= 9), o resultado será 1 apenas se x for maior ou igual a zero e, simultaneamente, menor ou igual a nove. - Operador OU (||): A expressão resultante será verdadeira se pelo menos uma das condições for verdadeira. Por exemplo, em
(a == 'F' || b != 32), se qualquer uma das partes for real, o resultado final será 1. - Operador NEGAÇÃO (!): Este operador inverte o estado lógico. Se uma expressão é verdadeira, a negação a torna falsa, e vice-versa. Assim,
!(x == 10)é logicamente equivalente a dizer que x é diferente de 10.
Abaixo, veja como esses operadores se comportam na prática:
#include <stdio.h>
#include <stdlib.h>
int main(){
int r, x = 5, y = 3;
// Verdadeiro (1): ambas as condições são reais
r = (x > 2) && (y < x);
printf("Resultado: %d\n", r);
// Falso (0): a primeira condição (5%2 == 0) é falsa
r = (x % 2 == 0) && (y > 0);
printf("Resultado: %d\n", r);
// Verdadeiro (1): embora (y > x) seja falso, (x > 2) é verdadeiro
r = (x > 2) || (y > x);
printf("Resultado: %d\n", r);
// Falso (0): ambas as condições são falsas
r = (x % 2 == 0) || (y < 0);
printf("Resultado: %d\n", r);
// Falso (0): (x > 2) é verdadeiro, mas a negação o torna falso
r = !(x > 2);
printf("Resultado: %d\n", r);
// Verdadeiro (1): !(falso) torna-se verdadeiro, e (5 > 3) também é verdadeiro
r = !(x > 7) && (x > y);
printf("Resultado: %d\n", r);
system("pause");
return 0;
}
Tabela-Verdade4.1.4.1
A tabela abaixo resume todas as combinações possíveis para os operadores lógicos, onde a e b representam os resultados de expressões relacionais (0 para falso, 1 para verdadeiro).
a | b | !a | !b | a && b | a || b |
|---|---|---|---|---|---|
| 0 | 0 | 1 | 1 | 0 | 0 |
| 0 | 1 | 1 | 0 | 0 | 1 |
| 1 | 0 | 0 | 1 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 | 1 |
Operadores bit a bit4.1.5
A linguagem C permite a realização de operações bit a bit (bitwise) em valores numéricos. Como o computador representa internamente qualquer número em formato binário (uma sequência de 0s e 1s), esses operadores permitem que o programador manipule cada um desses bits individualmente. Por exemplo, o número 44 é armazenado na memória como 00101100.
O uso desses operadores é essencial para programadores que precisam interagir com o sistema em baixo nível. A linguagem C disponibiliza seis operadores para essa finalidade:
| Operador | Significado | Exemplo |
|---|---|---|
| ~ | Complemento (inversão) | ~x |
| & | E (AND) bit a bit | x & 167 |
| \| | OU (OR) bit a bit | x | 129 |
| ^ | OU Exclusivo (XOR) | x ^ 167 |
| << | Deslocamento à esquerda | x << 2 |
| >> | Deslocamento à direita | x >> 2 |
Estes operadores só podem ser aplicados aos tipos char, int e long. Eles não funcionam com tipos de ponto flutuante (float e double), pois a representação interna desses números segue a norma de mantissa e expoente ($M * 2^E$), o que impossibilita a manipulação direta de bits de forma simples.
Operador de Complemento (~)4.1.5.1
Este operador unário inverte todos os bits do número: onde há 0, torna-se 1, e onde há 1, torna-se 0.
#include <stdio.h>
#include <stdlib.h>
int main(){
unsigned char x, y;
x = 44; // Binário: 00101100
printf("x = %d\n", x);
y = ~x; // Invertido: 11010011 (211 decimal)
printf("~x = %d\n", y);
system("pause");
return 0;
}
Operadores Lógicos Bit a Bit (&, |, ^)4.1.5.2
Estes operadores comparam dois valores bit por bit em suas respectivas posições:
- E (&): O bit resultante é 1 apenas se os dois bits comparados forem 1.
- OU (|): O bit resultante é 1 se ao menos um dos dois bits comparados for 1.
- OU Exclusivo (^): O bit resultante é 1 apenas se os bits comparados forem diferentes entre si.
#include <stdio.h>
#include <stdlib.h>
int main(){
unsigned char x, y;
x = 44; // Em binário: 00101100
printf("x = %d\n", x);
// Operação E bit a bit (&)
y = x & 167;
printf("x & 167 = %d\n", y);
// Operação OU bit a bit (|)
y = x | 129;
printf("x | 129 = %d\n", y);
// Operação OU EXCLUSIVO bit a bit (^)
y = x ^ 167;
printf("x ^ 167 = %d\n", y);
system("pause");
return 0;
}
/* Saída do programa:
x = 44
x & 167 = 36
x | 129 = 173
x ^ 167 = 139
*/
Abaixo, veja a demonstração matemática dessas operações com o valor 44 (x):
E bit a bit:
00101100(44)10100111(167)00100100(Resultado: 36)
OU bit a bit:
00101100(44)10000001(129)10101101(Resultado: 173)
XOR bit a bit:
00101100(44)10100111(167)10001011(Resultado: 139)
Operadores de Deslocamento (<< e >>)4.1.5.3
Os operadores de deslocamento movem o conjunto de bits para a esquerda ou para a direita por N posições. Curiosamente, deslocar bits para a esquerda por uma posição equivale a multiplicar o número por 2, enquanto deslocar para a direita equivale a uma divisão inteira por 2.
valor << N; // Desloca N bits para a esquerda
valor >> N; // Desloca N bits para a direita
#include <stdio.h>
#include <stdlib.h>
int main(){
unsigned char x, y;
x = 44; // Representação binária: 00101100
printf("x = %d\n", x);
// Desloca os bits de x duas posições para a esquerda
y = x << 2;
printf("x << 2 = %d\n", y);
// Desloca os bits de x duas posições para a direita
y = x >> 2;
printf("x >> 2 = %d\n", y);
system("pause");
return 0;
}
/* Saída do programa:
x = 44
x << 2 = 176
x >> 2 = 11
*/
Ao deslocar os bits de x (44 ou 00101100) em duas posições, temos:
- x << 2: Resulta em
10110000(176 decimal). - x >> 2: Resulta em
00001011(11 decimal).
Operadores de atribuição simplificada4.1.6
Como observado anteriormente, muitos operadores são frequentemente utilizados em conjunto com o operador de atribuição. Para tornar o código mais conciso e facilitar a escrita, a linguagem C permite a utilização de operadores de atribuição simplificada. Esses operadores combinam uma operação aritmética ou lógica com a atribuição do resultado à mesma variável.
Abaixo, a seguinte tabela detalha as formas simplificadas disponíveis:
| Operador | Significado | Exemplo | Equivalente a |
|---|---|---|---|
| += | Soma e atribui | x += y | x = x + y |
| -= | Subtrai e atribui | x -= y | x = x - y |
| *= | Multiplica e atribui | x *= y | x = x * y |
| /= | Divide e atribui quociente | x /= y | x = x / y |
| %= | Divide e atribui resto | x %= y | x = x % y |
| &= | E bit a bit e atribui | x &= y | x = x & y |
| \|= | OU bit a bit e atribui | x |= y | x = x | y |
| ^= | OU exclusivo e atribui | x ^= y | x = x ^ y |
| <<= | Desloca à esquerda e atribui | x <<= y | x = x << y |
| >>= | Desloca à direita e atribui | x >>= y | x = x >> y |
Este recurso é extremamente útil quando a variável que recebe o resultado final também é um dos operandos da operação. Veja a comparação entre a escrita convencional e a simplificada na imagem a seguir:
// Comparação de escrita
int x = 10, y = 20;
// Sem operador simplificado
x = x + y - 10;
// Com operador simplificado
x += y - 10;
Precedência na Atribuição Simplificada4.1.6.1
Embora os operadores simplificados facilitem a codificação, é necessário ter cautela ao utilizá-los em expressões complexas que envolvem diferentes níveis de precedência. Um erro comum é acreditar que a simplificação mantém exatamente a mesma ordem das operações originais.
Na realidade, o operador simplificado atua sobre o resultado total da expressão que está à sua direita. Isso significa que a expressão à direita é tratada como se estivesse entre parênteses.
// Versão SEM o operador simplificado
#include <stdio.h>
#include <stdlib.h>
int main(){
int x = 10, y = 20;
// x = (10 * 20) - 10 = 190
x = x * y - 10;
printf("x = %d\n", x);
// x = 190 - 5 + 20 = 205
x = x - 5 + y;
printf("x = %d\n", x);
system("pause");
return 0;
}
/* Saída:
x = 190
x = 205
*/
// Versão COM o operador simplificado
#include <stdio.h>
#include <stdlib.h>
int main(){
int x = 10, y = 20;
// x = x * (y - 10) -> 10 * (20 - 10) = 100
x *= y - 10;
printf("x = %d\n", x);
// x = x - (5 + y) -> 100 - (5 + 20) = 75
x -= 5 + y;
printf("x = %d\n", x);
system("pause");
return 0;
}
/* Saída:
x = 100
x = 75
*/
Ao usar atribuição simplificada, o compilador avalia toda a expressão à direita do operador antes de realizar a operação combinada.
Observe a diferença de comportamento lógico:
x *= y - 10;equivale ax = x * (y - 10);e não ax = x * y - 10;.x -= 5 + y;equivale ax = x - (5 + y);e não ax = x - 5 + y;.
Essa diferença ocorre porque a atribuição (mesmo a simplificada) possui uma das menores prioridades na ordem de execução da linguagem C, forçando a resolução prévia do que está ao seu redor.
Operadores de pré e pós-incremento/decremento4.1.7
Além das formas simplificadas, a linguagem C oferece operadores específicos para incremento (++) e decremento (--). Esses operadores são utilizados para somar ou subtrair exatamente uma unidade de uma variável, integrando uma operação aritmética e uma de atribuição em um único comando.
| Operador | Significado | Exemplo | Resultado Equivalente |
|---|---|---|---|
| ++ | Incremento | ++x ou x++ | x = x + 1 |
| -- | Decremento | --x ou x-- | x = x - 1 |
Uma característica fundamental desses operadores é que eles podem ser posicionados antes ou depois do nome da variável. Embora o resultado final sobre a variável seja o mesmo (ela será alterada em uma unidade), a ordem em que isso acontece dentro de uma expressão é distinta:
- ++x (pré-incremento): Adiciona 1 à variável x antes de seu valor ser utilizado na expressão.
- x++ (pós-incremento): Utiliza o valor atual de x na expressão e somente após o uso adiciona 1 à variável.
- --x (pré-decremento): Subtrai 1 da variável x antes de seu valor ser utilizado.
- x-- (pós-decremento): Utiliza o valor atual de x e somente depois subtrai 1 da variável.
Quando utilizados sozinhos em uma linha de comando, não há diferença prática entre as formas de pré ou pós-fixação, conforme ilustrado abaixo:
#include <stdio.h>
#include <stdlib.h>
int main(){
int x = 10;
++x; // x passa a ser 11
printf("x = %d\n", x);
x = 10;
x++; // x também passa a ser 11
printf("x = %d\n", x);
system("pause");
return 0;
}
A distinção torna-se evidente quando os operadores são inseridos em expressões compostas ou atribuições. No caso do pré-incremento, a operação de soma é a prioridade:
#include <stdio.h>
#include <stdlib.h>
int main(){
int y, x = 10;
// O valor de x é incrementado para 11 e, em seguida, atribuído a y
y = ++x;
printf("x = %d\n", x);
printf("y = %d\n", y);
system("pause");
return 0;
}
/* Saída do programa:
x = 11
y = 11
*/
No exemplo acima, a instrução y = ++x; equivale a executar primeiro x = x + 1; e depois y = x;. Assim, ambas as variáveis terminam com o valor 11. Já no caso do pós-incremento, a prioridade é o uso do valor atual:
#include <stdio.h>
#include <stdlib.h>
int main(){
int y, x = 10;
// Primeiro, o valor atual de x (10) é atribuído a y.
// Somente após essa atribuição, x é incrementado para 11.
y = x++;
printf("x = %d\n", x);
printf("y = %d\n", y);
system("pause");
return 0;
}
/* Saída do programa:
x = 11
y = 10
*/
Neste segundo cenário, a instrução y = x++; equivale a executar primeiro y = x; e apenas depois x = x + 1;. Como resultado, y mantém o valor original (10), enquanto x é atualizado para 11.
Pense no posicionamento do operador como uma fila de prioridade: se o sinal vier antes da variável, a conta é feita primeiro; se vier depois, a conta fica para o final.
Modeladores de tipos (casts)4.1.8
Os modeladores de tipos, também conhecidos tecnicamente como type cast, são uma forma de conversão explícita de dados. Diferente da conversão implícita, que ocorre automaticamente quando o compilador tenta ajustar valores de tipos diferentes (como atribuir um real a um inteiro), o cast permite que o programador defina exatamente como e quando uma conversão deve ocorrer.
Na linguagem C, a forma geral para utilizar um modelador de tipo é:
(nome_do_tipo) expressão;
O modelador é definido pelo nome do tipo desejado entre parênteses, posicionado diretamente à frente da expressão que se deseja converter. Sua função é forçar o resultado daquela expressão a assumir o tipo especificado, conforme demonstrado no exemplo a seguir:
#include <stdio.h>
#include <stdlib.h>
int main(){
float x, y, f = 65.5;
// x recebe o resultado normal da divisão (6.55)
x = f / 10.0;
// y recebe o resultado convertido explicitamente para inteiro antes da atribuição
y = (int) (f / 10.0);
printf("x = %f\n", x);
printf("y = %f\n", y);
system("pause");
return 0;
}
No exemplo acima, embora as variáveis x e y utilizem a mesma base de cálculo, o comportamento final é distinto devido ao cast. Na linha 6, o resultado da divisão é forçado a tornar-se um valor inteiro (int) antes de ser armazenado na variável y. Como resultado dessa conversão explícita, a parte fracionária do número é descartada, transformando o valor 6.55 em 6.000000.
O uso do cast é uma ferramenta poderosa para garantir que operações entre tipos diferentes resultem no comportamento esperado pelo desenvolvedor, evitando ambiguidades e controlando a perda de precisão de forma consciente.
O operador vírgula (,)4.1.9
Na linguagem C, o operador vírgula (,) desempenha dois papéis fundamentais e distintos, dependendo do contexto em que é inserido no código:
- Como pontuação: É utilizado para separar elementos em uma lista, como os argumentos de uma função (exemplo:
int minha_funcao(int a, float b)) ou a declaração de múltiplas variáveis do mesmo tipo em uma única linha. - Para execução sequencial: Permite determinar uma lista de expressões que devem ser processadas em sequência, da esquerda para a direita. O valor final da expressão inteira será sempre o valor da última expressão da lista.
Considere o seguinte exemplo de encadeamento:
x = (y = 2, y + 3);
Nesse cenário, o computador executa as operações na seguinte ordem: primeiro, o valor 2 é atribuído à variável y; em seguida, o valor 3 é somado ao valor atual de y; por fim, o resultado dessa última operação (5) é atribuído à variável x. É possível encadear quantas expressões forem necessárias utilizando este operador.
É importante compreender a distinção semântica entre os sinais: na linguagem C, a vírgula (,) atua como um separador de comandos, enquanto o ponto e vírgula (;) funciona como um terminador de comandos.
Dessa forma, a vírgula permite agrupar operações relacionadas em uma única instrução, mantendo a clareza sobre a ordem de execução dos processos.
Precedência de operadores4.1.10
Devido à vasta quantidade de operadores disponíveis na linguagem C, o uso de múltiplos elementos em uma única expressão pode tornar sua interpretação confusa. Para resolver esse problema, a linguagem adota regras de precedência de operadores, que permitem ao compilador decidir a ordem exata em que cada parte de uma expressão deve ser executada.
Essas regras seguem a lógica matemática tradicional: por exemplo, operações de multiplicação e divisão são processadas antes da soma e da subtração. Caso o programador deseje alterar essa ordem natural, deve-se utilizar os parênteses para forçar a execução prioritária de determinado trecho do cálculo.
A Tabela 3.8 detalha a hierarquia de prioridades. Os operadores situados no topo da lista possuem a maior precedência (são executados primeiro), enquanto os da base possuem a menor precedência (são executados por último).
| Nível de Precedência | Operadores |
|---|---|
| Altíssima | ++ -- (Pré-fixados), () (Parênteses), [] (Array), . -> (Structs) |
++ -- (Pós-fixados) | |
+ - (Unários), ! ~ (Lógica e Bit a bit), (tipo) (Cast), * & (Ponteiros), sizeof | |
| Aritmética | * / % (Multiplicação, Divisão e Resto) |
+ - (Soma e Subtração) | |
| Bit a Bit | << >> (Deslocamentos) |
| Relacional | < <= > >= (Comparações de magnitude) |
== != (Igualdade e Diferença) | |
| Lógica | & (E bit a bit), ^ (XOR bit a bit), | (OU bit a bit), && (E lógico), || (OU lógico) |
| Ternária | ?: |
| Atribuição | = += -= *= /= %= <<= >>= &= ^= |= |
| Baixíssima | , (Vírgula) |
Você notará que alguns símbolos se repetem em categorias diferentes, como o asterisco (*), que pode representar tanto a multiplicação quanto o acesso ao conteúdo de um ponteiro. O compilador diferencia o uso pelo contexto da expressão. Muitos desses operadores avançados serão explorados detalhadamente conforme avançarmos neste capítulo e nos próximos estudos.
Questões4.2
1. O operador de atribuição (=) é fundamental na linguagem C. Analise o trecho de código abaixo e identifique qual das instruções gera um erro de compilação, justificando sua resposta com base na "Regra de Direção" apresentada no texto.
int main() {
int a = 10;
int b = 20;
int c;
c = a + b; // Instrução I
a = 5; // Instrução II
a + b = c; // Instrução III
c = 15; // Instrução IV
return 0;
}
2. A divisão em C pode se comportar de maneira diferente dependendo dos tipos de dados envolvidos. Considere o código a seguir:
#include <stdio.h>
int main() {
float x, y;
x = 7 / 2;
y = (float) 7 / 2;
printf("x = %f\n", x);
printf("y = %f\n", y);
return 0;
}
Sem executar o código, determine qual será a saída exata impressa para x e y. Explique por que o valor de x pode não ser o esperado (3.5), mencionando o conceito de divisão inteira e perda de precisão.
3. Sobre os operadores de pré-incremento (++x) e pós-incremento (x++), analise a saída do seguinte programa e explique o comportamento da variável res em cada caso:
int main() {
int x = 5, res;
// Caso 1
res = x++;
// Qual o valor de 'res' e 'x' aqui?
x = 5; // Resetando x
// Caso 2
res = ++x;
// Qual o valor de 'res' e 'x' aqui?
return 0;
}
4. Realize manualmente as operações bitwise (bit a bit) abaixo, considerando A = 12 (Binário: 00001100) e B = 10 (Binário: 00001010). Apresente o resultado em decimal.
- a)
A & B(AND) - b)
A | B(OR) - c)
A ^ B(XOR) - d)
A >> 2(Deslocamento à direita)
5. Escreva um programa completo em C que solicite ao usuário dois números inteiros. O programa deve:
- Calcular a divisão do primeiro pelo segundo.
- Utilizar um modelador de tipo (cast) para garantir que o resultado da divisão inclua as casas decimais, mesmo que os números digitados sejam inteiros.
- Imprimir o resultado final.
6. Crie um programa que verifique se um número é "Válido". Para este exercício, um número é considerado válido se ele atender a todas as seguintes regras simultaneamente (utilize operadores lógicos e relacionais):
- Ser maior que 0.
- Ser menor ou igual a 100.
- Ser um número par (Dica: utilize o operador de resto
%).
O programa deve imprimir 1 se o número for válido e 0 caso contrário.
7. Utilize os operadores de atribuição simplificada para reescrever e programar a seguinte lógica de forma mais concisa. Considere que a variável total inicia com o valor 100.
- Some 50 ao
total. - Divida o
totalpor 2. - Obtenha o resto da divisão do
totalpor 5. - Imprima o valor final.
8. Analise a expressão abaixo considerando a tabela de precedência de operadores: int x = 10 + 5 * 2 < 25 && 4 != 5;
Qual será o valor final armazenado em x (0 ou 1)? Descreva o passo a passo da resolução que o computador faria (ex: qual conta é feita primeiro, qual comparação vem depois, etc.).
Próximos passos4.3
No próximo capítulo, Estruturas de Controle Condicional, avançaremos além das operações sequenciais. Você aprenderá como fazer seu programa tomar decisões baseadas nos resultados das operações lógicas e relacionais que vimos aqui, utilizando os comandos if, else e switch para criar fluxos de execução dinâmicos.