C++之常量与运算符

Constants

Constants are expressions with a fixed value.

Literals

Literals are the most obvious kind of constants. They are used to express particular values within the source code of a program. We have already used some in previous chapters to give specific values to variables or to express messages we wanted our programs to print out, for example, when we wrote:

a = 5;

The 5 in this piece of code was a literal constant.

Literal constants can be classified into: integer, floating-point, characters, strings, Boolean, pointers, and user-defined literals.

Integer Numerals

1776
707
-273

These are numerical constants that identify integer values. Notice that they are not enclosed in quotes or any other special character; they are a simple succession of digits representing a whole number in decimal base; for example, 1776 always represents the value one thousand seven hundred seventy-six.

In addition to decimal numbers (those that most of us use every day), C++ allows the use of octal numbers (base 8) and hexadecimal numbers (base 16) as literal constants. For octal literals, the digits are preceded with a 0 (zero) character. And for hexadecimal, they are preceded by the characters 0x (zero, x). For example, the following literal constants are all equivalent to each other:

75         // decimal
0113       // octal
0x4b       // hexadecimal  

All of these represent the same number: 75 (seventy-five) expressed as a base-10 numeral, octal numeral and hexadecimal numeral, respectively.

These literal constants have a type, just like variables. By default, integer literals are of type int. However, certain suffixes may be appended to an integer literal to specify a different integer type:

Suffix Type modifier
u or U unsigned
l or L long
ll or LL long long

Unsigned may be combined with any of the other two in any order to form unsigned long or unsigned long long.

For example:

75         // int
75u        // unsigned int
75l        // long
75ul       // unsigned long 
75lu       // unsigned long 

In all the cases above, the suffix can be specified using either upper or lowercase letters.

Floating Point Numerals

They express real values, with decimals and/or exponents. They can include either a decimal point, an e character (that expresses "by ten at the Xth height", where X is an integer value that follows the e character), or both a decimal point and an e character:

3.14159    // 3.14159
6.02e23    // 6.02 x 10^23
1.6e-19    // 1.6 x 10^-19
3.0        // 3.0  

These are four valid numbers with decimals expressed in C++. The first number is PI, the second one is the number of Avogadro, the third is the electric charge of an electron (an extremely small number) -all of them approximated-, and the last one is the number three expressed as a floating-point numeric literal.

The default type for floating-point literals is double. Floating-point literals of type float or long double can be specified by adding one of the following suffixes:

Suffix Type
f or F float
l or L long double

For example:

3.14159L   // long double
6.02e23f   // float  

Any of the letters that can be part of a floating-point numerical constant (e, f, l) can be written using either lower or uppercase letters with no difference in meaning.

Character and string literals

Character and string literals are enclosed in quotes:

'z'
'p'
"Hello world"
"How do you do?"

The first two expressions represent single-character literals, and the following two represent string literals composed of several characters. Notice that to represent a single character, we enclose it between single quotes ('), and to express a string (which generally consists of more than one character), we enclose the characters between double quotes (").

Both single-character and string literals require quotation marks surrounding them to distinguish them from possible variable identifiers or reserved keywords. Notice the difference between these two expressions:

x
'x'

Here, x alone would refer to an identifier, such as the name of a variable or a compound type, whereas 'x' (enclosed within single quotation marks) would refer to the character literal 'x' (the character that represents a lowercase x letter).

Character and string literals can also represent special characters that are difficult or impossible to express otherwise in the source code of a program, like newline ( ) or tab ( ). These special characters are all of them preceded by a backslash character ().

Here you have a list of the single character escape codes:

Escape code Description
newline
carriage return
tab
v vertical tab
 backspace
f form feed (page feed)
a alert (beep)
' single quote (')
" double quote (")
? question mark (?)
backslash ()

For example:

'
'
'	'
"Left 	 Right"
"one
two
three"

Internally, computers represent characters as numerical codes: most typically, they use one extension of the ASCII character encoding system (see ASCII code for more info). Characters can also be represented in literals using its numerical code by writing a backslash character () followed by the code expressed as an octal (base-8) or hexadecimal (base-16) number. For an octal value, the backslash is followed directly by the digits; while for hexadecimal, an x character is inserted between the backslash and the hexadecimal digits themselves (for example: x20 or x4A).

