Advanced application of C language complex expression and pointer

Advanced application of C language complex expression and pointer

"C Language Advanced Topic Part 4-4.4. C Language Complex Expression and Pointer Advanced Application"

The first part, chapter list
4.4.1. Pointer array and array pointer
4.4.2. Function pointer and typedef
4.4.3. Function pointer in practice 1
4.4.4. Function pointer in practice 2
4.4.5. Discuss typedef
4.4.6. II Heavy pointer
4.4.7. Two-dimensional array
4.4.8. Operation and pointer of two-dimensional array

The second part, chapter introduction
4.4.1. Pointer array and array pointer
This section describes two complicated C language symbols that are easy to confuse: pointer array and array pointer. And hope to introduce the analytical method of C language complex expression through these two "entry-level" complex symbols.
4.4.2. Function pointers and typedefs
This section describes function pointers, which are more complex expressions in C language. Use the methods introduced in the previous section to parse function pointers, so that everyone can review this very effective analysis method again. And introduce the typedef key.
4.4.3. Function pointer practice 1 In
this section, we will conduct practical programming exercise 1 of function pointers. By writing a calculator program, let everyone experience the conventional usage of function pointers and point out the implicit object-oriented thinking mode.
4.4.4. Function Pointer Actual Combat 2
This section continues the actual combat programming exercise 2 of function pointer. It is a case of using function pointers to implement architecture under a more complex hierarchical structure. This technique is widely used in the Linux kernel driver.
4.4.5. Re-discussion on typedef
This section will discuss the idiom and application purpose of typedef in detail. In addition to sorting out the content mentioned before, it focuses on the two new knowledge points of typedef and structure, typedef and const.
4.4.6. Dual pointers
This section describes dual pointers, through code examples to let everyone understand the nature of dual pointers, so as to better understand the nature of pointer variables. Through examples, several common application methods of dual pointers, the relationship between dual pointers and pointer arrays, etc. are explained.

4.4.7. Two-dimensional arrays
This section explains two-dimensional arrays. 1. analyze the two-dimensional array from the perspective of memory, and then access the array elements from the perspective of subscript access and pointer access, trying to make everyone understand the essence of the two-dimensional array.
4.4.8. Two-dimensional array operations and pointers
This section combines two-dimensional arrays and pointers. Through some topic operations, let everyone understand some of the rules of operations between two-dimensional arrays and pointers. This is also the most troublesome in real programming. The place.

The third part, the accompanying record
4.4.1. Pointer array and array pointer
4.4.1.1, literally understand the pointer array and array pointer
(1) The essence of the pointer array is an array, and the contents stored in this array are all pointer variables .
(2) The essence of an array pointer is a pointer, which points to an array.
4.4.1.2. Analyze the expression of pointer array and array pointer
(1) int *p[5]; int (*p)[5]; int *(p[5]);
(2) General rule: int *p ;(p is a pointer); int p[5];(p is an array)
Summary: When we define a symbol, the key is: first of all, we must figure out who the symbol you define is (first step: find the core) ; Next, let's look at who is closest to the core and who is combined with the core (Step 2: Find the combination); Later, continue to expand outward (Step 3: Continue to combine outward until the entire symbol is completed).
(3) How to combine the core and * means that the core is a pointer; if the core is combined with [], it means that the core is an array; if the core is combined with (), it means that the core is a function.
(4) Use general rules to analyze 3 symbols: the
first one: int *p[5]; The core is p, p is an array, the array has 5 elements, the elements in the array are pointers, and the pointers point to The element type is of type int; the entire symbol is an array of pointers.
The second one, int (*p)[5]; the
core is p, p is a pointer, the pointer points to an array, the array has 5 elements, and the elements stored in the array are of type int; to summarize the meaning of the entire symbol is an array pointer.
The third one, int *(p[5]); The 
analytical method and conclusion are the same as the first one, () is optional here.

Note: What is the use of symbol priority? In fact, it is to decide which symbol is operated first and which symbol is operated after when two symbols work together.
What should I do if I encounter priority issues? 1. check the priority list; second, remember it by yourself (remembering all become gods, people only need to remember []. -> these priorities are better).

