1. Table 2.1. c Standard Escape Character \ 'single quote or apostrophe) \ "Double quotation marks "\? Question mark? (Question mark) \ backslash \ (backslash) \ A bell (alert or bell) \ B backspace \ f form feed) \ n line feed \ r carriage return \ t horizontal tab \ v vertical Tab)
2. the Declaration in C language includes three types: Variable declaration, function declaration, and type declaration. If a declaration of a variable or function requires the compiler to allocate storage space for it, it can also be called definition. Therefore, definition is a declaration. For variables, the definition or initialization in this file requires the allocation of storage space, and the form such as extern int top; only declares that the top variable is defined in another file, it is declared that no storage space is allocated here. Declaring a type does not allocate a bucket. You must note that the type definition is also a declaration. The declaration must end with a; sign, and the struct type must be defined less after; it is a common mistake for beginners.
The Declaration is similar to the statement, and it ends with a; sign. However, the syntax declaration is different from the statement. The statement can only appear in {} brackets, the Declaration can either appear in {} or all.
3. characters can also be expressed by ASCII escape sequences. The Escape sequences include \ and 1 ~ Three Octal numbers, or \ x or uppercase \ x plus 1 ~ It is composed of two hexadecimal numbers and can be used in character constants or strings. For example, '\ 0' indicates a null character (null character),' \ 11' or '\ x9' indicates a Tab character, "\ 11" or "\ x9" indicates a string consisting of TAB characters. Note that the ASCII code of '0' is 48, and the ASCII code of '\ 0' is 0. The two are different.
4. printf, we do not care about its return value (in fact it also has a return value, indicating the number of characters actually printed), we call printf not to get its return value, it is used to take advantage of its side effects (side effect) -- print. C functions can have side effect, which is the fundamental difference between them and mathematical functions.
5. the Return Value of the main function is int type and return 0; this statement indicates that the return value is 0, and the return value of the main function is returned to the operating system, because the main function is called by the operating system, usually 0 is returned when the program is successfully executed, and a non-zero value is returned when an error occurs during execution. For example, we can change the return statement in the main function to return 4; then execute it. After execution, we can see its exit status (exit status) in shell: $. /. out11 and 0 hours $ echo $? 4 $? Is a special variable in shell, indicating the exit status of the previous command.
6. in fact, the operating system transmits parameters when calling the main function. The most standard form of the main function should be int main (INT argc, char * argv []). the standard also allows the int main (void) method. If you do not use the two parameters passed in by the system, you can also write them in this form. However, apart from the two forms, other methods for defining the main function are incorrect or cannot be transplanted.
7. clarify the concepts of function declaration, function definition, and function prototype. For example, void threeline (void) declares a function name, parameter type and number, and return value type. This is called a function prototype. In the code, you can write a function prototype, followed by a; sign to end without writing the function body, for example, void threeline (void ); this method can only be called a function declaration, not a function definition. Only a declaration with a function body is called a definition. As mentioned above, only the declaration of variables for Bucket allocation is called variable definition. In fact, the same is true for functions. The Compiler generates commands only when it sees the function definition, commands also occupy storage space when running programs. So what is the purpose of function declaration without function bodies? It provides useful information for the compiler. During code translation, the compiler can only see the function prototype (whether with or without the function body) then you will know the name, parameter type, and return value of the function. In this way, you will know how to generate corresponding commands when you encounter a function call. Therefore, the function prototype must appear before the function is called, this follows the principle of "first voice and then use. Because of the existence of the old style C syntax, not all function declarations contain the complete function prototype, such as void threeline (). This declaration does not explicitly specify the parameter type and number, therefore, it is not a function prototype. The information provided to the compiler is only the function name and return value type. If a function is called after such a declaration, the compiler does not know the type and number of parameters, so it is easy to introduce bugs. You need to understand this knowledge point to maintain the code written in the old style C style, but you should never write new code in this style.
8. if threeline is not declared when the main function calls threeline, the compiler considers that int threeline (void) is implicitly declared here. The returned values of the implicitly declared function are int, because no parameter is passed when we call this function, the compiler considers the implicit declared parameter type to be void, so that the parameter and return value types of the function are determined, the compiler generates corresponding commands for function calls based on the information. Then the compiler looks down and we can see that the prototype of the threeline function is void.
Threeline (void), which is inconsistent with the type of the previously implicitly declared return value, so a warning is reported. If the return value of this function is not used at this time, the execution result will still be correct.
9. Remember this basic principle: the shape parameter is equivalent to the variable defined in the function. The process of calling the function to transfer parameters is equivalent to defining the shape parameter variable and initializing it with the value of the real parameter.
10. The variables defined in the function are called local variable. Because the form parameter is equivalent to the variable defined in the function, the form parameter is also a local variable. Here, "local" has two meanings: 1. variables defined in a function cannot be used by another function. 2. Each time a function is called, local variables indicate different buckets. Local variables are allocated with storage space for each function call ,. The bucket is released when each function returns. Compared with the concept of local variables, global variables are defined outside all function bodies and they allocate storage space when the program starts running, when the program ends, the bucket is released and global variables can be accessed in any function, because global variables can be accessed in any function, therefore, the order in which global variables are read and written cannot be seen from the source code. The order in which the source code is written does not reflect the order in which the function is called. When a program encounters a bug, it is often because the read/write sequence of global variables is incorrect in a humble place. If the code size is large, this error is hard to find. Access to local variables is not limited to a function, but also limited to callback function calls. From the source code of the function, it is easy to see what the access sequence is, therefore, it is easier to find bugs. Therefore, although global variables are easy to use, you must use them with caution. If you can use a function to transmit parameters, do not use global variables.
In C language, each identifier has a specific scope. A global variable is an identifier defined outside the body of all functions. Its scope starts from the defined position until the end of the source file, the scope of local variables of the main function is limited to the main function. As shown in, imagine that the entire source file is a large piece of paper, that is, the scope of global variables, and the main function is a small piece of paper built on this large piece of paper, that is, the scope of the local variables of the main function.
11. when initializing a variable, we use constants for initializer. In fact, we can also use expressions for initializer. However, note that local variables can be initialized using any expressions with consistent types, however, global variables can only be initialized using a constant expression (constant expression. For example, the global variable PI Initialization is legal: Double Pi = 3.14 + 0.0016; but the initialization is illegal: Double Pi = ACOs (-1.0 ); however, local variables can be initialized in this way. When the program starts running, appropriate values must be used to initialize global variables. Therefore, the initial values must be stored in the compiled executable files. Therefore, the initial values must be calculated during compilation, however, the value of the second initializer must call the ACOs function when the program runs. Therefore, it cannot be used to initialize global variables. Note the two concepts of compile time and runtime. To simplify the implementation of the compiler, the C language syntactically stipulates that global variables can only be initialized using constant expressions. Therefore, the initialization of the following global variables is illegal: int minute = 360; int hour = minute/60; although it is possible to calculate the initial value of hour during compilation, minute/60 is not a constant expression and does not comply with syntax requirements, therefore, the compiler does not have to calculate this initial value. If the global variable is not initialized during definition, the initial value is 0. If the local variable is not initialized during definition, the initial value is uncertain. Therefore, you must assign values to local variables before using them. If you perform subsequent calculations based on an uncertain value, a bug will certainly be introduced.
12. non-defined function declaration can also be written in a local scope, for example: int main (void) {void print_time (INT, INT); print_time (23, 59); 66 return 0 ;} in this way, the declared identifier print_time has a local work domain. It is only a valid function name in the main function, and the identifier print_time does not exist when the main function is generated. The statement block can also define local variables, such as void Foo (void) {int I = 0; {int I = 1; Int J = 2; printf ("I = % d, j = % d \ n ", I, j);} printf (" I = % d \ n ", I, the storage space of variable J is allocated each time the statement block is entered, and the storage space of variable J is released each time the statement block is exited. The statement block also constitutes a scope. Similar to the above analysis, if the entire source file is a large piece of paper, the foo function is built on a small piece of paper, the statement block in the function is a smaller piece of paper built on a small paper. The variable I in the statement block and the local variable I of the function are two different variables, so the I value printed twice is different; the variable J in the statement block does not exist after exiting the statement block. Therefore, the printf in the last row cannot print the variable J. Otherwise, the compiler reports an error. The statement block can be used in any place where statements are allowed, not necessarily in the IF statement. The statement block is used separately to define some variables that are more "local" than the local variables of the function.
13. Int is_even (int x) {return! (X % 2);} the return value of the function should be understood as follows: The function returns a value equivalent to defining a temporary variable of the same type as the return value and initializing it with the expression following return. For example, the above function call is equivalent to the following process: int Temporary Variable =! (X % 2); function exit, local variable X storage space is released; If (temporary variable) {} else {} when the if statement judges the return value of the function, the function has exited and the local variable X has been released. Therefore, it is impossible to calculate the expression at this time! (X % 2), the expression value must have been calculated in advance and exist in a temporary variable. Then the function exits and the local variable is released, the IF statement determines the value of this temporary variable. Note: although the return value of a function can be considered as a temporary variable, we only read its value and release it after reading the value, instead of storing New Values in it.
14. In the case of multi-layer loops or switch nesting, the break can only jump out of the innermost loop or switch, and the continue can only terminate the innermost loop and return to the beginning of the loop.
15. generally, the GOTO statement is only used in this scenario, if an error condition occurs anywhere in a function, you can immediately jump to the end of the function to handle the error (for example, releasing the previously allocated resources and recovering the previously modified global variables ), after processing, the function returns. The only restriction is that GOTO can only jump to a certain number in the same function, but not to another function.
16. If the user input is valid (the input is indeed a number rather than other characters), scanf returns 1, indicating that a data is successfully read.
17. generally, if the char type is used to store ASCII characters, it is not necessary to specify whether it is signed or unsigned. If the char type is used to represent an eight-digit integer, for portability, you must specify whether it is signed or unsigned. Note that if the signed or unsigned keyword is not explicitly written for other types except the char type, it indicates signed, which is clearly defined by the C standard, not Implementation defined. In addition to the explicit specification of char type in the C standard, other integers occupy several bytes of Implementation defined. The general compiler implementation complies with the ilp32 or lp64 specifications. The ilp32 stands for int (I), long (l), and pointer (p) types, generally, the C compiler of a 32-bit computer adopts this specification, as does the GCC on the X86 platform. Lp64 indicates that long (L) and pointer occupy 64 bits. Generally, the C compiler of 64-bit computers adopts this specification. The length of the pointer type is always the same as the number of digits in the computer.
18. implementation-defined, unspecified, or undefined is used in areas not clearly defined in the C standard. In this book, these three situations are sometimes referred to as "not clearly defined. What are the differences between these three situations? We just saw an implementation-defined situation. The C standard does not explicitly specify whether char is signed or unsigned, but requires the compiler to clearly define this, and written in the compiler documentation. For unspecified, there are usually several optional processing methods. The C standard does not specify which method to process. The compiler can decide on its own, and it does not have to be written in the compiler documentation, in this way, different results may be obtained even if different versions of the same compiler are used for compilation, because the compiler does not explicitly write what it will do in the document, the compiler of different versions can select different processing methods. For example, in the next chapter, we will talk about the order in which each real parameter expression of a function call is evaluated as unspecified. The undefined condition is completely uncertain. The C standard does not stipulate how to handle it, and the compiler may not specify it or even handle errors, many undefined cases cannot be checked by the compiler, which will eventually lead to runtime errors. For example, if the array access is out of bounds, it will be undefined.
19. type conversions that occur during function call and return are often overlooked because function prototypes and function calls are not written together. For example, char c = getchar (); The returned value of getchar is char type. In fact, the returned value of getchar is int type, in this way, the value assignment will cause type conversion and may cause bugs.
20. Due to the type conversion and shift problems, it is inconvenient to use the number of signed bits for bitwise operations. Therefore, we recommend that you only perform bitwise operations on the number of unsigned bits to reduce the possibility of errors.
21. for header files contained in parentheses, GCC first looks for the directory specified by the-I option, and then finds the system's header file directory (usually/usr/include, my system also includes/usr/lib/GCC/i486-linux-gnu/4.3.2/include); for header files included in quotation marks, GCC first looks for header files that contain the header file. the directory where the c file is located, then find the directory specified by-I option, and then find the system's header file directory.
Assume that all three code files are in the current directory: $ tree. | -- Main. c | -- stack. c' -- stack. h0 directories. For 3 files, you can use gcc-C main. c compilation, GCC automatically in main. find the stack in the directory where C is located. h. Assume that the stack. h move to a sub-Directory: $ tree. | -- Main. c' -- stack | -- stack. c' -- stack. h1 directory, 3 files requires gcc-C main. c-istack compilation. Use the-I option to tell the GCC header file to be found in the subdirectory stack. You can use relative paths in the # include pre-processing instructions. For example, you can change the above Code to # include "stack/stack. H ", you do not need to add the-istack option during compilation, because GCC will automatically. C is located in the directory, and the header file is relative to main. the relative path of directory c is stack/stack. h.
22. Repeated inclusion of header files has the following problems: 1. First, the preprocessing speed is slowed down and many header files that do not need to be processed. 2. second, if there is foo. h contains bar. h, bar. H also contains Foo. in the case of H, the pre-processor will be in an endless loop (in fact, the compiler will set a maximum number of layers ). 3. third, some code in the header file cannot be repeated. Although variables and functions can be declared multiple times (as long as they are not defined multiple times ), however, some code in the header file is not allowed to appear many times, such as the typedef type definition and struct tag definition. It can only appear once in a program file.
23. we set the stack. c. Push. c. Pop. c. Compile is_empty.c into the target file: $ gcc-C stack/stack. c stack/push. c stack/pop. c stack/is_empty.c is then packaged into a static library libstack. a: $ ar Rs libstack. A stack. O push. O pop. O is_empty.oar: Creating libstack. the file names of library a start with Lib, and static libraries start. A is used as the suffix to indicate archive. The ar command is similar to the tar command and serves as a package. However, you can only use the AR command instead of the tar command to package the target file into a static library. Option R: Add the following file list to the package. If the package does not exist, create it. If the package already contains a file with the same name, replace it with the new one. S is used to generate a static database, which indicates creating an index for the static database. This index is used by the linker. The ranlib command can also create an index for a static library. The preceding command is equivalent to: $ ar R libstack. A stack. O push. O pop. O is_empty.o $ ranlib libstack.
Then we set libstack. A and Main. c compilation links: $ GCC main. c-l. -lstack-istack-O main-L option tells the compiler where to find the required library file,-l. indicates to find in the current directory. -Lstack tells the compiler to connect to the libstack library, and the-I option tells the compiler where to find the header file. Note: even if the library file is in the current directory, the compiler will not find it by default, so the-L. Option cannot be fewer. You can use the-print-search-dirs option to view the default directory that the compiler will find. Libraries indicates the search path list of the library file, which is separated by. The compiler searches for libraries specified by the-L option in these search paths and the path specified by the-L option, for example,-lstack. The Compiler first checks whether the shared library libstack exists. so, if there is a link to it, if not, find whether there is a static library libstack. a. link it if any. Therefore, the compiler gives priority to shared libraries. If you want the compiler to only link to static libraries, you can specify the-static option. What is the difference between a linked shared library and a linked static library? When you connect to the libc shared library, you only specify the dynamic linker and the library files required by the program, and there is no real link, the libc library function called in the main of the executable file is still undefined. Dynamic Links should be made at runtime.
Interestingly, Main. C only calls the push function, so only push and is_empty are available in the executable file generated by the link. This is a benefit of using a static library. The linker can extract only the required parts from the static library for link. If the target file and Main. c compilation links: $ GCC main. c stack. O push. O pop. functions that are not used by O is_empty.o-istack-O main will also be linked. Of course, another advantage is that you only need to write a library file name for a static library, instead of a long string of target file names.
24. in C language, suffix operators such as struct are used to obtain members., pointer access->, array subscript [], function call () has the highest priority. The priority of a single object operator is second only to that of a suffix operator, and is higher than that of other operators. most operators are left-bound. Value assignment operators, conditional judgment operators, and single-object operators are right-bound.
25. when the array type is used as the right value, it is automatically converted to a pointer pointing to the first element of the array. This also explains why the array type cannot be assigned or initialized to each other. The Compiler reports an error: incompatible types in assignment, but the left value still indicates the storage space of the entire array, rather than the storage space of the first element. There is a special difference in the left value of the array name, it does not support ++ and value assignment operators, but supports the get address operator, so & A is legal.
26. the array element can be accessed by adding an array name to the lower mark, and the string literal value can also be used like the array name. The character in the string can be accessed by adding a subscript: Char c = "Hello, world. \ n "[0]; however, it is not allowed to modify the characters by Subscript:" Hello, world. \ n "[0] = 'a'; this line of code produces a compilation error, saying that the string literal value is read-only and cannot be modified. The literal value of a string is similar to the array name. When the right value is used, it is automatically converted to a pointer pointing to the first element. This pointer should be of the const char * type. We know that the first parameter of the printf function prototype is the const char * type. You can pass the char * or const char * pointer to it.
If you want to define a pointer pointing to the string literal value, this pointer should be of the const char * type. If it is written as char * P = "ABCD";, it is not good and there are risks, such:
Int main (void) {char * P = "ABCD";... * P = 'a ';...}
P points to the. rodata segment. Rewriting is not allowed, but the compiler does not report errors. A segment error occurs during runtime.
27. This can be regarded as a rule. If the access to a local variable of a function is out of bounds, it may not produce a segment error immediately, but a segment error occurs when the function returns.
30. We know that the standard prototype of the main function should be int main (INT argc, char * argv []);. Argc is the number of command line parameters. Argv is a pointer to a pointer. Why is it not a pointer array? As mentioned above, [] in the function prototype indicates a pointer instead of an array, which is equivalent to Char ** argv. So why should we write char * argv [] instead of char ** argv? In this way, the code reader provides useful information. argv points to the first element of a pointer array instead of a single pointer. Each element in the array is a char
* Pointer, pointing to a command line parameter string.
31. # include <stdio. h> void say_hello (const char * Str) {printf ("Hello % s \ n", STR);} int main (void) {void (* F) (const char *) = say_hello; F ("guys"); Return 0;} analyze the type declaration void (* f) (const char *) of the variable F *), f is first combined with the * sign, so it is a pointer. (* F) The outside is a function prototype format. The parameter is const char * and the return value is void. Therefore, F is a pointer to this function. The parameter of say_hello is const char *, and the return value is void, which is the same as this function. Therefore, F can point to say_hello. Note that say_hello is a function type, and the function type is similar to the array type. When using the right value, it is automatically converted to the function pointer type, so it can be directly assigned to F, of course, you can also write it as void (* f) (const char *) = & say_hello;. The function say_hello takes the address and then assigns it to F without automatic type conversion. You can call a function directly through the function pointer. For example, F ("guys") above, you can also use * F to retrieve the function type it refers to and then call the function, that is, (* F) ("guys "). It can be understood that the function call operator () requires that the operand be a function pointer, so f ("guys ")
Is the most direct method, while say_hello ("guys") or (* f) ("guys") is to automatically convert the function type into a function pointer and then call the function.