Several string literals can be concatenated to form a single string literal simply by separating them by one or more blank spaces, including tabs, newlines, and other valid blank characters. For example:

"this forms" "a single"     " string "
"of characters"

The above is a string literal equivalent to:

"this formsa single string of characters"

Note how spaces within the quotes are part of the literal, while those outside them are not.

Some programmers also use a trick to include long string literals in multiple lines: In C++, a backslash () at the end of line is considered a line-continuation character that merges both that line and the next into a single line. Therefore the following code:

x = "string expressed in 
two lines"

is equivalent to:

x = "string expressed in two lines"

All the character literals and string literals described above are made of characters of type char. A different character type can be specified by using one of the following prefixes:

Prefix Character type
u char16_t
U char32_t
L wchar_t

Note that, unlike type suffixes for integer literals, these prefixes are case sensitive: lowercase for char16_t and uppercase for char32_t and wchar_t.

For string literals, apart from the above u, U, and L, two additional prefixes exist:

Prefix Description
u8 The string literal is encoded in the executable using UTF-8
R The string literal is a raw string

In raw strings, backslashes and single and double quotes are all valid characters; the content of the literal is delimited by an initial R"sequence( and a final )sequence", where sequence is any sequence of characters (including an empty sequence). The content of the string is what lies inside the parenthesis, ignoring the delimiting sequence itself. For example:

R"(string with ackslash)"
R"&%$(string with ackslash)&%$"

Both strings above are equivalent to "string with ackslash". The R prefix can be combined with any other prefixes, such as u, L or u8.

Other literals

Three keyword literals exist in C++: true, false and nullptr:
true and false are the two possible values for variables of type bool.
nullptr is the null pointer value.

bool foo = true;
bool bar = false;
int* p = nullptr;

Typed constant expressions

Sometimes, it is just convenient to give a name to a constant value:

double pi = 3.1415926;
const char tab = '	';

We can then use these names instead of the literals they were defined to:

#include <iostream>
using namespace std;

const double pi = 3.14159;
const char newline = '
';

int main ()
{
  double r=5.0;               // radius
  double circle;

  circle = 2 * pi * r;
  cout << circle;
  cout << newline;
}
// output 31.4159

