C ++ from scratch (4)
-- Value assignment operator
This article is a continuation of "C ++ from scratch (ii)" and describes the content of expressions left over from "C ++ from scratch (ii, it also paves the way for the use of pointers in the next article. Although the previous article has already explained what the variable is, the most critical things of the variable are not described Due to space limitations. The following describes how to access the memory.
Assignment Statement
As mentioned above, to access the memory, the corresponding address is required to indicate which memory to access, and the variable is a ing, so the variable name is equivalent to an address. For memory operations, generally, only the values in the memory are read and the values are written into the memory (the memory allocation and release are not considered). In C ++, to write a value to the memory identified by the address corresponding to a variable (for convenience, the address corresponding to variable A is called the address of variable a later, directly called the address of variable A, the memory identified by the address is variable A. You only need to write the variable name, followed by "=", and then the number to be written (about the number, see C ++ from scratch (2) and semicolons. As follows:
A = 10.0f; B = 34;
Because the number is connected, you can connect to the expression and generate the Code required to calculate the corresponding expression by the compiler, which can also be as follows:
C = A/B * 120.4f;
The compiler will generate a CPU command for division and multiplication calculation. After the calculation is complete (that is, after obtaining the value of expression a/B * 120.4f ), the CPU command that puts the calculation result into variable C is also generated at the same time, which is the basic function of the statement (For the statement, in C ++ from scratch (6) ).
When writing the value assignment statement above, make sure that the variables used have been defined before this statement, so that the compiler can find the address of the corresponding variable when generating the CPU command for value assignment, then, the CPU command is generated. For example, A and B, you need to write a variable definition similar to the following before writing the above statement:
Float a; long B;
Writing the variable name directly is also a statement, which causes the compiler to generate a statement to read the content of the corresponding variable. You can write as follows:
A;
The above will generate a statement to read the memory, even if the number read from the memory is not applied (of course, if the compiler has enabled the optimization option, the above statement will not generate any code ). From this point and the above C = A/B * 120.4f; statement, we can see that the variable can return numbers. The number returned by the variable is the number obtained by interpreting the content of the variable corresponding to the memory according to the variable type. This sentence may not be so easy to understand. It should be understandable after reading the type conversion section below.
Therefore, in order to write data into a piece of memory, use the value assignment statement (equal sign); read a piece of memory and write the variable name that identifies the memory. So we can write it like this: a = a + 3;
Assuming that the original value of A is 1, the above value assignment statement will take the value of A and add 3. Expected result 4 is displayed, and 4 is written to. Because c ++ uses "=" to represent the value assignment statement, it is easy to confuse people with equal signs in mathematics. This should be noted.
In the above float a statement, what is the value of a when no value assignment is performed on the variable? God knows. What is the content of a at that time (for VC compiler, when debugging options are enabled, the non-initialized memory will be filled with 0xcccccccccc ), it is interpreted in IEEE real * 4 format and a corresponding number is obtained, that is, the value of. Therefore, values should be assigned when the variable is defined (but there will be performance impact, but it is very small) to initialize the variable to prevent inexplicable values, such as: float a = 0.0f ;.
Value assignment operator
The above a = a + 3; Means to increase the value of a by 3. In C ++, a short solution is provided for this situation, that is, the preceding statement can be written as a + = 3 ;. Note that these two statements logically increase the value of variable A by 3, but they are actually different. The latter can be compiled into optimized code, because it means to increase the value of a piece of memory by a certain amount, and the former is to write a number into a piece of memory. If possible, try to use the latter, that is, a + = 3 ;. This statement can optimize the compiler (but the current compiler is very intelligent, it can be found that a = a + 3; it is a value-added operation on a piece of memory, rather than a value assignment operation on a piece of memory. Therefore, the above two statements can be considered completely the same and only have a short function ).
In the above case, it can also be applied to binary non-logical operators such as subtraction and multiplication (not logical value operators, that is, not a & = 3;), such: A * = 3; A-= 4; A | = 34; A >>= 3; and so on.
In addition to the short form above, C ++ also provides a short form, that is, a ++; which is logically equivalent to a + = 1 ;. Same as above, in computer programming, adding one and subtracting one are often used. Therefore, the CPU provides two commands specifically for adding one and subtracting one (converted to an assembly language INC and DEC ), however, the speed is much faster than simply using addition or subtraction commands. Therefore, C ++ provides the "++" and "-" operators to correspond to INC and Dec. Therefore, the logic of a ++ is equivalent to that of a = a + 1. Actually, the compiler may make different optimizations, it is possible that a = a + 1; can be compiled into the INC command, and even if a ++ is not used, the code can still be optimized, so that a ++; only the meaning of shorthand is left.
Note that a = 3; this statement also returns a number, that is, the value of a after a is assigned. Since it can return numbers, as stated in "C ++ from scratch (2)", "=" is an operator and can be written as follows:
C = 4 + (A = 3 );
The reason for the parentheses is that "=" has a lower priority than "+", while the more common and normal applications are: c = A = 3;
It should be noted that C and A are not assigned 3, but a is assigned to C after a is assigned 3, although the final result is the same as that of C and A, it should not be understood as follows. Because a ++; indicates a + = 1; that is, a = a + 1;, a ++; returns a number. For this reason, C ++ provides another shorthand method, ++ ;.
Assume that A is 1, then a ++; first returns the value of A, 1, and then adds the value of A; and ++ A; first adds the value of, then return the value of A, 2. The same is true for a-and-A, but only for a minus.
The above variable A is defined as the top variable and is a float type variable. The use of the ++ operator cannot be optimized as expected, because the float type is a floating point type, it uses the IEEE real * 4 format to represent numbers, rather than binary original codes or supplementary codes, the INC and Dec commands mentioned above are based on the binary representation advantage to quickly increase and subtract one. Therefore, if the "++" operator is used for floating-point variables, it will be simply abbreviated, without any optimization effect (of course, if the CPU provides a new instruction set, such as MMX, to quickly add and subtract one in the real * 4 format, the compiler supports the corresponding instruction sets, which can still produce optimization results ).
Return Value of the value assignment operator
Before learning more about the differences between ++ A and A ++, let's take a look at what is the operator calculation (evaluate ). The operator is to process the given number and then return a number. The computation of operators is the processing of execution operators and the return value. As we already know, the operator is a symbol, and one or both sides of the operator can be connected to numbers, that is, other operators, and because the value assignment operator is also a type of operator, therefore, the execution sequence of operators becomes very important.
For A + B + C, A + B is executed first, and then (a + B) + C is executed. You may think there is nothing, as shown below, assume that A is 1 before:
C = (A * = 2) + (a + = 3 );
After the preceding statement is executed, A is 5. C = (a + = 3) + (A * = 2); after execution, A is 8. What about C? The results may be unexpected. The former's C is 10, while the latter's C is 16.
The above is actually a blind eye. "+" has no meaning, that is, it is not because "+" is executed from left to right, but because (A * = 2) the priority is the same as that of (a + = 3). The order of () is calculated from left to right. But why is the C value not the expected 2 + 5 and 4 + 8? Because of the relationship between the values returned by the value assignment operator.
The number returned by the value assignment operator is not the value of the variable, but the address of the variable. This is important. As mentioned above, writing a variable name will return the value of the corresponding variable, because the variable is a ing, and the variable name is equivalent to an address. In C ++, numbers are treated as a special operator, that is, any number is an operator. The address, like a long integer or a single-precision floating point number, is a type of number. When a number is of the address type, as an operator, it has no numbers to operate on, only the content in the memory identified by the number as the address is returned (explained by the address type ). The address can be obtained in multiple ways. For example, if you write a variable name above, you can get the corresponding address. The type of the obtained address is the type of the corresponding variable. If this sentence cannot be understood, you should be able to understand it after reading the following section on type conversion.
Therefore, the preceding c = (a + = 3) + (A * = 2); because the "()" Participation Changes the priority, two value assignment operators are executed first, then, both the value assignment operators return the address of a, calculate the value of "+", and calculate the numbers on both sides-the address of a (the address of a is also an operator ), that is, the value of a that has already executed two value assignment operations is 8, so the final value of C is 16. The other one makes C 10 for the same reason.
Now we need to consider the order in which operators are computed. When several operators with the same priority appear at the same time, different operators have different computational order. The calculation sequence of binary operators such as "()", "-", and "*" is calculated from left to right, and "!" Negative signs, "-", and other unary operators described above are calculated from the right to the left, such :! -!! A; assume that A is 3. Calculate the third "!" from left to right. , Resulting in the calculation of the address value of A, get 3; then the logic is reversed to 0, and then calculate the second "!" Value, the logic gets 1 after the inverse, and then calculates the value of the negative "-", gets-1, and finally calculates the first "!". Value, 0.
The assignment operators are calculated from the right to the left, except for the suffix "++" and suffix "-" (that is, the above a ++ and --). Therefore, the above C = A = 3;, because the two "=" have the same priority, from the right to the left, calculate the value of a = 3 first, and return the corresponding address of, then calculate the returned address and get the value 3. Then calculate c = (a = 3) and write 3 to C. Instead of calculating from left to right, that is, calculate c = a first, return the address of C, then calculate the second "=", write 3 to C, in this way, a is not assigned a value and a problem occurs. Also:
A = 1; C = 2; C * = a + = 4;
Because "* =" and "+ =" have the same priority, calculate a + = 4 from the right to the left, get a as 5, and then return the address of, calculate the value of a 5 for the address of a, and calculate "* =" to make the value of C 10.
Therefore, as mentioned above, ++ A will return the address of a, and a ++ must return an address because it is a value assignment operator, but obviously it cannot be the address of, therefore, the compiler will write code to allocate a memory of the same size as a from the stack, copy the value of A to this temporary memory, and then return the address of this temporary memory. This temporary memory is allocated because of the need of the compiler and has nothing to do with the programmer. Therefore, the programmer should not or cannot write this temporary memory (because the compiler is responsible for compiling the code, if the programmer wants to access this memory, the compiler will report an error), but can read its value, which is also the main purpose of the return address. The following statement is correct:
(++ A) = a + = 34;
But (A ++) = a + = 34; an error will be reported during compilation, because the memory identified by the address returned by a ++ can only be processed by the compiler, programmers can only obtain their values.
A ++ means to first return the value of A, that is, the address of the temporary memory mentioned above, and then add the value of the variable. If multiple A ++ instances exist at the same time, a temporary memory must be allocated for each a ++ instance (note that c = (a + = 3) + (A * = 2 ); ), it will be a little bad, and a ++ means to first return the value of A. When is the value of? In VC, when the expression contains the suffix "++" or the suffix "-", only one temporary memory is allocated, then, all the suffixes "++" or the suffixes "-" return the address of the temporary memory. After all the values of other operators that can be computed are calculated, then, write the value of the corresponding variable to the temporary memory, calculate the value of the expression, and add or subtract one from the value of the corresponding variable.
Therefore, a = 1; C = (a ++) + (A ++); after execution, the value of C is 2, and the value of A is 3. And as follows:
A = 1; B = 1; C = (++ A) + (A ++) + (B * = A ++) + (A * = 2) + (A * = A ++ );
During execution, the temporary memory is allocated first. Because of the five "()", the calculation order is from left to right,
Calculates the value of ++ A and returns the address of a after adding one. The value of A is 2.
Calculates the value of a ++ and returns the address of the temporary memory. The value of A is still 2.
Calculates a ++ in B * = A ++ and returns the address of the temporary memory. The value of A is still 2.
Calculate "* =" in B * = A ++, write the value of a to the temporary memory, calculate the value of B as 2, and return the address of B.
Calculates the value of a * = 2 and returns the address of a. The value of A is 4.
Calculates a ++ in a * = A ++ and returns the address of the temporary memory. The value of A is still 4.
Calculate "* =" in a * = A ++, write the value of a to the temporary memory, and return the address of a. The value of A is 16.
Calculate the remaining "+". For calculation, write the value of a to the temporary memory. The value 16 + 16 + 2 + 16 + 16 is 66, and the value is written to C.
Calculate the plus one of the three A ++ accounts, and the value of a becomes 19.
As mentioned above, I just want to warn you that using the value assignment operator in an expression is not highly respected. Because it does not conform to the common mathematical expression habits, and the computing sequence is easy to confuse. If there are multiple "++" operators, it is best to separate the expressions, otherwise it will easily lead to incorrect calculation order and calculation errors. In addition, the disordered computing order is not limited to the ++ above. In order to make you pay more attention to the red letter above, we will introduce more things that are even more prosperous, if you have already agreed to the above red letter, the following section can be skipped. It can be considered meaningless for programming (if not to write this article, I do not know its existence ).
Sequence point and side effect)
When c = A ++ is calculated, when the value of C is calculated (evaluate), the value of A is also increased by one. The value of a plus is used to calculate the additional effect of the preceding expression. What's the problem? It may affect the calculation result of the expression.
For a = 0; B = 1; (A * = 2) & (B + = 2);, because the two "()" have the same priority, they are calculated from left to right, calculate "* =" and return the address of a. Then calculate "+ =" and return the address of B. Finally, the logical false is returned because the value of A is 0. It is normal, but the efficiency is low.
If the number on the left of "&" is already 0, the formula on the right is no longer required. Similarly, if the number on the left of "|" is not zero, you do not need to calculate the number on the right. Because "&" and "|" are both mathematical. in mathematics, no matter whether the value on the left or the value on the right of the plus sign is calculated, the result will not change, therefore, "&" and "|" will be explained just now. This is also C ++'s guarantee. It not only satisfies the mathematical definition, but also provides optimization methods ("&" and "|" do not need to be computed on the right ).
Therefore, the formula above will be interpreted as -- if the value of a after the multiplication of 2 is 0, then B will no longer need to increase by 2. This clearly violates our original intention and thinks that B will be automatically increased by 2 in any case. However, C ++ ensures that not only the definition of mathematics, but also the optimization of code generation. However, the above B + = 2 will still be executed based on the operator's priority (that is why we will write the above Code ). To realize that B + = 2 is not computed when a is 0, C ++ proposes the concept of sequence points.
Sequence points are some special locations that are forcibly defined by C ++ (C ++ does not define sequence points, so different compilers may give different sequence point definitions, VC is a sequence point defined by C language ). During operator calculation, if a sequence point is encountered, the value at the sequence point must be prioritized for some special purposes, make sure that when a is 0, B + = 2 is not calculated, and operators related to sequence points (such as the preceding "&" and "| ") the calculation is completed before the normal calculation is resumed.
The number on the left of "&" is a sequence point, and the number on the left of "|" is also calculated. C ++ defines multiple sequence points, including expression calculation under conditions such as condition statements and function parameters. Here, you do not need to know which sequence points are specific, you only need to know that the existence of sequence points may lead to unexpected calculation of the value assignment operator. The following is an example:
A = 0; B = 1; (A * = 2) & (B ++ );
In the order of priority, the compiler finds that a * = 2 should be calculated first, then ++ A should be calculated, then "+ =", and finally "&" should be calculated. Then the compiler finds that the number sequence on the left of "&" appears during the computation process and must be prioritized, in this way, you may not need to calculate B ++. Therefore, the compiler first calculates the "&" number. Through the above calculation process, the compiler finds that the number on the left of "&" must be calculated after a * = 2, therefore, a * = 2 is calculated first, and the address of a is returned. Then, the number on the left of "&" is calculated, and the value of A is 0, therefore, B ++ A is not calculated. Instead of adding a to a first and then calculating a to return 1 because of the priority. Therefore, after calculation, A is 0, B is 1, and 0 is returned, indicating logical false.
Therefore, sequence points are generated to ensure the emergence of some special rules, such as "&" and "| ". Consider the "," operator. The operation is to calculate the values on both sides, and then return the number on the right, that is, a, B + 3 will return the value of B + 3, however, a is still calculated. Because "," has the lowest priority (but higher than the "Number" operator mentioned above), if A is 3, 4;, a is 3 instead of 4, because "=" is calculated first, return the address of a and then calculate ",". Also:
A = 1; B = 0; B = (a + = 2) + (A * = 2, B = A-1) & (C = ));
Because the number on the left of "&" is a sequence point, the values of a * = 2 and B are calculated first. However, according to the return value definition of ",", only the number on the right is returned, therefore, if a * = 2 is not calculated and B = A-1 is calculated as 0, "&" is returned, however, if a * = 2 is not calculated, the value of A is still 1, which violates the definition. To eliminate this (of course there may be other applications ","), c ++ also sets the left number of "," as a sequence point, that is, it will certainly give priority to ", "the number on the left to ensure the definition of", "-calculate the numbers on both sides. Therefore, the sequence of numbers on the left of "," causes a * = 2 to be preferentially executed, and B to be 1, therefore, because "&" is a sequence point and the number on the left is not zero, the priority must be calculated after the number on the right is calculated. c = A, 2, finally, the normal priority order is restored. Execute a + = 2 and "+ ". The result is that a is 4, C is 2, and B is 5.
So the previous A = 3, 4; in fact, the compiler should first find the "," sequence point, and find that the value on the Left needs to be calculated, A = 3 must be calculated first, so a = 3 is calculated first, so that the sensory sequence points do not seem to work. The following formula is analyzed by yourself. After execution, A is 4, but if you replace "," with "&", A is 2.
A = 1; B = (A * = 2) + (A * = 3), (a-= 2 ));
If you are dizzy, it doesn't matter, because the content above can be considered meaningless. It is written here to further prove to you, it is not good to use the value assignment operator in an expression. Even if it may make you write concise statements, it also reduces the maintainability of the Code.
Type conversion
In "C ++ from scratch (2)", numbers can be floating-point numbers, integer numbers, or other types, that is, numbers are of the type. Note that C ++ starts from scratch (3) explains the type. The type only describes how to explain the State. As mentioned earlier, binary numbers are used to represent the State for convenience, so we can say that the type is used to tell the compiler how to interpret the binary number.
Therefore, a long integer number tells the compiler to explain the state of the binary number in the binary complement format to get a value, A single-precision floating point number tells the compiler to explain the state of the binary number in the IEEE real * 4 format to obtain a decimal number. Obviously, the status represented by the same binary number is interpreted according to different types to get different numerical values. How does the compiler know the type of binary number to be interpreted?
As mentioned above, a number is a very special operator. It has no operands and only returns the State indicated by the binary number of its type (for convenience in the future, the "status represented by the binary number" is called the "binary number "). The operator is to execute the command and return numbers. Therefore, all operators must be executed at the end to return a binary number. This is very important, and it is important to understand the pointer behind it.
Let's look at 15 first. This is a statement, because 15 is a number. Therefore, 15 is considered a char-type number (because it is smaller than 128 and does not exceed the char expression range), an 8-bit long binary number is returned, the binary number is written in the complement format, which is 00001111.
Let's look at 15.0f again. Same as above, it is considered a float number because of the suffix "f". A 32-bit long binary number is returned, the binary number is written in the IEEE real * 4 format, which is 1000001011100000000000000000000.
Although the preceding 15 and 15.0f values are equal, they are expressed in different formats due to different types, and even the length of binary numbers used for representation is different. Therefore, if 15.0f = 15; is written, 0 is returned, indicating logical false. But actually 1 is returned. Why?
Since 15 and 15.0f are represented as two completely different binary numbers, but we think 15 and 15.0f are equal, but their binary representation is different, what should we do? The binary number representing 15.0f is interpreted as the 15th value in the IEEE real * 4 format, and then compiled into the binary number in the 8-bit binary complement format, then compare it with the original binary number representing 15.
To implement the preceding operations, C ++ provides the type conversion operator "()". It looks the same as the brackets operator, but the format is different: (<type Name>) <number> or <type Name> (<number> ).
The <type Name> operator of the above type conversion operator is not a number, so it will not be operated, but is used as a parameter to control how it operates the following <number>. <Type Name> is an identifier that uniquely identifies a type, such as char and float. The Return Value of the type conversion operator is as shown in its name. The <number> value is interpreted according to the <type Name> identifier type, and the return type is a number of <type Name>. Therefore, the above example needs to be written as follows: 15 = (char) 15.0f;, now it can return 1, indicating that the logic is true. But even if you do not write (char), the previous statement returns 1. This is because the compiler added (float) before 15 for convenience, so 1 is still returned. This is called implicit type conversion and will be mentioned later when class is described.
When a type can completely replace another type, the compiler will perform the implicit type conversion above and automatically add the type conversion operator. For example, char can only represent integers-128 to 127, and float can obviously represent these numbers, so the compiler performs implicit type conversion. It should be noted that this implicit conversion is required by the operator, that is, the preceding "=" requires that the numeric types on both sides be consistent. The result is that the two sides are different and the compiler converts Char to float, then execute the "=" operation. Note: In this case, the compiler always converts a poor type (such as the previous char) to a better type (such as the previous float) to ensure that the numerical truncation problem does not occur. For example:-41 = 3543; the left side is Char, and the right side is short. Because short is better than char (short can completely replace char), it is actually :( short) -41 = 3543;, 0 is returned. If it is-41 = (char) 3543; because Char cannot represent 3543, 3543 is converted to 0000110111010111 in binary number with a complement code, and then takes its 8-bit lower, as a result, 00001101 of the 8-bit height is discarded, which is called truncation. The return value of result (char) 3543 is the binary number of char 11010111, which is-41, and result-41 = (char) 3543. the return value is 1, indicating the logic is true, the error is obvious. Therefore, the preceding 15 = 15.0f; will actually be (float) 15 = 15.0f; (Note that 15 is interpreted by the compiler as the char type is not accurate here, more compilers interpret it as int type ).
Note that the reason for the previous development (that is, converting Char to float) is because "=", which requires this. Consider "=": Short B = 3543; char A = B ;. Because the value of B is of the short type, the requirement of "=" is to convert the number on the right of "=" to the same value on the left, in this way, you can write the correct memory (simply copy the binary number returned by the right number to the memory indicated by the Left address ). Therefore, a will be-41. However, the above implicit conversion is performed by the compiler according to the "=" requirement, the possible cause is that the programmer did not find this error due to negligence (the value of B must be within the range of-128 to 127). Therefore, the compiler will give a warning on the above situation, the value of B may be truncated. To eliminate the concerns of the compiler, char A = (char) B ;. This is called a display type conversion. It tells the compiler-"I know Data truncation may occur, but I promise not to cut it ". Therefore, the compiler will not issue a warning. But as follows: Char A = (char) 3543; since the compiler can be sure that 3543 will be truncated and the returned value is incorrect, the compiler will give a warning that 3543 will be truncated, regardless of whether the previous type conversion operator exists.
Now we can launch -- 15 + 15.0f; a float number is returned. Therefore, if char a = 15 + 15.0f; is used, the compiler will issue a warning that the data may be truncated. Therefore, it is changed to the following: Char A = (char) 15 + 15.0f;, but the type conversion operator "()" has a higher priority than "+, the result is that 15 is converted to Char first, and then implicitly converted to float due to the "+" requirement. Finally, the compiler still issues a warning when "=" is returned. To this end, you need to increase the "+" priority, as shown below: Char A = (char) (15 + 15.0f); it will be okay (or char (15 + 15.0f )), it means that I guarantee that 15 + 15.0f will not cause data truncation.
Pay attention to the type conversion operators "()", prefix "++", and "!". And negative signs such as "-" have the same priority and are calculated from the right to the left. Therefore, (char)-34; the value of-34 is calculated first, then calculate the (char) value, which is exactly in line with people's habits.
In the next article, we will propose a series of things for the special operator of numbers. Therefore, if you understand the meaning of numbers, pointers will be easy to understand.