Lesson 04: Operations and Operators
4.1 Expressions, Operands and Operators
Expression, Operands, and Operators
Expressions are used to calculate a result involving a number of operands (either variables or constants). Operators allow us to manipulate the variables and constants in the expressions. Operands are the constants or variables upon which the operators operate upon.
In programming terms, an expression is something, which expresses a value. It is either a simple value — a single operand to express a single value.
120
"Welcome to EE2450"
score
or a set of operands that are combined together using operators:
5 + 12 + 15
value1 + value2
pow(x, 2) * r + 2
The operands of an expression may be variables, constants, other subexpressions, or nonvoid function callings. Figure 1.1 illustrates the difference between operands, operators, and expressions.
Figure 1.1: Operands, operators, and expressions
Expressions
Expressions
A simple expression is:
top = 10;
Here, the variable top is assigned the constant value 10. In this example, 10 is the expression.
Another example is:
sum = 2 + 6 + 10;
The expression 2 + 6 + 10 consists of two operators and three operands. This expression is simple to work out as variables 2, 6, and 10 are added together and the result of 18 is stored in sum.
Operators
Operators
Operators are symbols that instruct the computer to perform a single, simple task, and most consist of just one or two characters (=, +, *, <=, or == — note that spaces between twocharacter operators are not allowed), but some operators consist of a complete word (new or return). Operands are expressions or values on which an operator acts or works.
Operands
Operands
An operand is simply something that an operator works on. In an expression like sum / 3.0, one operand is a variable (sum) and the other is a constant (3.0). Constants, variables, and function return values may all serve as operands and may be intermixed when forming expressions.
(a)
(b)
(c)
(d)
 Operands may be constants
 Operands may be variables
 Operands may be more complex expressions; in this example, 2, x, 4, and b are * operands while 2 * x and 4 * y are + operands.
 The value returned by a nonvoid function may also serve as an operand
Number of Operands
The number of operands of an operator is called its arity. Based on arity, operators are classified as nullary (no operands), unary (1 operand), binary (2 operands), ternary (3 operands), etc.
Unary Operators
Unary operators only require one operand. Most of the time the operand is placed to the right of the operator but sometimes it is placed to the left.
+a  Positive 
a  Negative 
!x  Logical NOT 
~x  Bitwise not 
++a  Increment 
a++  Increment 
a  Decrement 
a  Decrement 
*a  Pointer to an address 
&a  The address of a variable 
Binary Operators
Binary operators require two operands. The arithmetic operators are the most familiar examples of binary operators. The assignment operator is also common. For example:
x = counter + 10;
counter and 10 are the + operator's operands, and x and the value of the expression counter + 10 are the = operator's operands. Note that the above example ends with a semicolon, which also makes it an example of a statement (specifically, an assignment statement).
Ternary Operator
The single C/C++ ternary operator is the conditional operator, which is formed with two symbols and requires three operands:
op1 ? op2 : op3;
The conditional operator is described in detail later in this chapter.
4.2 Operators
C/C++ has a rich set of operators that can be divided into the following classes:
 Binary Operators
Arithmetic, bitwise, shift operators  Nonbinary Operators
Assignment, Logical, relational, conditional, and pointer operators

Assignment
Operators 
Arithmetic
Operators 
Logical
Operators 
Bitwise
Operators 
Relational
Operators 
Inc/Decrement
Operators 
Shift
Operators 
Conditional
Opertaors 
Pointer
Operators 
Comma
Operators 
Miscellaneous
Operators 
Scope Resolution
Operator (C++) 
PointertoMember
Operators (C++) 
I/O
Operators (C++)
 Assignment
Operators  Arithmetic
Operators  Logical
Operators  Bitwise
Operators  Relational
Operators  Inc/Decrement
Operators  Shift
Operators  Conditional
Opertaors  Pointer
Operators  Comma
Operators  Miscellaneous
Operators  Scope Resolution
Operator (C++)  PointertoMember
Operators (C++)  I/O
Operators (C++)
AssignmentOperators
Assignment Operators
The equal sign (=) in C/C++ is an operator, called the assignment operator. It operates by setting the operand on its left to a value of the operand on its right. In other words, the equal sign (=) causes the value on its right to be assigned to (placed in) the variable on its left; that is, following the statement:
the variable a will have the value '8'.
ArithmeticOperators
Arithmetic Operators
C/C++ have the following arithmetic operators:
Operator  Meaning  Example (A = 25, B = 7)  Description  

+  Addition  A + B  25 + 7 = 32  Add A and B 
  Subtraction  A  B  25 7 = 18  Subtract B from A 
*  Multiplication  A * B  25 * 7 = 175  Multiply A and B 
/  Division  A / B  25 / 7 = 3  Divide A by B 
%  Modulus (Remainder)  A % B  25 % 7 = 4  The remainder of dividing A by B 
+  Unary Positive  +A  +25  
  Unary Negative  B  7 
