Reading notes: A deep analysis of C language

Source: Internet
Author: User

Reading notes: C-depth anatomy of the "C language in Depth" this book is a good book to solve the programmer's secret of the interview test. The author Chengzhong "to the gold to brave the challenge of similar books at home and abroad," Indeed, the book of knowledge points are some of the common in the Interview test center, and many are our usual point of no attention, for our in-depth understanding of C language is really helpful.

1th Chapter Keywords

1.register
Although the register is very fast, there are some limitations to using the Register modifier: The register variable must be a type that can be accepted by the CPU register.
means that the register variable must be a single value, and its length should be less than or equal to the length of the integral type. and the register variable may not be stored in memory,
Therefore, the address of the register variable cannot be obtained by using the fetch operator "&".
2.static modifier
(1) Modifier variables
Static local variables, defined within the function body, can only be used in this function, and other functions in the same document cannot be used. Since the static modified variable always exists in memory, even if the function ends, the value of the static variable will not be destroyed, and the function will still use this value the next time it is used.
(2) Modifier function
Second effect: modifier function. The function is static by adding a static to the function. But the meaning of "static" here is not the means of storage, but the scope of the function is confined to this file (so called intrinsic function). The advantage of using intrinsic functions is that different people write different functions without worrying about the functions they define and whether they have the same name as the functions in other files.
The keyword static has an unusual history. Initially, the keyword static was introduced in C to represent a local variable that still exists after exiting a block. Then, Static has a second meaning in C: the global variables and functions that represent the inability to be accessed by other files. To avoid introducing new keywords, the static keyword is still used to represent this second meaning.

3.if Statement Usage Note
Handle the normal situation first, and then deal with the abnormal situation.
The code is written so that the normal execution code is clear, and that the infrequently occurring exception handling code does not obscure the normal execution path. This is important for the readability and performance of your code. Because, if
The statement always needs to be judged, and the normal situation is generally more likely to occur than the exception (otherwise it should be transferred to normal), if the execution probability of the larger code to the back, it means that if the statement will be more than a meaningless comparison.
In addition, it is very important to put the normal processing behind the if and not behind else. This, of course, is in line with the requirement to put the normal situation in front of the deal.

4. Be careful and careful with the void pointer type.
By the ANSI (Americannationalstandardsinstitute) standard, you cannot perform algorithmic operations on void pointers, that is, the following operations are not valid:
void*pvoid;
Pvoid++;//ansi: Error
Pvoid+=1;//ansi: Error
The ANSI standard is determined because it insists that the pointer to the algorithm operation must be determined to be aware of its point to the data type size. This means that you must know the exact value of the memory destination address.
For example:
Int*pint;
Pint++;//ansi: Correct
But the famous GNU (GNU ' Snotunix's recursive abbreviation) does not identify it, it specifies that void* 's algorithm operation is consistent with char*. The following statements are therefore correct in the GNU compiler:
Pvoid++;//gnu: Correct
Pvoid+=1;//gnu: Correct
In the actual program design, in order to conform to the ANSI standard and improve the portability of the program, we can write code that implements the same function:
void*pvoid;
(char*) Pvoid++;//ansi: correct; GNU: Correct
(char*) Pvoid+=1;//ansi: error; GNU: Correct
There are some differences between GNU and ANSI, and in general, the GNU is more "open" than ANSI, providing support for more grammars. But we should conform to the ANSI standard as much as possible when it comes to real design.

5.const and macros
Save space, avoid unnecessary memory allocations, and increase efficiency
The compiler typically does not allocate storage space for ordinary const read-only variables, but instead saves them in the symbol table, which makes it a compile-time value, without the memory and read operations, making it highly efficient.
For example:
#define M 3//Macro Constants
const int n=5;//does not put N in memory at this time
......
inti=n;//now allocates memory for N, no longer assigned!
INTI=M;//macro substitution during precompilation, allocating memory
intj=n;//No memory Allocations
INTJ=M;//Another macro replacement and allocate memory again!
Const-defined read-only variable from the assembly's point of view, just give the corresponding memory address, instead of the immediate number as given in # define, so the const definition of the read-only variable in the program run only one copy (because it is a global read-only variable, stored in the static area), and # The macro constants defined by define have several copies in memory. #define宏是在预编译阶段进行替换, and the const-modified read-only variable determines its value at compile time.
#define宏没有类型, and the const-modified read-only variable has a specific type.