4.4.1.3. Summary 1: Priority and associativity are the key to analyzing the meaning of symbols
(1) When analyzing C language problems, don t guess the rules indiscriminately. Don t always feel that C language is unpredictable. Start from the known rules and follow the established Just follow the rules.
4.4.1.4. Summary 2: Learn the analysis method of layer-by-layer peeling
(1) After finding the core, combine it layer by layer from the inside to the outside. After the combination, the combined part can be regarded as a whole, and then continue with the outside of the whole Combine.
4.4.1.5. Summary 3: Basic theories and principles are the key, there are no unreasonable rules

4.4.2. Function pointer and typedef
4.4.2.1. The essence of function pointer (still pointer variable)
(1) The essence of function pointer is still pointer or pointer variable. It occupies 4 bytes (in a 32-bit system, all pointers are 4 bytes)
(2) There is no essential difference between function pointers, array pointers, and ordinary pointers. The difference lies in what the pointer points to.
(3) The essence of a function is a piece of code. This piece of code is continuously distributed in memory (all statements enclosed in curly braces of a function will be compiled to generate an executable program in the future), so it is very important for a function. The key is the address of the first sentence of code in the function. This address is the so-called function address, which is represented by the symbol of function name in C language.
(4) Combining the essence of the function, the function pointer is actually an ordinary variable. The type of this ordinary variable is the function pointer variable type, and its value is the address of a certain function (that is, the symbol of its function name corresponds to the compiler Value)

4.4.2.2. Writing and analysis methods of function pointers
(1) The C language itself is a strongly typed language (each variable has its own variable type), and the compiler can help us do strict type checking.
(2) In fact, all pointer variable types are essentially the same, but why should they be distinguished in C language and written differently (for example, int type pointers are written as int *p; array pointers are written as int (*p) [5], the function pointer has to be written more complicated)
(3) Suppose we have a function: void func(void); Corresponding function pointer: void (*p)(void); Type: void (*) (void);
(4) The biggest difference between a function name and an array name is: the effect and meaning of adding or not adding & when the function name is used as an rvalue is the same; but the meaning and meaning of adding or not adding & when an array name is used as an rvalue is different.
(5) Write an example of a complex function pointer: For example, the function is strcpy function (char *strcpy(char *dest, const char *src);), the corresponding function pointer is: char *(*pFunc)(char *dest , const char *src);

4.4.2.3. Usage of typedef keyword
(1) typedef is a keyword in C language, which is used to define (or rename type)
(2) There are two types in C language: one is compiled The native type defined by the device (basic data types, such as int, double, etc.); the second is user-defined types, which are not built in the language but are defined by the programmer themselves (such as array types, structure types, function types) ).
(3) The array pointers, pointer arrays, function pointers, etc. we are talking about today are all user-defined types.
(4) Sometimes a custom type is too long and inconvenient to use, so use typedef to rename it with a shorter name.
(5) Note: Typedef is to rename the type, that is to say, all types processed by typedef are types, not variables.

4.4.2.4. Summary: The analysis method of function pointers is also derived from the basic theory of priority and layer-by-layer separation