Preprocessor definitions (#define)

Another mechanism to name constant values is the use of preprocessor definitions. They have the following form:

define identifier replacement

After this directive, any occurrence of identifier in the code is interpreted as replacement, where replacement is any sequence of characters (until the end of the line). This replacement is performed by the preprocessor, and happens before the program is compiled, thus causing a sort of blind replacement: the validity of the types or syntax involved is not checked in any way.

For example:

#include <iostream>
using namespace std;

#define PI 3.14159
#define NEWLINE '
'

int main ()
{
  double r=5.0;               // radius
  double circle;

  circle = 2 * PI * r;
  cout << circle;
  cout << NEWLINE;

}
// output 31.4159
  • Note that the #define lines are preprocessor directives, and as such are single-line instructions that -unlike C++ statements- do not require semicolons(;)at the end; the directive extends automatically until the end of the line. If a semicolon is included in the line, it is part of the replacement sequence and is also included in all replaced occurrences.

Operators

Once introduced to variables and constants, we can begin to operate with them by using operators. What follows is a complete list of operators. At this point, it is likely not necessary to know all of them, but they are all listed here to also serve as reference.

Assignment operator (=)

The assignment operator assigns a value to a variable.

x = 5;

This statement assigns the integer value 5 to the variable x. The assignment operation always takes place from right to left, and never the other way around:

x = y;

This statement assigns to variable x the value contained in variable y. The value of x at the moment this statement is executed is lost and replaced by the value of y.

Consider also that we are only assigning the value of y to x at the moment of the assignment operation. Therefore, if y changes at a later moment, it will not affect the new value taken by x.

For example, let's have a look at the following code - I have included the evolution of the content stored in the variables as comments:

// assignment operator
#include <iostream>
using namespace std;

int main ()
{
  int a, b;         // a:?,  b:?
  a = 10;           // a:10, b:?
  b = 4;            // a:10, b:4
  a = b;            // a:4,  b:4
  b = 7;            // a:4,  b:7

  cout << "a:";
  cout << a;
  cout << " b:";
  cout << b;
}

This program prints on screen the final values of a and b (4 and 7, respectively). Notice how a was not affected by the final modification of b, even though we declared a = b earlier.

Assignment operations are expressions that can be evaluated. That means that the assignment itself has a value, and -for fundamental types- this value is the one assigned in the operation. For example:

y = 2 + (x = 5);

In this expression, y is assigned the result of adding 2 and the value of another assignment expression (which has itself a value of 5). It is roughly equivalent to:

x = 5;
y = 2 + x;

With the final result of assigning 7 to y.

The following expression is also valid in C++:

x = y = z = 5;

It assigns 5 to the all three variables: x, y and z; always from right-to-left.

Arithmetic operators ( +, -, *, /, % )

The five arithmetical operations supported by C++ are:

operator description
+ addition
- subtraction
* multiplication
/ division
% modulo

Operations of addition, subtraction, multiplication and division correspond literally to their respective mathematical operators. The last one, modulo operator, represented by a percentage sign (%), gives the remainder of a division of two values. For example:

x = 11 % 3;

results in variable x containing the value 2, since dividing 11 by 3 results in 3, with a remainder of 2.

Compound assignment (+=, -=, *=, /=, %=, >>=, <<=, &=, ^=, |=)

Compound assignment operators modify the current value of a variable by performing an operation on it. They are equivalent to assigning the result of an operation to the first operand:

expression equivalent to...
y += x; y = y + x;
x -= 5; x = x - 5;
x /= y; x = x / y;
price *= units + 1; price = price * (units+1);

and the same for all other compound assignment operators. For example:

// compound assignment operators
#include <iostream>
using namespace std;

int main ()
{
  int a, b=3;
  a = b;
  a+=2;             // equivalent to a=a+2
  cout << a;
}
// output 5

Increment and decrement (++, --)

Some expression can be shortened even more: the increase operator (++) and the decrease operator (--) increase or reduce by one the value stored in a variable. They are equivalent to +=1 and to -=1, respectively. Thus:

++x;
x+=1;
x=x+1;

are all equivalent in its functionality; the three of them increase by one the value of x.

In the early C compilers, the three previous expressions may have produced different executable code depending on which one was used. Nowadays, this type of code optimization is generally performed automatically by the compiler, thus the three expressions should produce exactly the same executable code.

A peculiarity of this operator is that it can be used both as a prefix and as a suffix. That means that it can be written either before the variable name (++x) or after it (x++). Although in simple expressions like x++ or ++x, both have exactly the same meaning; in other expressions in which the result of the increment or decrement operation is evaluated, they may have an important difference in their meaning: In the case that the increase operator is used as a prefix (++x) of the value, the expression evaluates to the final value of x, once it is already increased. On the other hand, in case that it is used as a suffix (x++), the value is also increased, but the expression evaluates to the value that x had before being increased. Notice the difference:

Example 1Example 2
x = 3;
y = ++x;
// x contains 4, y contains 4
x = 3;
y = x++;
// x contains 4, y contains 3

In Example 1, the value assigned to y is the value of x after being increased. While in Example 2, it is the value x had before being increased.

Relational and comparison operators ( ==, !=, >, <, >=, <= )

Two expressions can be compared using relational and equality operators. For example, to know if two values are equal or if one is greater than the other.

The result of such an operation is either true or false (i.e., a Boolean value).

The relational operators in C++ are:

operator description
== Equal to
!= Not equal to
< Less than
> Greater than
<= Less than or equal to
>= Greater than or equal to

Here there are some examples:

(7 == 5)     // evaluates to false
(5 > 4)      // evaluates to true
(3 != 2)     // evaluates to true
(6 >= 6)     // evaluates to true
(5 < 5)      // evaluates to false 

Of course, it's not just numeric constants that can be compared, but just any value, including, of course, variables. Suppose that a=2, b=3 and c=6, then:

(a == 5)     // evaluates to false, since a is not equal to 5
(a*b >= c)   // evaluates to true, since (2*3 >= 6) is true
(b+4 > a*c)  // evaluates to false, since (3+4 > 2*6) is false
((b=2) == a) // evaluates to true 

Be careful! The assignment operator (operator =, with one equal sign) is not the same as the equality comparison operator (operator ==, with two equal signs); the first one (=) assigns the value on the right-hand to the variable on its left, while the other (==) compares whether the values on both sides of the operator are equal. Therefore, in the last expression ((b=2) == a), we first assigned the value 2 to b and then we compared it to a (that also stores the value 2), yielding true.

Logical operators ( !, &&, || )

The operator ! is the C++ operator for the Boolean operation NOT. It has only one operand, to its right, and inverts it, producing false if its operand is true, and true if its operand is false. Basically, it returns the opposite Boolean value of evaluating its operand. For example:

!(5 == 5)   // evaluates to false because the expression at its right (5 == 5) is true
!(6 <= 4)   // evaluates to true because (6 <= 4) would be false
!true       // evaluates to false
!false      // evaluates to true 

The logical operators && and || are used when evaluating two expressions to obtain a single relational result. The operator && corresponds to the Boolean logical operation AND, which yields true if both its operands are true, and false otherwise. The following panel shows the result of operator && evaluating the expression a&&b:

&& OPERATOR (and)
aba && b
truetruetrue
truefalsefalse
falsetruefalse
falsefalsefalse

The operator || corresponds to the Boolean logical operation OR, which yields true if either of its operands is true, thus being false only when both operands are false. Here are the possible results of a||b:

|| OPERATOR (or)
aba || b
truetruetrue
truefalsetrue
falsetruetrue
falsefalsefalse

For example:

( (5 == 5) && (3 > 6) )  // evaluates to false ( true && false )
( (5 == 5) || (3 > 6) )  // evaluates to true ( true || false ) 

When using the logical operators, C++ only evaluates what is necessary from left to right to come up with the combined relational result, ignoring the rest. Therefore, in the last example ((5==5)||(3>6)), C++ evaluates first whether 5==5 is true, and if so, it never checks whether 3>6 is true or not. This is known as short-circuit evaluation, and works like this for these operators:

When using the logical operators, C++ only evaluates what is necessary from left to right to come up with the combined relational result, ignoring the rest. Therefore, in the last example ((5==5)||(3>6)), C++ evaluates first whether 5==5 is true, and if so, it never checks whether 3>6 is true or not. This is known as short-circuit evaluation, and works like this for these operators:

operator short-circuit
&& if the left-hand side expression is false, the combined result is false (the right-hand side expression is never evaluated).
|| if the left-hand side expression is true, the combined result is true (the right-hand side expression is never evaluated).

This is mostly important when the right-hand expression has side effects, such as altering values:

if ( (i<10) && (++i<n) ) { /*...*/ }   // note that the condition increments i 

Here, the combined conditional expression would increase i by one, but only if the condition on the left of && is true, because otherwise, the condition on the right-hand side (++i<n) is never evaluated.

Conditional ternary operator ( ? )

The conditional operator evaluates an expression, returning one value if that expression evaluates to true, and a different one if the expression evaluates as false. Its syntax is:

condition ? result1 : result2

If condition is true, the entire expression evaluates to result1, and otherwise to result2.

7==5 ? 4 : 3     // evaluates to 3, since 7 is not equal to 5.
7==5+2 ? 4 : 3   // evaluates to 4, since 7 is equal to 5+2.
5>3 ? a : b      // evaluates to the value of a, since 5 is greater than 3.
a>b ? a : b      // evaluates to whichever is greater, a or b.  

For example:

// conditional operator
#include <iostream>
using namespace std;

int main ()
{
  int a,b,c;

  a=2;
  b=7;
  c = (a>b) ? a : b;

  cout << c << '
';
}
// output 7

In this example, a was 2, and b was 7, so the expression being evaluated (a>b) was not true, thus the first value specified after the question mark was discarded in favor of the second value (the one after the colon) which was b (with a value of 7).

Comma operator ( , )

The comma operator (,) is used to separate two or more expressions that are included where only one expression is expected. When the set of expressions has to be evaluated for a value, only the right-most expression is considered.

For example, the following code:

a = (b=3, b+2);

would first assign the value 3 to b, and then assign b+2 to variable a. So, at the end, variable a would contain the value 5 while variable b would contain value 3.

Bitwise operators ( &, |, ^, ~, <<, >> )

Bitwise operators modify variables considering the bit patterns that represent the values they store.

operator asm equivalent description
& AND Bitwise AND
| OR Bitwise inclusive OR
^ XOR Bitwise exclusive OR
~ NOT Unary complement (bit inversion)
<< SHL Shift bits left
>> SHR Shift

Explicit type casting operator

Type casting operators allow to convert a value of a given type to another type. There are several ways to do this in C++. The simplest one, which has been inherited from the C language, is to precede the expression to be converted by the new type enclosed between parentheses (()):

int i;
float f = 3.14;
i = (int) f;

The previous code converts the floating-point number 3.14 to an integer value (3); the remainder is lost. Here, the typecasting operator was (int). Another way to do the same thing in C++ is to use the functional notation preceding the expression to be converted by the type and enclosing the expression between parentheses:

i = int (f);

Both ways of casting types are valid in C++.

sizeof

This operator accepts one parameter, which can be either a type or a variable, and returns the size in bytes of that type or object:

x = sizeof (char);

Here, x is assigned the value 1, because char is a type with a size of one byte.

The value returned by sizeof is a compile-time constant, so it is always determined before program execution.

Other operators

Later in these tutorials, we will see a few more operators, like the ones referring to pointers or the specifics for object-oriented programming.

Precedence of operators

A single expression may have multiple operators. For example:

x = 5 + 7 % 2;

In C++, the above expression always assigns 6 to variable x, because the % operator has a higher precedence than the + operator, and is always evaluated before. Parts of the expressions can be enclosed in parenthesis to override this precedence order, or to make explicitly clear the intended effect. Notice the difference:

x = 5 + (7 % 2);    // x = 6 (same as without parenthesis)
x = (5 + 7) % 2;    // x = 0 

From greatest to smallest priority, C++ operators are evaluated in the following order:

LevelPrecedence groupOperatorDescriptionGrouping
1Scope::scope qualifierLeft-to-right
2Postfix (unary)++ --postfix increment / decrementLeft-to-right
()functional forms
[]subscript
. ->member access
3Prefix (unary)++ --prefix increment / decrementRight-to-left
~ !bitwise NOT / logical NOT
+ -unary prefix
& *reference / dereference
new deleteallocation / deallocation
sizeofparameter pack
(type)C-style type-casting
4Pointer-to-member.* ->*access pointerLeft-to-right
5Arithmetic: scaling* / %multiply, divide, moduloLeft-to-right
6Arithmetic: addition+ -addition, subtractionLeft-to-right
7Bitwise shift<< >>shift left, shift rightLeft-to-right
8Relational< > <= >=comparison operatorsLeft-to-right
9Equality== !=equality / inequalityLeft-to-right
10And&bitwise ANDLeft-to-right
11Exclusive or^bitwise XORLeft-to-right
12Inclusive or|bitwise ORLeft-to-right
13Conjunction&&logical ANDLeft-to-right
14Disjunction||logical ORLeft-to-right
15Assignment-level expressions= *= /= %= += -=
>>= <<= &= ^= |=
assignment / compound assignmentRight-to-left
?:conditional operator
16Sequencing,comma separatorLeft-to-right

When an expression has two operators with the same precedence level, grouping determines which one is evaluated first: either left-to-right or right-to-left.

Enclosing all sub-statements in parentheses (even those unnecessary because of their precedence) improves code readability.

References

[1] Constants
[2] Operators

  • 变更记录
时间 地点 修改人 备注
2020-09-06 佛山 PatrickLee 首发
原文地址:https://www.cnblogs.com/leaguecn/p/13622526.html