6. The most volatile keyword----volatile
Volatile is a volatile, unstable meaning. Many people have never seen this keyword and don't know it exists. There are also many programmers who know it exists, but never use it. I have a kind of "Yang family has a female to grow up, raise in purdah person not to know" feeling. The volatile keyword, like const, is a type modifier, and a variable that is decorated with it can be changed by factors unknown to the compiler, such as the operating system, hardware, or other threads. When you encounter a variable declared by this keyword, the compiler will no longer optimize the code that accesses the variable, providing stable access to the special address.
Let's take a look at the following example:
int i=10;
int j=i;//(1) statement
int k=i;//(2) statement
This time the compiler optimizes the code because I is not used as an lvalue in the (1) (2) two statement. At this time the compiler thinks that the value of I is not changed, so the value of I is taken out of memory in the (1) statement to the J
After that, the value is not discarded, but the value is continued to be assigned to K at the (2) statement. The compiler does not generate the assembly code to re-fetch the value of I from memory, which improves efficiency. However, note that: (1) (2) Between statements I is not used as a left value to line.
Let's look at another example:
volatile int i=10;
int j=i;//(3) statement
int k=i;//(4) statement
The volatile keyword tells the compiler i is ready to change, and each time it is used it must take the value of I from memory, so the compiler generated assembly code will re-read the data from the address of I in K.
Thus, if I is a register variable or represents a port data or multiple threads of shared data, it is prone to error, so volatile can guarantee a stable access to the special address.
However note: In vc++6.0, the General debug mode is not optimized for code, so the role of this keyword may not be seen. You can build both the debug and release versions of the program to do a test.
Leave a question: const volatile int i=10; Is there a problem with this line of code? If not, what is the attribute of that I?
This can be used at the same time.
7. Empty structures are of a size
Structstudent
{
}stu;
What is the value of sizeof (STU)? Test on visualc++6.0.
Unfortunately, not 0, but 1. Why? You think, if we look at structstudent as a mold, can you create a mold without any volume? Obviously not. The compiler also thinks so. The compiler considers any data type to have its size, and uses it to define a variable to allocate space for determining the size. In this case, the compiler takes for granted that any struct is of a size, even if the struct is empty. If the structure is really empty, why is its size appropriate? Assume that the structure body has only one char data member, which is 1byte in size (this is not the case for memory alignment). That is, non-empty struct type data requires a minimum of one byte of space, And the empty structure type data can not be larger than the smallest non-empty structure type data occupies space. This is the trouble, the size of the empty structure can not be 0, and can not be greater than 1, how to do? is defined as 0.5 byte? But the minimum unit of memory address is 1 byte,0.5 byte how to deal with it? The best way to solve this problem is to compromise, The compiler takes it for granted that you construct a struct data type that is used to package some data members, while the smallest data member requires 1 bytes, and the compiler reserves at least 1 bytes for each struct type data. Therefore, the size of the empty structure is positioned at 1 bytes.

8. Big-endian and small-end
What is the output value under the x86 system?
#include <stdio.h>
int main ()
{
int a[5]={1,2,3,4,5};
int *ptr1= (int *) (&a+1);
int *ptr2= (int *) ((int) a+1);
printf ("%x,%x", PTR1[-1],*PTR2);
return 0;
}
5 and 0x02000000
Is the way in which array a is stored in memory.

01

0xbfd46624

01

0xbfd46624

00

00

0xbfd46625

00

00

00

00

02

0xbfd46628

02

0xbfd46628

00

00

00

00

00

00

03

0xbfd4662c

03

0xbfd4662c

00

00

00

00

00

00

04

0xbfd46630

04

0xbfd46630

00

00

00

00

00

00

05

0xbfd46634

05

0xbfd46634

00

00

00

00

00

00

00

0xbfd46638

00

0xbfd46638

00

00

00

00

00

00