4.4.3. Function pointer actual combat 1
4.4.3.1. Call execution function with function pointer
(1) The simplest example of function pointer to call function has been demonstrated in the previous lesson.
(2) This section demonstrates the use of function pointers to point to different functions to achieve different results when the same call is executed.
(3) If you have studied object-oriented languages such as C++ or Java or C#, you will know that there is a polymorphism among the three major characteristics of object-oriented. Polymorphism means that the actual result of the same execution is different, which is actually the same as the phenomenon we see here.
(4) In the debugging process just now, a lot of information can be obtained:
First: When a segment error occurs in the program, the first step is to locate the segment error. The method of locating is to add printing information to the suspicious place, thereby locking the sentence that caused the segfault, and then focus on analyzing why this sentence is a segfault.
Second: The command line in linux is line buffered by default, which means that when our program printf outputs, linux will not output our content word by word, but buffer it and put it in the buffer and wait for a line to prepare After that, output all lines at once (for efficiency). The basis for linux to judge whether a line is finished is the newline character'\n' (the newline character is/r\n in windows,/n in linux, and/r in iOS). In other words, no matter how many printf you have, as long as it does not encounter/n (or the program terminates, or the buffer is full), it will not output but will continue to buffer. At this time, you will not see the content output. Therefore, you must add/n after each printf print statement (especially the printf statement used for debugging), otherwise it may cause misjudgment.
Third: Regarding the issue of using scanf to write interactive code under the Linux command line, I would like to say the following:
1. The interactive program under the command line is purely for learning programming and has almost no practical significance. Don t waste time. Up.
2. Scanf deals with the standard input of the system, and printf deals with the standard output. To fully understand these things, you have to clarify the standard input and standard output.
3. Our users always end with/n when inputting content, but they will not receive the final/n when scanning in the program, which causes this carriage return character to remain in the standard input. The next time you scanf, it will be taken out first, which will cause the number you really want to take but no chance to take, resulting in an error.

4.4.4. Function Pointer Actual Combat 2
Topic: Implementing Layered Function Pointer Embedded in Structure
(1) Why should the program be layered? Because there are too many complicated programs that one person can't handle, and more people are needed to work together, so it is necessary to divide the work. The division of labor must be layered first, and then each layer will be completed by different people, and then call each other to work together.
(2) This program is to complete a calculator. We have designed two levels: the upper layer is framework.c, which implements the application framework; the lower layer is cal.c, which implements the calculator. In actual work, cal.c completes the work directly, but the key part of cal.c is completed by calling the function in framework.c.
(3) Write framework.c first and complete it by one person. This person needs to complete the business logic of the calculator in framework.c, and write the corresponding interface in the corresponding header file and send it out. In the future, people at other levels will use this header file to work together.
(4) Another person completes cal.c and implements a specific calculator; this person needs the staff of the framework layer to provide header files to work (but does not need framework.c)
(5) Summary:
First: this section and The previous section actually completed the same exercise, but used a different program structure.
Second: For simple problems, the non-layering in the previous section is easy to understand, but simple; the layered code in this section is not easy to understand, and it seems to complicate simple problems. The reason is that our problem itself is indeed a simple problem, and simple problems should be dealt with in a simple way. Why do we do this even knowing that we are wrong? The purpose is to demonstrate to everyone the idea and method of this layered code writing.
Third: The idea of writing code hierarchically is: there are multiple levels of combination to complete tasks, each level focuses on its own different fields and tasks; different levels use header files to interact.
Fourth: After layering, the upper layer provides services for the lower layer, and the code written by the upper layer is to be called in the lower layer.
Fifth: The upper layer pays attention to business logic, which is directly related to our ultimate goal, and there is no specific work function.
Sixth: The lower layer pays attention to the functions that actually work, and pays attention to filling the variables for the upper layer, and passing the variables to the functions in the upper layer (in fact, it is to call the interface function provided by the upper layer) to complete the task.
Seventh: The core of the lower-level code is actually a structure variable (such as struct cal_t in this example). The logic of the lower-level code is actually very simple: the first step is to define the structure variable; the second step is to fill the structure variable; Call the interface function written in the upper layer in three steps and pass the structure variable to it.

4.4.5. Again on typedef
4.4.5.1, two types of C language: built-in type and user-defined type
(1) built-in type ADT, custom type UDT
4.4.5.2, typedef definition (or rename) Type instead of variable
(1) Type is a data template, variable is a real data. Types do not occupy memory, while variables do not occupy memory.
(2) In object-oriented languages: the type is the class, and the variable is the object.
4.4.5.3, the difference between typedef and #define macro
typedef char *pChar;
#define pChar char *