Unary Positive
The role of a positive operator is to preserve the sign.
int i = 100;
int j = +i; // j = 100
Although such construction is syntactically correct, using it doesn’t make much sense and it would be hard to find a good rationale for doing so.
Unary Negative
The negative operator changes the sign of its operand. A positive number becomes negative, and a negative number becomes positive.
int i = 100;
int j = i; // j = 100
int k = j; // k = 100
Remainder
The remainder operator (%) is quite peculiar because it has no equivalent amount of traditional arithmetic operators. You can use the % operator to find the remainder of a division, it is a binary operator (it performs the modulo operators), and both operands can not be floats.
int i = 12;
int j = 5;
int k = i % j;
The k variable is set to the value of 2.
Compute Quotient and Remainder
Write a program that asks a user to enter two integers (dividend and divisor), which are stored in variables dividend and divisor respectively. Then the quotient is evaluated using / (the division operator), and stored in the quotient. Similarly, the remainder is evaluated using % (the modulo operator) and stored in the remainder.
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> int main() { int dividend, divisor, quotient, remainder; printf("Enter dividend: "); scanf("%d", ÷nd); printf("Enter divisor: "); scanf("%d", &divisor); // Computes quotient quotient = dividend / divisor; // Computes remainder remainder = dividend % divisor; printf("Quotient = %d\n", quotient); printf("Remainder = %d", remainder); return 0; }
LogicalOperators
Logical Operators
It's often convenient to test the true/false values of several expressions at the same time and combine the results into a single true/false value.
True/false values can be operated on with logical operators. There are three logical operators in C/C++ (see below Table). Two of them combine two true/false values, and the third negate a true/false value.
Operator  Meaning  Example  Description 

  Logical OR  (x==3)  (x<1)  It returns true when at least one of the conditions is true 
&&  Logical AND  (x>0) && (x<10)  It returns true when both conditions are true 
!  Logical NOT  !(x > 10)  It reverses the state of the operand ("(x>10)") If (x>10) is true, the logical NOT operator makes it false. 
The logical operator will convert the operands into Boolean values. Any value that is not equal to zero would be considered as logic 1 (even negative numbers), and values that equal 0 are considered as logic 0.
Example of all the logical operators
Try the following example:
#include <stdio.h> main() { int a = 5; int b = 20; int c ; if ( a && b ) { printf("Line 1  Condition is true.\n" ); } if ( a  b ) { printf("Line 2  Condition is true.\n" ); } /* lets change the value of a and b */ a = 0; b = 10; if ( a && b ) { printf("Line 3  Condition is true.\n" ); } else { printf("Line 3  Condition is not true.\n" ); } if ( !(a && b) ) { printf("Line 4  Condition is true.\n" ); } }
When you compile and execute the above program, it produces the following results −
Line 1  Condition is true.
Line 2  Condition is true.
Line 3  Condition is not true.
Line 4  Condition is true.
BitwiseOperators
Bitwise Operators
C/C++ provides operators that act upon the actual bits that comprise a value. The bitwise operators can be used only on integral types. The bitwise operators are as follows:
Operator  Meaning  Example (A = 12, B = 38)  Description 

~  Bitwise not  ~A = 13  Binary One's Complement Operator is unary and has the effect of 'flipping' bits. 
&  Bitwise and  (A & B) = 4  Binary AND Operator copies a bit to the result if it exists in both operands. 
  Bitwise or  (A  B) = 46  Binary OR Operator copies a bit if it exists in either operand. 
^  Bitwise xor  (A ^ B) = 42  Binary XOR Operator copies the bit if it is set in one operand but not both. 
The truth tables for bitwise operators are as follows:
x  y  ~x  x & y  x  y  x ^ y 

0  0  1  0  0  0 
0  1  1  0  1  1 
1  0  0  0  1  1 
1  1  0  1  1  0 
These rules are applied to each bit in each operand when the bitwise and, or, and xor operations are performed.
A simple bitwise and operation is shown below:
00001100 = 12
& 00100110 = 38

00000100 = 4
A bitwise or operation looks like this:
00001100 = 12
 00100110 = 38

00101110 = 46
A bitwise xor operation is shown here:
00001100 = 12
^ 00100110 = 38

00101010 = 42
The One's Complement Operator
The one's complement operator, ~, will invert all the bits in its operand. For example, if a is an 8bit integer variable, and its value is a = 12. The binary value of a is 0b00001100.
The one's complement of an operation is shown here:
~ 00001100 = 12

11110011 = 13
Bit 7 is the sign bit, so 0b11110011 is 13 in decimal.
Example of all the bitwise operators
#include <stdio.h> int main() { unsigned int a = 60; // 60 = 0011 1100 unsigned int b = 13; // 13 = 0000 1101 int c = 0; c = a & b; // 12 = 0000 1100 printf("Line 1  Value of c is %d\n", c ); c = a  b; // 61 = 0011 1101 printf("Line 2  Value of c is %d\n", c ); c = a ^ b; // 49 = 0011 0001 printf("Line 3  Value of c is %d\n", c ); c = ~a; //61 = 1100 0011 printf("Line 4  Value of c is %d\n", c ); return 0; }
#include <stdio.h> #include <iostream> using namespace std; int main() { unsigned int a = 60; // 60 = 0011 1100 unsigned int b = 13; // 13 = 0000 1101 int c = 0; c = a & b; // 12 = 0000 1100 cout << "Line 1  Value of c is " << c << endl; c = a  b; // 61 = 0011 1101 cout << "Line 2  Value of c is " << c << endl; c = a ^ b; // 49 = 0011 0001 cout << "Line 3  Value of c is " << c << endl; c = ~a; //61 = 1100 0011 cout << "Line 4  Value of c is " << c << endl; return 0; }
When you compile and execute the above program, it produces the following results −
Line 1  Value of c is 12
Line 2  Value of c is 61
Line 3  Value of c is 49
Line 4  Value of c is 61
RelationalOperators
Relational Operators
The relational operators compare two expressions, producing a Boolean (true/false) result. They are most often used in the flow of control statements such as ifelse statements, forloops, whileloops, and dowhile loops.
Operator  Meaning  Example A = 5; B = 2;  Description 

>  Greater than  A > B  5 > 2, result is True (1) 
>=  Greater than or equal  A >= B  5 >= 2, result is True (1) 
<  Less than  A < B  5 < 2, result is False (0) 
<=  Less than or equal  A <= B  5 <= 2, result is False (0) 
==  Equal  A == B  5 == 2, result is False (0) 
!=  Not equal  A != B  5 != 2, result is True (1) 
Chaining of Comparison Operators in C
In Python, an expression like "c > b > a" is treated as "c > b and b > a", but this type of chaining does not happen in C. For example, consider the following program. The output of the following program is "FALSE".
#include <stdio.h> int main() { int a = 10, b = 20, c = 30; if (c > b > a) printf("TRUE"); else printf("FALSE"); return 0; }
In line 7, the (c > b > a) is treated as ((c > b) > a), associativity of '>' is left to right. So, the expression becomes ((30 > 20) > 10), while the (30 > 20) is ture (1). The final expression becomes (1 > 20), and the result is false (0).
Inc/DecrementOperators
Increment and Decrement Operators
Increment operators are used to increase the value of the variable by one, and decrement operators are used to decrease the value of the variable by one in the C/C++ program.
Operator  Meaning  Example a = 3; b = 7;  Description 

++ var var ++ 
Increment operator  ++a; b++; 
++a ⇒ a += 1 ⇒ a + 1 = 4 b++ ⇒ b += 1 ⇒ b + 1 = 8 
 var var  
Decrement operator  a; b; 
a ⇒ a = 1 ⇒ a  1 = 2 b ⇒ b = 1 ⇒ b  1 = 6 
Pre/Post Increment and Decrement Operators
When you use the increment and decrement operator in the expressions, they have different meanings. The below table will explain the difference between pre/postincrement and decrement operators in the C/C++ programming language.
 PreIncrement Operator
A preincrement operator is used to increment the value of a variable before using it in an expression. In the PreIncrement, the value is first incremented/decremented and then used inside the expression.  PreDecrement Operator
A predecrement operator is used to decrement the value of a variable before using it in an expression. In the PreDecrement, the value is first decremented and then used inside the expression.  PostIncrement Operator
A postincrement operator is used to increment the value of the variable after executing the expression completely in which postincrement is used. In the PostDecrement, value is first used in an expression and then incremented.  PostDecrement operator
A postdecrement operator is used to decrement the value of the variable after executing expression completely in which postdecrement is used. In the PostDecrement, value is first used in an expression and then decremented.
Syntax (i = 5, j = 2)  Function  Description 

a = ++i;  Preincrement operator (++i) 

a = i;  Predecrement operator (i) 

a = i++;  Postincrement operator (i++) 

a = i;  Postdecrement operator (i) 

a = (++i) + (j);  Preincrement (++i) and Postdecrement (j) operators 

Simple post and preincrement operator
// C program to demonstrate working of unary increment // and decrement operators #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> int main() { // Post increment int a = 1; printf("a value: %d \n", a); int b = a++; printf("b value after b = a++ : %d \n", b); printf("a value after b = a++ : %d \n\n", a); // Pre increment a = 1; printf("a value: %d \n", a); b = ++a; printf("b value after b = ++a : %d \n", b); printf("a value after b = ++a : %d \n\n", a); // Post decrement a = 5; printf("a value before decrement: %d \n", a); b = a; printf("b value after b = a : %d \n", b); printf("a value after b = a : %d \n\n", a); // Pre decrement a = 5; printf("a value: %d \n", a); b = a; printf("b value after b = a : %d \n", b); printf("a value after b = a : %d \n", a); return 0; }
Result:
a value: 1
b value after b = a++ : 1
a value after b = a++ : 2
a value: 1
b value after b = ++a : 2
a value after b = ++a : 2
a value before decrement: 5
b value after b = a : 5
a value after b = a : 4
a value: 5
b value after b = a : 4
a value after b = a : 4
Evaluating post and preincrement together
#include <iostream> using namespace std; int main() { int A = 10, B = 20, C = 0; C = A++ + ++B * 10 + B++; // 1. ++B => B= B + 1 = 21 // 2. C = A + B * 10 + B = 10 + 21 * 10 + 21 = 241 // 3. A++ => A = A + 1 = 11 // B++ => B = B + 1 = 22 cout << "A = " << A << "," << "B = " << B << "," << "C = " << C << endl; return 0; }
Output:
A = 11,B = 22,C = 241
ShiftOperators
Shift Operators
The shift operators move bits to the right or the left. There are two bitwise shift operators in C:
Operator  Meaning  Example (A = 0b0011 1100 = 60)  Description 

X << n  Binary Left Shift Operator. The left operands value is moved left by the number of bits specified by the right operand.  A << 2 => 0b1111 0000 = 240  Shift the value 60 to left 2 bits 
X >> n  Binary Right Shift Operator. The left operands value is moved right by the number of bits specified by the right operand.  A >> 2 => 0b0000 1111 = 15  Shift the value 60 to right 2 bits 
X must be an integer (signed or unsigned)
n must be an unsigned integer and the value is from 0 to less than the number of bits of X value.
Logical Shift (for unsigned integers)
In a logical shift, zeros are shifted in to replace the discarded bits. Therefore, the logical and arithmetic leftshifts are exactly the same.
However, as the logical rightshift inserts value 0 bits into the most significant bit, instead of copying the sign bit, it is ideal for unsigned binary numbers, while the arithmetic rightshift is ideal for signed two's complement binary numbers.
Left Logical Shift
The leftshift operator causes the bits in X to be shifted to the left by the number of positions specified by n. The bit positions that have been vacated by the shift operation are zerofilled. A left shift is a logical shift (the bits that are shifted off the end are discarded, including the sign bit).
Right Logical Shift
The rightshift operator causes the bit pattern in X to be shifted to the right by the number of positions specified by n. For unsigned numbers, the bit positions that have been vacated by the shift operation are zerofilled.
Arithmetic shift (for signed integers)
In an arithmetic shift, the bits that are shifted out of either end are discarded. In a left arithmetic shift, zeros are shifted in on the right; in a right arithmetic shift, the sign bit (the MSB in two's complement) is shifted in on the left, thus preserving the sign of the operand.
Left Arithmetic Shift
A left arithmetic shift of a number X by n is equivalent to multiplying X by 2^{n} and is thus equivalent to a logical left shift; a logical shift would also give the same result since MSB anyway falls off the end and there's nothing to preserve. The leftshift operations can be used on unsigned and signed numbers.
Right Arithmetic Shift
A right arithmetic shift of a number X by n is equivalent to integer division of X by 2^{n} ONLY if X is nonnegative! Integer division is nothing but mathematical division and round towards 0. For signed numbers, the sign bit is used to fill the vacated bit positions. In other words, if the number is positive, 0 is used, and if the number is negative, 1 is used.
Logical and Arithmetic Shifts
When shifting left (<<), there is no difference between arithmetic and logical shift.
When shifting an unsigned value, the >> operator in C is a logical shift. When shifting a signed value, the >> operator is an arithmetic shift.
#include <stdio.h> #include <stdint.h> void bin8ToStr(uint8_t num, char* bitStr); int main() { int8_t i8 = 0b11101010; uint8_t u8 = 0b11101010; int8_t iResult; uint8_t uResult; char bitStr[17] = {0}; bin8ToStr(i8, bitStr); printf("Original signed Value: %s (0x%02X, %03d) \n", bitStr, i8 & 0xFF, i8); iResult = i8 >> 2; bin8ToStr(iResult, bitStr); printf("Shifted 2 right: %s (0x%02X, %03d) \n", bitStr, iResult & 0xFF, iResult); iResult = i8 << 2; bin8ToStr(iResult, bitStr); printf("Shifted 2 left: %s (0x%02X, %03d) \n\n", bitStr, iResult & 0xFF, iResult); i8 = 22; bin8ToStr(i8, bitStr); printf("Original signed Value: %s (0x%02X, %03d) \n", bitStr, i8, i8); iResult = i8 >> 2; bin8ToStr(iResult, bitStr); printf("Shifted 2 right: %s (0x%02X, %03d) \n", bitStr, iResult & 0xFF, iResult); iResult = i8 << 2; bin8ToStr(iResult, bitStr); printf("Shifted 2 left: %s (0x%02X, %03d) \n\n", bitStr, iResult & 0xFF, iResult); bin8ToStr(u8, bitStr); printf("Original unsigned Value : %s (0x%02X, %03d) \n", bitStr, u8, u8); uResult = u8 >> 2; bin8ToStr(uResult, bitStr); printf("Shifted 2 right: %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = u8 << 2; bin8ToStr(iResult, bitStr); printf("Shifted 2 left: %s (0x%02X, %03d) \n\n", bitStr, uResult & 0xFF, uResult); return 0; } void bin8ToStr(uint8_t num, char* bitStr) { int i; for (i = 0; i < 8; i++) bitStr[i] = (char)((num >> (7i)) & 0x01) + '0'; }
Output
Original signed Value: 11101010 (0xEA, 22)
Shifted 2 right: 11111010 (0xFA, 06)
Shifted 2 left: 10101000 (0xA8, 88)
Original signed Value: 00010110 (0x16, 022)
Shifted 2 right: 00000101 (0x05, 005)
Shifted 2 left: 01011000 (0x58, 088)
Original unsigned Value : 11101010 (0xEA, 234)
Shifted 2 right: 00111010 (0x3A, 058)
Shifted 2 left: 01011000 (0xA8, 168)
Rotate (Circular Shifts)
In this operation, sometimes called rotate no carry, the bits are "rotated" as if the left and right ends of the register were joined. The value that is shifted into the right during a leftshift is whatever value was shifted out on the left, and vice versa for a rightshift operation. This is useful if it is necessary to retain all the existing bits, and is frequently used in digital cryptography.
Left Circular Shift or Rotate
Right Circular Shift or Rotate
Circular Shifts (Rotate) in C
C++ 20 already provides std::rotl and std::rotr rotate function, but C does not have operators or standard functions for rotating, even though most processors have bitwise operation instructions for rotate (e.g. Intel x86 has ROL and ROR instructions).
#include <stdio.h> #include <stdlib.h> #include <stdint.h> // for uint8_t, to get 8bitwide rotates #include <stdbool.h> void bin8ToStr(uint8_t num, char* bitStr); uint8_t rotl8(uint8_t num, uint8_t count); uint8_t rotr8(uint8_t num, uint8_t count); int main() { uint8_t u8 = 0b10010110; uint8_t uResult; char bitStr[9] = {0}; bin8ToStr(u8, bitStr); printf("Original Value: %s (0x%02X, %03d) \n", bitStr, u8 & 0xFF, u8); uResult = rotl8(u8, 1); bin8ToStr(uResult, bitStr); printf("Left circular Shift by 1 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotl8(u8, 2); bin8ToStr(uResult, bitStr); printf("Left circular Shift by 2 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotl8(u8, 3); bin8ToStr(uResult, bitStr); printf("Left circular Shift by 3 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotl8(u8, 4); bin8ToStr(uResult, bitStr); printf("Left circular Shift by 4 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotl8(u8, 5); bin8ToStr(uResult, bitStr); printf("Left circular Shift by 5 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotl8(u8, 6); bin8ToStr(uResult, bitStr); printf("Left circular Shift by 6 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotl8(u8, 7); bin8ToStr(uResult, bitStr); printf("Left circular Shift by 7 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotl8(u8, 8); bin8ToStr(uResult, bitStr); printf("Left circular Shift by 8 : %s (0x%02X, %03d) \n\n", bitStr, uResult & 0xFF, uResult); uResult = rotr8(u8, 1); bin8ToStr(uResult, bitStr); printf("Right circular Shift by 1 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotr8(u8, 2); bin8ToStr(uResult, bitStr); printf("Right circular Shift by 2 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotr8(u8, 3); bin8ToStr(uResult, bitStr); printf("Right circular Shift by 3 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotr8(u8, 4); bin8ToStr(uResult, bitStr); printf("Right circular Shift by 4 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotr8(u8, 5); bin8ToStr(uResult, bitStr); printf("Right circular Shift by 5 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotr8(u8, 6); bin8ToStr(uResult, bitStr); printf("Right circular Shift by 6 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotr8(u8, 7); bin8ToStr(uResult, bitStr); printf("Right circular Shift by 7 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); uResult = rotr8(u8, 8); bin8ToStr(uResult, bitStr); printf("Right circular Shift by 8 : %s (0x%02X, %03d) \n", bitStr, uResult & 0xFF, uResult); return 0; } // void bin8ToStr(uint8_t num, char* bitStr) { int i; for (i = 0; i < 8; i++) bitStr[i] = (char)((num >> (7i)) & 0x01) + '0'; } // uint8_t rotl8(uint8_t num, uint8_t count) { uint8_t result; result = (count % 8) ? (num << count)  (num >> (8  count)) : num; return result; } // uint8_t rotr8(uint8_t num, uint8_t count) { uint8_t result; result = (count % 8) ? (num >> count)  (num << (8  count)) : num; return result; } //
Output
Original Value: 10010110 (0x96, 150)
Left circular Shift by 1 : 00101101 (0x2D, 045)
Left circular Shift by 2 : 01011010 (0x5A, 090)
Left circular Shift by 3 : 10110100 (0xB4, 180)
Left circular Shift by 4 : 01101001 (0x69, 105)
Left circular Shift by 5 : 11010010 (0xD2, 210)
Left circular Shift by 6 : 10100101 (0xA5, 165)
Left circular Shift by 7 : 01001011 (0x4B, 075)
Left circular Shift by 8 : 10010110 (0x96, 150)
Right circular Shift by 1 : 01001011 (0x4B, 075)
Right circular Shift by 2 : 10100101 (0xA5, 165)
Right circular Shift by 3 : 11010010 (0xD2, 210)
Right circular Shift by 4 : 01101001 (0x69, 105)
Right circular Shift by 5 : 10110100 (0xB4, 180)
Right circular Shift by 6 : 01011010 (0x5A, 090)
Right circular Shift by 7 : 00101101 (0x2D, 045)
Right circular Shift by 8 : 10010110 (0x96, 150)
Rotate through Carry
Rotate through carry is a variant of the rotate operation, where the bit that is shifted in (on either end) is the old value of the carry flag, and the bit that is shifted out (on the other end) becomes the new value of the carry flag.
A single rotate through carry can simulate a logical or arithmetic shift of one position by setting up the carry flag beforehand. For example, if the carry flag contains 0, then x RIGHTROTATETHROUGHCARRYBYONE is a logical rightshift, and if the carry flag contains a copy of the sign bit, then x RIGHTROTATETHROUGHCARRYBYONE is an arithmetic rightshift. For this reason, some microcontrollers such as lowend PICs just have to rotate and rotate through carry and don't bother with arithmetic or logical shift instructions.
Rotate through carry is especially useful when performing shifts on numbers larger than the processor's native word size because if a large number is stored in two registers, the bit that is shifted off one end of the first register must come in at the other end of the second. With rotatethroughcarry, that bit is "saved" in the carry flag during the first shift, ready to shift in during the second shift without any extra preparation.
Left Rotate Through Carry
Right Rotate Through Carry
ConditionalOpertaors
Conditional Operator
The conditional operator is the only C/C++ operator that operates on three operands. It consists of two symbols: a question mark (?) and a colon (:).
Operator  Meaning  Example  Description 

( )? :  Conditional Operator  nabs = (n > 0)? n : n;  Find the absolute value of variable n 
The syntax of conditional operator is:
(condition) ? (expr_true) : (expr_false) ;
The conditional operator was invented because a particular construction occurs often in C/C++ programs and it is nice to shorten it.
PointerOperators
Pointer Operators
There are two types of pointer operators: * and &. A pointer is an object that contains the address of another object. Or, put differently, an object that contains the address of another object is said to "point to" the other object.
The & Pointer Operator
The & operator returns the address of the object it precedes. For example, if the integer x is located at memory address $1000, then
p = &x;
places the value 0x1000 into p. The & can be thought of as "the address of". For example, the above statement could be read as "place the address of x into p".
The * Pointer Operator
The * is the indirection operator. It uses the current value of the variable it precedes as the address at which data will be stored or obtained. For example, the following fragment
p = & x; // put address of x into p
*p = 100; // use address contained in p
places the value 100 into x. The * can be remembered as "at address". In this example, it could be read as "place the value 100 at address p". Since p contains the address of x, the value 100 is actually stored in x. In other words, p is said to "point to" x. The * operator can also be used on the righthand side of an assignment. For example:
p = &x;
*p = 100;
z = *p / 25;
places the value of 4 into z.
The detail information about pointers will be discussed in EE2440Lesson 03: Minterms and Maxterms
CommaOperators
The comma in C/C++
In C/C++, comma (,) can be used in two contexts:
Comma as an operator
Comma as an operator
The comma operator (,) is a binary operator that evaluates its first operand and discards the result, it then evaluates the second operand and returns this value (and type). The use of the comma token as an operator is distinct from its use in function calls and definitions, variable declarations, enum declarations, and similar constructs, where it acts as a separator.
The comma operator has the lowest precedence of any C operator and acts as a sequence point.
// comma as an operator
int i = (5, 10); // 10 is assigned to i
int j = (f1(), f2()); // f1() is called (evaluated) first followed by f2().
// The returned value of f2() is assigned to j
The primary use of the comma operator is to produce side effects in the following situations:
 Calling a function
 Entering or repeating an iteration loop
 Testing a condition
 Other situations where a side effect is required but the result of the expression is not immediately needed
The following table gives some examples of the uses of the comma operator.
Statement  Description 

f (a, (t = 3, t + 2), c);  The function f() has only three arguments: the value of a, the value 5, and the value of c. 
for ( i=0; i < 2; ++i, f() );  A for statement in which i is incremented and f() is called at each iteration. 
if ( f(), ++i, i > 1 ) { /* ... */ } 
An if statement in which function f() is called, variable i is incremented, and variable i is tested against a value. The first two expressions within this comma expression are evaluated before the expression i > 1. Regardless of the results of the first two expressions, the third is evaluated and its result determines whether the if statement is processed. 
func( ( ++a, f(a) ) );  A function call to func() in which a is incremented, the resulting value is passed to a function f(), and the return value of f() is passed to func(). The function func() is passed only a single argument because the comma expression is enclosed in parentheses within the function argument list. 
Comma Operator in forLoops
The most common use is to allow multiple assignment statements without using a block statement, primarily in the initialization and the increment expressions of a forloop. This is the only idiomatic use in elementary C programming. In the following example, the order of the loop's initializers is significant:
void rev(char *s, size_t len)
{
char *first;
for (first = s, s += len; s >= first; s) {
putchar(*s);
}
}
Comma Operator in Place of a Semicolon
Comma operator in place of a semicolon.
We know that in C and C++, every statement is terminated with a semicolon but the comma operator also used to terminate the statement after satisfying the following rules.
 The variable declaration statements must be terminated with a semicolon.
 The statements after the declaration statement can be terminated by the comma operator.
 The last statement of the program must be terminated by a semicolon.
Example:
#include <iostream>
using namespace std;
int main()
{
cout << "First Line\n",
cout << "Second Line\n",
cout << "Third Line\n",
cout << "Last line";
return 0;
}
Output
First Line
Second Line
Third Line
Last line
Comma as a Separator
Comma as a Separator
Comma acts as a separator when used with function calls and definitions, functionlike macros, variable declarations, enum declarations, and similar constructs.
// comma as a separator
int a = 1, b = 2;
void fun(x, y);
The use of the comma as a separator should not be confused with the use as an operator. For example, in the below statement, f1() and f2() can be called in any order.
// Comma acts as a separator here and doesn't enforce any sequence.
// Therefore, either f1() or f2() can be called first
void fun(f1(), f2());
See this for C vs C++ differences of using the comma operator. You can try the below programs to check your understanding of comma in C.
// PROGRAM 1
#include <stdio.h>
int main()
{
int x = 10;
int y = 15;
printf("%d", (x, y));
getchar();
return 0;
}
// PROGRAM 2: Thanks to Shekhu for suggesting this program
#include <stdio.h>
int main()
{
int x = 10;
int y = (x++, ++x);
printf("%d", y);
getchar();
return 0;
}
// PROGRAM 3: Thanks to Venki for suggesting this program
#include <stdio.h>
int main()
{
int x = 10, y;
// The following is equivalent
// to y = x + 2 and x += 3,
// with two printings
y = (x++,
printf("x = %d\n", x),
++x,
printf("x = %d\n", x),
x++);
// Note that last expression is evaluated
// but side effect is not updated to y
printf("y = %d\n", y);
printf("x = %d\n", x);
return 0;
}
More Examples for Comma
In this example, the different behavior between the second and third lines is due to the comma operator has lower precedence than the assignment. The last example differs as well since the return expression must be fully evaluated before the function can return.
/** * Commas act as separators in this line, not as an operator. * Results: a=1, b=2, c=3, i=0 */ int a=1, b=2, c=3, i=0; /** * Assigns value of b into i. * Results: a=1, b=2, c=3, i=2 */ int a=1, b=2, c=3; int i = (a, b); /** * Assigns value of a into i. Equivalent to (i = a), b; * Results: a=1, b=2, c=3, i=1 * (The braces on the second line are needed to * avoid a compiler error. The second 'b' declared * is given no initial value.) */ int a=1, b=2, c=3; { int i = a, b; } /** * Increases value of a by 2, then assigns value of resulting operation a+b into i . * Results: a=3, b=2, c=3, i=5 */ int a=1, b=2, c=3; int i = (a += 2, a + b); /** * Increases value of a by 2, then stores value of a to i, and discards unused * values of resulting operation a + b . Equivalent to (i = (a += 2)), a + b; * Results: a=3, b=2, c=3, i=3 */ int a=1, b=2, c=3; int i; i = a += 2, a + b; /** * Assigns value of a into i; the following 'b' and 'c' * are not part of the initializer but declarators for * second instances of those variables. * Results: a=1, b=2, c=3, i=1 * (The braces on the second line are needed to * avoid a compiler error. The second 'b' and second * 'c' declared are given no initial value.) */ int a=1, b=2, c=3; { int i = a, b, c; } /** * Assigns value of c into i, discarding the unused a and b values. * Results: a=1, b=2, c=3, i=3 */ int a=1, b=2, c=3; int i = (a, b, c); /** * Returns 6, not 4, since comma operator sequence points following the keyword * 'return' are considered a single expression evaluating to rvalue of final * subexpression c=6 . */ return a=4, b=5, c=6; /** * Returns 3, not 1, for same reason as previous example. */ return 1, 2, 3; /** * Returns 3, not 1, still for same reason as above. This example works as it does * because return is a keyword, not a function call. Even though compilers will * allow for the construct return(value), the parentheses are only relative to "value" * and have no special effect on the return keyword. * Return simply gets an expression and here the expression is "(1), 2, 3". */ return (1), 2, 3;
MiscellaneousOperators
Miscellaneous Operators
Member Operators
The dot (.) operator and the arrow (>) operator can be used to reference individual members of classes, structures, and unions. The dot operator is applied to the actual object. The arrow operator is used with a pointer to an object.
Operator  Description  Example  Description 

.  member access  a.b  access member b of struct, union, or class a. 
>  member access through a pointer  a>b  access member b of struct, union, or class pointed to by a. 
Scope Resolution Operator (C++)
Scope Resolution Operator
The scope resolution operator :: is used to identify and disambiguate identifiers used in different scopes.
Syntax  Description 

::identifier  
className::identifier  
namespace::identifier  
enum class:identifier  
enum struct::identifier 
The identifier can be a variable, a function, or an enumeration value.
With Classes and Namespace
The following example shows how the scope resolution operator is used with namespaces and classes:
namespace NamespaceA{ int x; class ClassA { public: int x; }; } int main() { // A namespace name used to disambiguate NamespaceA::x = 1; // A class name used to disambiguate NamespaceA::ClassA a1; a1.x = 2; }
A scope resolution operator without a scope qualifier refers to the global namespace.
namespace NamespaceA{ int x; } int x; int main() { int x; // the x in main() x = 0; // The x in the global namespace ::x = 1; // The x in the A namespace NamespaceA::x = 2; }
You can use the scope resolution operator to identify a member of a namespace, or to identify a namespace that nominates the member's namespace in a usingdirective. In the example below, you can use NamespaceC to qualify ClassB, even though ClassB was declared in namespace NamespaceB, because NamespaceB was nominated in NamespaceC by a using directive.
namespace NamespaceB { class ClassB { public: int x; }; } namespace NamespaceC{ using namespace B; } int main() { NamespaceB::ClassB c_b; NamespaceC::ClassB c_c; c_b.x = 3; c_c.x = 4; }
You can use chains of scope resolution operators. In the following example, NamespaceD::NamespaceD1 identifies the nested namespace NamespaceD1, and NamespaceE::ClassE::ClassE1 identifies the nested class ClassE1.
namespace NamespaceD{ namespace NamespaceD1{ int x; } } namespace NamespaceE{ class ClassE{ public: class ClassE1{ public: int x; }; }; } int main() { NamespaceD:: NamespaceD1::x = 6; NamespaceE::ClassE::ClassE1 e1; e1.x = 7 ; }
4.3 Compound Assignment Operators
The compound assignment operators consist of a binary operator and the simple assignment operator. They perform the operation of the binary operator on both operands and store the result of that operation into the left operand, which must be a modifiable lvalue.
The following table lists the compound assignment operators and shows an expression using each operator:
Operator  Meaning  Example  Description 

+=  Add AND assignment operator. It adds the right operand to the left operand and assigns the result to the left operand.  a += exp;  a = a + (exp); 
=  Subtract AND assignment operator. It subtracts the right operand from the left operand and assigns the result to the left operand.  a = exp;  a = a  (exp); 
*=  Multiply AND assignment operator. It multiplies the right operand with the left operand and assigns the result to the left operand.  a *= exp;  a = a * (exp); 
/=  Divide AND assignment operator. It divides the left operand with the right operand and assigns the result to the left operand.  a /= exp;  a = a / (exp); 
%=  Modulus AND assignment operator. It takes modulus using two operands and assigns the result to the left operand.  a %= exp;  a = a % (exp); 
<<=  Left shift AND assignment operator.  a <<= exp;  a = a << (exp); 
>>=  Right shift AND assignment operator.  a >>= exp;  a = a >> (exp); 
&=  Bitwise and assignment operator.  a &= exp;  a = a & (ex); 
=  Bitwise or and assignment operator  a = exp;  a = a  (exp); 
^=  Bitwise xor and assignment operator.  a ^= exp;  a = a ^ (exp); 
Note: the expression
a *= b + c;
is equivalent to
a = a * (b + c);
and not
a = a * b + c;
Example of compound operators
Let's have a look at an example using some of these operators:
#include<iostream> using namespace std; int main() { int a = 3, b = 2; a += b; cout << a << endl; a = b; cout << a << endl; a *= b; cout << a << endl; a /= b; cout << a << endl; return 0; }
This will give the output:
5
3
6
3
4.4 Order of Operations
In mathematics and computer programming, the order of operations (or operator precedence) is a collection of rules that reflect conventions about which procedures to perform first in order to evaluate a given mathematical expression.
The following table is the operator precedence in C/C++
Precedence ^{1}  Operator ^{2}  Description  Example  Associativity ^{3} 

1  ::  Scope resolution  class::mdata class::mfunction() 
Lefttoright 
::  Global scope  ::variable  Righttoleft  
2  ++  
Post increment and post decrement 
v++ v 
Lefttoright 
( )  Grouping (parentheses)  (expr)  Lefttoright  
( )  Function call  function(args)  Lefttoright  
[ ]  Array indexing or subscripting  array[index]  Lefttoright  
.  Member selection by reference  obj.field obj.mfunc() 
Lefttoright  
>  Member selection by pointer  obj>field obj>mfunc() 
Lefttoright  
3  ++  
Pre increment and pre decrement 
++v v 
Righttoleft 
+  
Unary positive and negative 
N (a+b) 
Righttoleft  
! ~ 
Logical NOT and bitwise not 
!a, !(a&&b) ~k 
Righttoleft  
(dataType)  Type cast (convert value to temporary value of type)  (int) var  Righttoleft  
*  indirection (dereference)  *p  Righttoleft  
&  addressof  &var  Righttoleft  
sizeof  Sizeof (Determine size in bytes)  sizeof(int), sizeof(var)  Righttoleft  
new new[] 
Dynamic memory allocation  Person *p = new Person; int *scores = new int[100]; 
Righttoleft  
delete delete[] 
Dynamic memory deallocation  delete p; delete [] score; 
Righttoleft  
4  .* >* 
Pointer to member  Lefttoright  
5  * / % 
Multiplication Division Remainder 
3 * x y / z a % 2 
Lefttoright 
6  +  
Addition Subtraction 
a + b a  b 
Lefttoright 
7  << >> 
Bitwise shift left Bitwise shift right 
x << 2 y >> 2 
Lefttoright 
<< >> 
Overloaded stream I/O  cout << x cin >> y 
Lefttoright  
8  < <= > >= 
For relational operators < and ≦ respectively For relational operators > and ≧ respectively 
x < 10, x <= 10 x > 10, x >= 10 
Lefttoright 
9  == !=  For relational = and ≠ respectively  x == y, x != y  Lefttoright 
10  &  Bitwise and  x & 0x00008000  Lefttoright 
11  ^  Bitwise xor  x ^ 0xFFFF0000  Lefttoright 
12    bitwise or  x  0x00000004  Lefttoright 
13  &&  Logical AND  x && y  Lefttoright 
14    Logical OR  x  y  Lefttoright 
15  ? :  Conditional operator  (x < y)? x : y;  Righttoleft 
=  Direct assignment (provided by default for C++ classes)  x = y + b * sqrt(c)  Righttoleft  
+= =  Assignment by sum and difference  x += 2, x = 2  Righttoleft  
*= /= %=  Assignment by product, quotient, and remainder  x *= 2, x /= 2, x %= 2  Righttoleft  
<<= >>=  Assignment by bitwise left shift and right shift  x <<= 2, x >>= 2  Righttoleft  
&= ^= !=  Assignment by bitwise and, xor, and or  a &= b, a = b, a ^= b  Righttoleft  
16  throw  Throw operator (for exceptions)  throw over_flow  Lefttoright 
17  ,  Comma (separate expressions)  i = 0, j = 0, i++, j++  Lefttoright 
 Highest operator precedence to lowest. Operators in the same group (groups are separated by horizontal lines) have the same precedence.
 Unary operators are in blue, binary operators are in yellow, and the single ternary operator is in green
 Associativity is only used when there are two or more operators of the same precedence.
 Lefttoright: evaluates left to right. e.g. in a + b + c, a + b is evaluated first.
 Righttoleft: evaluates right to left. eg. in a = b = c, b = c is evaluated first.
Precedence of Function Calls
Consider the following program, associativity of the + operator is lefttoright, but it does not mean f1() is always called before f2(). The output of the following program is infact compiler dependent.
#include <stdio.h> int x = 0; int f1() { x = 5; return x; } int f2() { x = 10; return x; } int main() { int p = f1() + f2(); printf("p = %d, x = %d \n\n", p, x); return 0; }
Carefully to Use Comma
Comma has the least precedence among all operators and should be used carefully.
For example, consider the following program, the output is 1. See this and this for more details.
#include int main() { int a; a = 1, 2, 3; // Evaluated as (a = 1), 2, 3 printf("%d", a); return 0; }
Precedence of Logical and Increment Operators
x && y++
 The second operand, y++, is evaluated only if x is true (nonzero).
 Thus, y is not incremented if x is flase (zero).
#include <stdio.h> #include <iostream> using namespace std; int main() { int x = 0; int y = 10; if (x && y++) { cout << "Condition result is true." << endl; } else { cout << "Condition result is false." << endl; } cout << "y = " << y << endl; return 0; }
4.5 Multiple Unsequenced Modifications to the Same Variable
The modification of a variable is unsequenced relative to another operation on the same variable. This may lead to undefined behavior.
The analyzer detected an expression leading to undefined behavior. A variable is used several times between two sequence points while its value is changing. We cannot predict the result of such an expression. Let's consider the notions "undefined behavior" and "sequence point" in detail.
Undefined behavior is a feature of some programming languages — most famously C/C++. In these languages, to simplify the specification and allow some flexibility in implementation, the specification leaves the results of certain operations specifically undefined.
For example, in C the use of any automatic variable before it has been initialized yields undefined behavior, as do division by zero and indexing an array outside of its defined bounds. This specifically frees the compiler to do whatever is easiest or most efficient, should such a program be submitted. In general, any behavior afterward is also undefined. In particular, it is never required that the compiler diagnose undefined behavior — therefore, programs invoking undefined behavior may appear to compile and even run without errors at first, only to fail on another system, or even on another date. When an instance of undefined behavior occurs, so far as the language specification is concerned anything could happen, maybe nothing at all.
A sequence point in imperative programming defines any point in a computer program's execution at which it is guaranteed that all side effects of previous evaluations will have been performed, and no side effects from subsequent evaluations have yet been performed. They are often mentioned in reference to C and C++ because the result of some expressions can depend on the order of evaluation of their subexpressions. Adding one or more sequence points is one method of ensuring a consistent result because this restricts the possible orders of evaluation.
It is worth noting that in C++11, the terms sequenced before/after, sequenced and unsequenced were introduced instead of sequence points. Many expressions, resulting in undefined behavior in C++03, became defined (for instance, i = ++i). These rules were also supplemented in C++14 and C++17. The analyzer issues a false positive regardless of the used standard. The certainty of the expressions of i = ++i type is not an excuse to use them. It is better to rewrite such expressions to make them more understandable. Also if you need to support an earlier standard, you can get a bug that is hardly debugged.
i = ++i + 2; // undefined behavior until C++11
i = i++ + 2; // undefined behavior until C++17
f(i = 2, i = 2); // undefined behavior until C++17
f(++i, ++i); // undefined behavior until C++17,
// unspecified after C++17
i = ++i + i++; // undefined behavior
cout << i << i++; // undefined behavior until C++17
a[i] = i++; // undefined behavior until C++17
n = ++i + i; // undefined behavior
Sequence points come into play when the same variable is modified more than once within a single expression. An oftencited example is an expression i = i++, which both assigns i to itself and increments i. The final value of i is ambiguous, because, depending on the language semantics, the increment may occur before, after, or interleaved with the assignment. The definition of a particular language might specify one of the possible behaviors or simply say the behavior is undefined. In C and C++, evaluating such an expression yields undefined behavior.
C and C++ define the following sequence points:
 Between evaluation of the left and right operands of the && (logical AND),  (logical OR), and comma operators (,). For example, in the expression *p++ != 0 && *q++ != 0;, all side effects of the subexpression *p++ != 0 are completed before any attempt to access q.
 Between the evaluation of the first operand of the ternary "questionmark" operator and the second or third operand. For example, in the expression a = (*p++) ? (*p++) : 0; there is a sequence point after the first *p++, meaning it has already been incremented by the time the second instance is executed.
 At the end of a full expression. This category includes expression statements (such as the assignment a = b;), return statements, the controlling expressions of if, switch, while, or dowhile statements, and all three expressions in a for statement.
 Before a function is entered in a function call. The order in which the arguments are evaluated is not specified, but this sequence point means that all of their side effects are complete before the function is entered. In the expression f(i++) + g(j++) + h(k++), f is called with a parameter of the original value of i, but i is incremented before entering the body of f. Similarly, j and k are updated before entering g and h respectively. However, it is not specified in which order f(), g(), h() are executed, nor in which order i, j, k are incremented. The values of j and k in the body of f are therefore undefined. Note that a function call f(a,b,c) is not a use of the comma operator and the order of evaluation for a, b, and c is unspecified.
 At a function return, after the return value is copied into the calling context. (This sequence point is only specified in the C++ standard; it is present only implicitly in C[4].)
 At the end of an initializer; for example, after the evaluation of 5 in the declaration int a = 5;.
 In C++, overloaded operators act as functions, so a call of an overloaded operator is a sequence point.
Now let's consider several samples causing undefined behavior:
int i, j;
...
X[i] = ++i;
X[i++] = i;
j = i + X[++i];
i = 6 + i++ + 2000;
j = i++ + ++i;
i = ++i + ++i;
We cannot predict the calculation results in all these cases. Of course, these samples are artificial and we can notice the danger right away. So let's examine a code sample taken from a real application:
while (!(m_pBitArray[m_nCurrentBitIndex >> 5] &
Powers_of_Two_Reversed[m_nCurrentBitIndex++ & 31])) {
}
return (m_nCurrentBitIndex  BitInitial  1);
The compiler can calculate either of the left or right arguments of the '&' operator first. It means that the m_nCurrentBitIndex variable might be already incremented by one when calculating "m_pBitArray[m_nCurrentBitIndex >> 5]". Or it might still be not incremented.
This code may work well for a long time. However, you should keep in mind that it will behave correctly only when it is built in some particular compiler version with a fixed set of compilation options. This is the correct code:
while (!(m_pBitArray[m_nCurrentBitIndex >> 5] &
Powers_of_Two_Reversed[m_nCurrentBitIndex & 31])) { ++m_nCurrentBitIndex; }
return (m_nCurrentBitIndex  BitInitial);
This code does not contain ambiguities anymore. We also got rid of the magic constant "1".
Programmers often think that undefined behavior may occur only when using postincrement, while preincrement is safe. It's not so. Further is an example from a discussion on this subject.
[Warning] multiple unsequenced modifications
Question
When you compile the following code in C++Builder 10.3, you will get a warning message: [bcc32c Warning] multiple unsequenced modifications to 'a'. The variable a is modified while being used twice between sequence points.
The code:
a = (++a) % 2;
It seems to me that there is no undefined behavior because the a variable does not participate in the expression twice.
Answer
There is undefined behavior here. It's another thing that the probability of error occurrence is rather small in this case. The '=' operator is not a sequence point. It means that the compiler might first put the value of the a variable into the register and then increment the value in the register. After that, it calculates the expression and writes the result into the a variable, and then again writes a register with the incremented value into the same variable. As a result, we will get a code like this:
In assembly code:
MOV R1, a
LOAD R2, [R1]
ADD R2, #1
MOD R3, R1, #2
SAVE R3, [R1]
SAVE R1, [R1]
Pseudocode:
REG = a;
REG++;
a = (REG) % 2;
a = REG;
The compiler has the absolute right to do so. Of course, in practice, it will most likely increment the variable's value at once, and everything will be calculated as the programmer expects. But you should not rely on that.
Consider one more situation with function calls.
The order of calculating function arguments is not defined. If a variable change over time serves as an argument, the result will be unpredictable. This is unspecified behavior. Consider this sample:
int a = 0;
foo(a = 2, a);
The 'Foo' function may be called both with the arguments (2, 0) and with the arguments (2, 2). The order in which the function arguments will be calculated depends on the compiler and optimization settings.
References:
 PVSStudio Documentation. V567.
 Wikipedia. Undefined behavior.
 Wikipedia. Sequence point.
 Klaus Kreft & Angelika Langer. Sequence Points and Expression Evaluation in C++.
 Discussion at Bytes.com. Sequence points.
 Discussion at StackOverflow.com. Why is a = (a+b)  (b=a) a bad choice for swapping two integers?
 cppreference.com. Order of evaluation.
4.6 Bitmask
In computer science, a mask or bitmask is data that is used for bitwise operations, particularly in a bit field. Using a mask, multiple bits in a byte, nibble, word, etc. can be set either on, off, or inverted from on to off (or vice versa) in a single bitwise operation.
Masking bit to 1
To turn certain bits on, the bitwiseor () operation can be used, following the principle that n or 1 = 1 and n or 0 = n. Therefore, to make sure a bit is on, bitwiseor can be used with a 1. To leave a bit unchanged, bitwiseor is used with a 0.
Example: Masking the higher nibble (bits 4, 5, 6, 7) to 1, and the lower nibble (bits 0, 1, 2, 3) unchanged.
1001 0101 = 0x95
or 1111 0000 = 0xF0 (mask 1 value)
––––––––––––
1111 0101 = 0xF5
1010 0011 = 0xA3
or 1111 0000 = 0xF0 (mask 1 value)
––––––––––––
1111 0011 = 0xF3
0001 0001 = 0x11
or 1111 0000 = 0xF0 (mask 1 value)
––––––––––––
1111 0001 = 0xF1
Masking bit to 0
More often in practice bits are "masked off" (or masked to 0) than "masked on" (or masked to 1). When a bit is ANDed with a 0, the result is always 0, i.e. n and 0 = 0. To leave the other bits as they were original, they can be ANDed with 1, since n and 1 = n.
Example: Masking the higher nibble (bits 4, 5, 6, 7) to 0, and the lower nibble (bits 0, 1, 2, 3) unchanged.
1001 0101 = 0x95
and 0000 1111 = 0x0F (mask 0 value)
––––––––––––
0000 0101 = 0x05
1010 0011 = 0xA3
and 0000 1111 = 0x0F (mask 0 value)
––––––––––––
0000 0011 = 0x03
0001 0001 = 0x11
and 0000 1111 = 0x0F (mask 0 value)
––––––––––––
0000 0001 = 0x01
Toggling bit values
So far we have covered how to turn bits on and turn bits off, but not both at once. Sometimes it does not really matter what the value is, but it must be made the opposite of what it currently is. This can be achieved using the bitwisexor ( ^, exclusive or) operator. The xor returns 0 if the two corresponding bits are the same value, and the result will be a 1 if only the bits are different values. Therefore inversion of the values of bits can be done by XORing them with a 1. Since $n\;xor\;0 = n$, and $n\;xor\;1 = \overline n $.
Example: Toggling bit values
1001 0101 = 0x95
xor 0000 1111 = 0x0F (mask 1 value)
––––––––––––
1001 1010 = 0x9A
1010 0011 = 0xA3
xor 1111 1111 = 0xFF (mask 1 value)
––––––––––––
0101 1100 = 0x5C
0001 0001 = 0x11
xor 1100 0011 = 0x0F (mask 1 value)
––––––––––––
1101 0010 = 0xD2
Querying the status of bits values
It is possible to use bitmasks to easily check the state of individual bits regardless of the other bits. To do this, turning off all the other bits using the bitwiseand is done as discussed above and the value is compared with 1. If it is equal to 0, then the bit was off, but if the value is any other value, then the bit was on. What makes this convenient is that it is not necessary to figure out what the value actually is, just that it is not 0.
Example: Querying the status of the 4th bit
1001 1101
and 0001 0000 = 0x10 (mask 1 value)

0001 0000
if (a & 0x10)
cout << "The bit 4 is one" << endl;
else
cout << "The bit 4 is zero" << endl;
Example: Querying the status of the higher nibble (bit 4, 5, 6, 7)
1001 1101
and 1111 0000 =0xF0 (mask 1 Value)

1001 1101
if ( (a & 0xF0) >> 4) == 0x09)
cout << "The higher nibble are 0x09" << endl;
else
cout << "The higher nibble are not 0x09" << endl;
Questions
Write a console application for each question.
 Calculate the Area of a Triangle: Input the width of the base and the height, then display the area on the screen.
 Mileage to Kilometers: Input a value in miles and convert it to kilometers. (1 mile is equal to 1.60934 kilometers)