Since x86 is a small-end approach, low-level content is stored in a low-level address. Each color in the figure is an int-type memory distribution. &a can get the address of the array a , which is the 0xbfd46624 here, so the &a+1 result should be 0xbfd46638 (the bottom red part of the figure). For PTR1 in code because it is an int pointer, ptr[-1] should mean an integer that precedes the 0xbfd46638 address, which is the last value in the a array of 5. When calculating the PTR2, (int) A is converting the integer address A to an integer, so that the result of the (int) a+1 is 0xbfd46625, which is then converted to an int pointer, so that the value obtained by using PTR2 is An integral type starting from 0xbfd46625, which is 0x02000000

9. #define a int[10] and typedef int A[10];
Leave two questions:

1), #define a int[10]//macro definition no problem
a), a[10] a[10];
B), a[10] A;
C), int a[10];
D), int A;
E), a b[10];
F), a B;
G), A * b[10];
H), A * b;
for the above a ~h , using int[10] instead of a , You can see that the declaration is incorrect

2), typedef int A[10];//This is the definition of a new type
a), a[10] a[10];
B), a[10] A;
C), int a[10];
D), int A;
E), a b[10]; Equivalent to the definition of int b[10][10]
F), a b; //equivalent to the definition of int b[10]
G), A * b[10];//b is an array, the elements in the array are pointers to "array element type int, Number 10"
H), A * b;//b is a pointer to "array element type int, Number 10"
a ~d Incorrect,

3), #define a int*[10]//macro definition no problem
a), a[10] a[10];
B), a[10] A;
C), int a[10];
D), int A;
E), a b[10];
F), a B;
G), A * b[10];
H), A * b;
The definitions above are not correct at compile time

4), typedef int * a[10];//defines a new type: An array of 10 elements of element int*
a), a[10] a[10];
B), a[10] A;
C), int a[10];
D), int A;
E), a b[10];//  equivalent to the definition of int *b[10][10]
F), a b; //is equivalent to defining an int *b[10]
G), A * b[10]; //b is an array, The elements in the array are pointers to "array element type int*, Number 10", and A * b; //b is a pointer to "array element type int*, Number 10"
a ~d is incorrect,

5), #define *A int[10]//macro definition is wrong
a), a[10] a[10];
B), a[10] A;
C), int a[10];
D), int A;
E), a b[10];
F), a B;
G), A * b[10];
H), A * b;
All Errors

6), typedef int (* a) [10];//defines a pointer type that points to 10 elements array
a), a[10] a[10];
B), a[10] A;
C), int a[10];
D), int A;
E), the element in a b[10];//b is a pointer type of 10 "to an array of 10 int elements"
F), a b;//b is an array pointer to an array of 10 int elements
G), and A * b[10];//b is an array in which the element is " Pointer to a pointer type of 10 element array "
H", A * b;//b is a pointer to a pointer type of 10 array of type int "
a ~d Incorrect,

7), #define *A * INT[10]//macro definition is incorrect
A), a[10] a[10];
B), a[10] A;
C), int a[10];
D), int A;
E), a b[10];
F), a B;
G), A * b[10];
H), A * b;
A ~h are wrong

8), typedef int * (* a) [10];//array pointer, the element in the array is int*, number is 10
A), a[10] a[10];
B), a[10] A;
C), int a[10];
D), int A;
E), a b[10]; The elements in B are 10 "pointer types that point to 10 arrays of int* elements"
F), a B; b is an array pointer, which points to an array with 10 elements of type int*
G), A * b[10]; b is an array in which the element is a pointer to a pointer type of 10 array of int* elements.
H), A * b; b is a pointer to a pointer type of 10 int* element array.

A ~d not correct,


Please determine which definitions are correct and which are not. In addition, int[10] and a[10] in the end how to use?
A macro definition is simply a substitution, and a typedef defines a type, so it's easy to tell if the items above are correct. Some of the same colors highlighted in the table above are very similar, or have the same effect.

10. Curly Braces
Curly braces have been seen by everyone, very simple. But once a student asked me the following questions:
Char a[10] = {"ABCDE"};
He did not understand why the expression was correct. I asked him to change this example:
Char a[10] {= "ABCDE"};
Ask him if he can do this. What does the reader think?
What is the effect of curly braces? We usually write functions, if, while, for, switch statements, etc. all used it, but sometimes omitted it. In simple terms, curly braces are packaged. Do you think that the curly braces used to wrap up some statements or code to form a whole and insulate the outside world. If you understand this, the problem above is not a problem.

