Translated from Deep C (and C ++) by Olve Maudal and Jon jarger, the water itself is not half a bucket. If any user finds a mistake, leave a message to point it out :)
Next, let's take a deep understanding of the C/C ++ journey. I did not learn much in the first article, so I planned to finish translating the entire ppt.
See the following code snippet:
# Include <stdio. h>
Void foo (void)
{
Int;
Printf ("% d \ n", );
}
Void bar (void)
{
Int a = 42;
}
Int main (void)
{
Bar ();
Foo ();
}
Compile and run. What are you expecting to output?
$ Cc foo. c &./a. out
42
Can you explain why?
First candidate: Um? The compiler may have a variable name pool for reuse. For example, if the bar function uses and releases variable a, when the foo function requires an integer variable a, it will get the same memory area as a in the bar function. If you rename variable a in the bar function, I don't think you will get 42 output.
You: Well. OK...
Second candidate: Yes, I like it. Do you want me to explain how to store the execution stack or activity frame (activation frames) operation code in the memory, for example, in some systems, a function exists in the memory in this form:
ESP
Format parameters
Local variable
EIP
)?
You: I think you have proved the key to your understanding of this problem. However, if we use optimization parameters or other compilers for compilation during compilation, what do you think will happen?
Candidate: If compilation optimization measures are involved, many things may happen. For example, the bar function may be ignored because it does not play any role. At the same time, I am not surprised if the foo function will be called by inline so that no function is called. However, the foo function must be visible to the compiler, so the foo function's target file will be created so that the foo function needs to be linked to other target file link phases. In short, if I use compilation optimization, I should get different values.
$ Cc-O foo. c &./a. out
1606415608
Candidate: Spam value.
So what will this code output?
# Include <stdio. h>
Void foo (void)
{
Int a = 41;
A = a ++;
Printf ("% d \ n", );
}
Int main (void)
{
Foo ();
}
First candidate: I did not write code like this.
You: Good. good habits.
Candidate: But I guess the answer is 42.
You: Why?
Candidate: because there is no other possibility.
You: indeed, running on my machine does get 42.
Candidate: Right, hey.
You: But this code is actually undefined.
Candidate: Yes, I told you I did not write code like this.
The second candidate will appear: a will get an undefined value.
You: I didn't get any warning information, and I got 42.
Candidate: You need to raise your warning level. After the assignment and auto-increment, the value of a is indeed undefined, Because you violate one of the fundamental principles of the C/C ++ language. This rule is mainly for sequencing). C/C ++ requires that you can update each variable only once in a sequence operation. Here, a = a ++; is updated twice, which leads to a being an undefined value.
You: You mean, I will get an arbitrary value? But I did get 42.
Candidate: indeed, a can be 1099, or any value. I'm not surprised at all that your machine gets 42. What else can you get here? Or select 42 as an undefined value before compilation :)
What about the following code?
# Include <stdio. h>
Int B (void)
{
Puts ("3 ");
Return 3;
}
Int c (void)
{
Puts ("4 ");
Return 4;
}
Int main (void)
{
Int a = B () + c ();
Printf ("% d \ n", );
}
First candidate: simple, printed 3, 4, 7 in sequence.
You: Indeed. But it may also be, 7.
Candidate: ah? Is the operation order undefined?
You: to be accurate, this is not undefined, but not specified.
Candidate: A nasty compiler. I think he should give us a warning.
What do you mean by warning?
The second candidate: in C/C ++, the operation order is not specified. For a specific platform, the compiler can determine the operation order due to optimization needs, this is related to the execution sequence.
This Code conforms to the C standard. This code may output 3, 4, 7, or 4, 3, and 7, depending on the compiler.
What a wonderful life would be if most of my colleagues understood their language as you did :)
At this time, we will feel that the second candidate's understanding of the C language is obviously more profound than the first candidate. If you answer the above questions, what stage are you staying? :)
So, try to see the potential of the second candidate? Check whether he knows more about C/C ++.
You can look at the relevant knowledge:
Description and definition;
Call conventions and activity frames;
Collation;
Memory Model;
Optimization;
Differences between different C standards;
Here, we will first share the knowledge about the order and the differences between different C standards.
Consider the following code to get the output?
1.
Int a = 41;
A ++;
Printf ("% d \ n", );
Answer: 42
2.
Int a = 41;
A ++ & printf ("% d \ n", );
Answer: Undefined
3.
Int a = 41;
A ++ & printf ("% d \ n", );
Answer: 42
4. int a = 41;
If (a ++ <42) printf ("% d \ n", );
Answer: 42
5.
Int a = 41;
A = a ++;
Printf ("% d \ n", );
Answer: Undefined
When will the C/C ++ language have any side effects?
Order:
What is a sequence?
In short, the order point is such a position where all side effects have occurred before it, and all side effects after it have not yet started, the execution order of all expressions or codes between two order points is undefined!
Collation rule 1:
A single value can be written at most once between the first and last vertices, that is, between two vertices;
Here, a is written twice between two vertices, so this behavior is undefined.
Collation Rule 2:
Further, the previous value should be read-only to determine the value to be stored.
Many developers may think that the C language has many collation points. In fact, the C language has very few collation points. This gives the compiler more room for optimization.
Next let's take a look at the differences between various C standards:
Now let's go back to the first two candidates.
What will the following code output?
# Include <stdio. h>
Struct X
{
Int;
Char B;
Int c;
};
Int main (void)
{
Printf ("% d \ n", sizeof (int ));
Printf ("% d \ n", sizeof (char ));
Printf ("% d \ n", sizeof (struct X ));
}
First candidate: it will print out 4, 1, 12.
You: indeed, I got this result on my machine.
Candidate: Of course. Because sizeof returns the number of bytes, on 32-bit machines, the int type of C language is 32-bit or 4 bytes. Char is a byte length. In struct, it is usually 4 bytes aligned.
You: OK.
Do you want another ice cream? (I don't know what special emotions I have)
The second candidate is en. First, complete the code. Sizeof's return value type is site_t, not always the same as int type. Therefore, the output format % d in printf is not a good specifier.
You: OK. So what format specifiers should be used?
Candidate: this is a bit complicated. Site_t is an unsigned integer. on 32-bit machines, it is usually an unsigned int type, but on 64-bit machines, it is usually a number of unsigned long types. However, in C99, a new specifier is specified for the site_t type, so % zu will be a small choice.
You: OK. Then we should first improve the bug of this specifier. Answer this question.
# Include <stdio. h>
Struct X
{
Int;
Char B;
Int c;
};
Int main (void)
{
Printf ("% zu \ n", sizeof (int ));
Printf ("% zu \ n", sizeof (char ));
Printf ("% zu \ n", sizeof (struct X ));
}
Candidate: This depends on the platform and the options during compilation. The only certainty is that sizeof (char) is 1. Do you want to run it on a 64-bit machine?
You: Yes. I have a 64-bit machine running in 32-bit compatibility mode.
Candidate: Because of byte alignment, I think the answer should be 4, 1, 12. of course, this also depends on your compilation option parameters, which may be 4, 1, 9. if you add-fpack-struct when compiling with gcc to explicitly require the compiler to compress struct.
You: on my machine, we did get 4, 1, 12. Why is it 12?
Candidate: The cost is very high when the bytes are not aligned. Therefore, the compiler optimizes data storage so that each data field is stored as a word boundary. The storage of struct will also consider the situation of byte alignment.
You: Why is it expensive to work with non-aligned bytes?
Candidate: The instruction sets of most processors are optimized in terms of copying long data from memory to cpu. If you need to change a value that spans the word boundary, you need to read two words, block other values, and then write the changes back. It may be slower than 10. Remember, C language pays great attention to the running speed.
You: What if I add a char d to struct?
Candidate: if you add char d after struct, I expect sizeof (struct X) to be 16. because, if you get a 13-byte structure, it does not seem to be a very valid length. However, if you add char d after char B, then 12 is a more reasonable answer.
You: Why does the compiler not rearrange the Data Order in the struct to better optimize memory usage and Running Speed?
Candidate: some languages do, but C/C ++ does not.
You: What if I add char * d to the end of the struct?
Candidate: You just said that your runtime environment is 64-bit, so the length of a pointer is 8 bytes. Maybe the length of struct is 20? But another possibility is that 64-bit pointers need to be aligned in efficiency, so the code may output 4, 1, 24?
You: Good. I don't care what the results will be on my machine, but I like your opinion and insight J
(To be continued)
Waiting
From the column of Rockics