4.4.5.4, typedef and structure
(1) When a structure is used, the structure type is defined first, and then the structure type is used to define the variable.
(2) The C language syntax stipulates that the structure type must be the struct structure type name and the structure variable name when used; variables are defined in this way.
(3) Use typedef to define two types at a time, namely the structure variable type and the structure variable pointer type.

4.4.5.5, typedef and const
(1) typedef int *PINT; const PINT p2; equivalent to int *const p2;
(2) typedef int *PINT; PINT const p2; equivalent to int *const p2;
(3) if it is true Want to get const int *p; this effect, you can only typedef const int *CPINT; CPINT p1;

4.4.5.6. The importance of using typedef (2: simplifying types, creating platform-independent types)
(1) Simplifying the description of types.
char *(*)(char *, char *); typedef char *(*pFunc)(char *, char *);    
(2) In many programming systems, people tend not to use C language built-in types such as int and double , Because these types themselves are related to the platform (for example, int is 16-bit on a 16-bit machine, and 32-bit on a 32-bit machine). To solve this problem, many programs use custom intermediate types for buffering. For example, this technology is widely used in the Linux kernel. The
kernel is first defined: typedef int size_t; and then size_t is used to replace int under specific coding needs (for example, there may be typedef int len_t)
(3) All use in the STM32 library A custom type, such as typedef volatile unsigned int vu32;


4.4.6. Double pointer
4.4.6.1. The difference between double pointer and ordinary single pointer
(1) Essentially, the essence of double pointer and single pointer is pointer variable, and the essence of pointer variable is variable.
(2) Both the single pointer variable and the double pointer variable themselves occupy 4 bytes of memory space.
4.4.6.2. The essence of the double pointer
(1) The double pointer is essentially a pointer variable, and the difference with ordinary pointers is what it points to The variable type must be a single pointer. A double pointer is actually a data type. The compiler will perform static type checking based on the data type of the double pointer when compiling. Once it finds that the data type does not match during the operation, the compiler will report an error.
(2) Can it work if there is no double pointer in C language? In fact, it is possible. One pointer can do the same thing as two pointers. The reason for inventing dual pointers (function pointers, array pointers) is to let the compiler know that when the pointer is defined, the programmer who defines it wants this pointer to be used to point What (marked with data types when defining pointers, such as int *p, means p points to int data), the compiler can help us do static type checking after knowing the pointer type. This kind of static type checking of the compiler can assist the programmer in discovering some implicit programming errors. This is a compile-time error checking mechanism provided by the C language to the programmer.
(3) Why does the C language need to invent a double pointer? The reason is the same as the invention of function pointers, array pointers, structure pointers, etc.
4.4.6.3. The usage of double pointers
(1) The double pointer points to the address of the first pointer
(2) The double pointer points to the pointer array
(3) The double pointer is rarely used in practice programming, and most of the time it is the pointer The array is tangled up.
(4) In practice programming, sometimes in order to change an external pointer variable through the function when passing parameters, the address of the pointer variable (that is, a double pointer) is passed in.

4.4.6.4. Double pointers and array pointers
(1) The essence of double pointers, array pointers, structure pointers, single pointers, and ordinary variables is the same, and they are all variables.
(2) All pointer variables are essentially the same. They are all 4 bytes and are used to point to other things. Different types of pointer variables can only point to (the compiler allows you to point to) the variable types are different .
(3) The double pointer is: pointer array pointer


4.4.7. Two-dimensional array
4.4.7.1. Memory mapping of two-dimensional array
(1) A one-dimensional array is composed of multiple consecutively distributed memory cells in the memory, while a two-dimensional array is also multiple consecutively distributed in the memory Composed of memory cells.
(1) From the memory point of view, there is no essential difference between a one-dimensional array and a two-dimensional array.
(2) There is actually no essential difference between the two-dimensional array int a[2][5] and the one-dimensional array int b[10]. We can write down the correspondence between the two units of the same unit.
a[0][0] a[0][1] a[0][4] a[1][0] a[1][1] a[1][4]    
b[0] b[1] b[4] b[5] b[6] b[9]
(3) Since two-dimensional arrays can be represented by one-dimensional arrays, what is the meaning and value of the existence of two-dimensional arrays? Tell everyone clearly: the two-dimensional array a and the one-dimensional array b are exactly the same in terms of memory usage efficiency and access efficiency (or the difference is negligible). In some cases, two-dimensional arrays are used instead of one-dimensional arrays. The reason is that two-dimensional arrays are easy to understand, code easy to write, and easy to organize.
(4) Summary: We use two-dimensional arrays (C language provides two-dimensional arrays), it is not necessary, but a way to simplify programming. Think about it, the emergence of one-dimensional arrays is actually not inevitable, but also to simplify programming.