11. again on the difference between a and &a
int main ()
{
Char a[5]={' A ', ' B ', ' C ', ' D '};
char (*P3) [5] = &a;
char (*P4) [5] = A;
return 0;
}
int main ()
{
Char a[5]={' A ', ' B ', ' C ', ' D '};
char (*P3) [3] = &a;
char (*P4) [3] = A;
return 0;
}
int main ()
{
Char a[5]={' A ', ' B ', ' C ', ' D '};
char (*P3) [Ten] = &a;
char (*P4) [ten] = A;
return 0;
}
int a[5][5];
int (*p) [4];
p = A;
Q &p[4][2]-what is the value of &a[4][2]?

12. request 0-byte memory with the malloc function
Another question: does requesting 0-byte memory with the malloc function return a NULL pointer?
You can test it, or you can find the documentation for the malloc function. To request 0 bytes of memory, the function does not return NULL, but instead returns a normal memory address. But you can't use this size of 0
of memory. A tick on this good ruler, the scale itself is not of length, only a certain two scale together to measure the length. Be careful with this because the IF (NULL! = p) Statement Check will not work at this time.

13.write strlen functions without using any variables
See here, perhaps someone will say, strlen function is so simple, there is nothing good to discuss. Yes, I believe you can skillfully apply this function, and also believe that you can easily write this function. But if I raise the demand:
The call to library functions is not allowed, nor is it allowed to write int my_strlen (char *strdest) with any global or local variables; It seems that the problem is not so simple, right? This question was discussed on the network more warmly, I almost the whole "spectator", almost also can not help preface. But because my solution was already raised when I saw the post, I dropped it.
There are several ways to solve this problem, such as nesting and editing languages. Because nested assembly is generally only used in the embedded low-level development, so this book is not intended to discuss the C language embedded in the arbitrage of knowledge. Interested readers can find the relevant information. Perhaps some readers have thought of using recursive functions to solve this problem. Yes, you should want to, because I put this problem in the explanation of function recursion.
Now that we have the idea, this is a very simple question.
The code is as follows:
int My_strlen (const char* strdest)
{
ASSERT (NULL! = strdest);
if (' + ' = = *strdest)
{
return 0;
}
Else
{
Return (1 + my_strlen (++strdest));
}
}
The first step: Use the Assert macro to do the entry check.
Step two: Determine if the memory stored on the address passed by the parameter is ' \ '. If yes, indicates that this is an empty string, or is the end of the string flag.
Step three: If the memory on the address passed by the parameter is not ' \ s ', then the memory on this address is stored as a character. Since a character is stored on this address, it is counted as 1, then the address is added to the size of the 1 char type element, and then the function itself is called. In this loop, the recursion stops when the address is added to the end-of-sign of the string.
Of course, recursion is also used, and people write more concise code:
int My_strlen (const char* strdest)
{
Return *strdest?1+strlen (strdest+1): 0;
}
Here is a clever use of the question mark expression, but did not do the parameter entry check, at the same time with *strdest to replace (' "= = *strdest) is not very good. Therefore, this kind of writing is very concise, but it does not conform to the coding specifications we described earlier.
Can rewrite:
int My_strlen (const char* strdest)
{
ASSERT (NULL! = strdest);
Return (' *strdest '! =)? (1+my_strlen (strdest+1)): 0;
}

The above problem takes advantage of the recursive nature of functions to be easily done, that is, every call to the My_strlen function, in fact, only judge the contents of a byte. However, if the passed-in string is long, the function call needs to be repeated several times, and the cost of the function call is much larger than the loop, so the recursion is inefficient, the recursion depth is too large, or even an error can occur (such as a stack overflow). So, usually write code, not the last resort, try not to use recursion. Even if you want to use recursion, be aware that the hierarchy of recursion is not too deep to prevent stack overflow errors, and that the recursive stop condition must be correct, otherwise recursion may be endless


Transferred from: http://www.cnblogs.com/xkfz007/

Reading notes: A deep analysis of C language

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.