4.4.7.2. Which is the first dimension and which is the second dimension?
(1) In the two-dimensional array int a[2][5], 2 is the first dimension and 5 is the second dimension.
(2) Combine memory mapping to understand the meaning of the first and second dimensions of a two-dimensional array. First of all, the first dimension is the outermost layer of the array, so the int a[2][5] array has 2 elements; each element is an array with 5 elements (this array is the second dimension).
(3) Summary: The first dimension of the two-dimensional array is the outermost layer, the first dimension itself is an array, and the elements stored in this array are also an array; the second dimension of the two-dimensional array is the inner layer , The second dimension itself is an array, the elements stored in the array are ordinary elements, and the second dimension is stored as an element in the first dimension.

4.4.7.3. Subscript access and pointer access
for two-dimensional arrays (1) Review: Two access methods for one-dimensional arrays. Take int b[10] as an example, int *p = b;.
b[0] is equivalent to *(p+0); b[9] is equivalent to *(p+9); b[i] is equivalent to *(p+i)
(2) Two access methods for two-dimensional arrays: Take int a[2][5] as an example, (appropriate type) p = a;
a[0][0] is equivalent to *(*(p+0)+0); a[i][j] is equivalent At *(*(p+i)+j)

4.4.7.4. The application of two-dimensional arrays and more-dimensional arrays
(1) In the simplest case, there are 10 students whose scores are to be counted; if there is no difference between these 10 students, use b[10]; if these 10 Each student is naturally divided into 2 groups, 5 in each group, it is suitable to use int a[2][5] to manage.
(2) The most common case: a one-dimensional array is used to represent a straight line, and a two-dimensional array is used to describe a plane. Mathematically, it is easy to understand the comparison of a two-dimensional array with a rectangular coordinate system.
(3) Compare and understand the three-dimensional array and the three-dimensional coordinate system. A three-dimensional array is actually a three-dimensional space.
(4) Four-dimensional arrays can also exist, but they are mathematically meaningful, and there is no correspondence in space now (because the universe in which humans live is three-dimensional).
Summary: Generally, two-dimensional arrays are most commonly used, and three-dimensional arrays are basically not used except for some special mathematical operations. (A three-dimensional array is used when calculating the angle and attitude of the four-axis aircraft)


4.4.8. Two-dimensional array operations and pointers
4.4.8.1. The pointer points to the array name of the two-dimensional array
(1) The array name of the two-dimensional array represents the first element of the first-dimensional array of the two-dimensional array (that is, the second The first address of the array)
(2) The array name a of the two-dimensional array is equivalent to &a[0], which is consistent with the symbolic meaning of the one-dimensional array.
(3) Using array pointers to point to the array name of a two-dimensional array matches the type.

4.4.8.2. The pointer points to the first dimension of the two-dimensional array
(1) Use int *p to point to the first dimension of the two-dimensional array a[i]

4.4.8.3. The pointer points to the second dimension of the two-dimensional array
(1) The second-dimensional element of the two-dimensional array is actually an ordinary variable (a[1][1] is actually 7 of the int type), and the pointer type is no longer available Mutual assignment with it.
(2) Unless int *p = &a[i][j];, similar to a pointer pointing to the first dimension of a two-dimensional array.

Summary: The key to the entanglement between two-dimensional arrays and pointers is 2 points:
1. The meaning of each symbol in the array.
2. Pointer access to arrays, especially pointer access to two-dimensional arrays.