1 Type conversions
When an arithmetic operation is performed, the type of the operand, if different, is converted, and the data type is generally converted to a higher floating-point precision and longer-length direction, and the integer type is converted to signed if the conversion to signed does not lose information, otherwise it is converted to unsigned.
K&r C has been used in the original version, that is, when an unsigned type is mixed with an int or a smaller integer, the result type is an unsigned type.
2 The const in the C language does not really represent constants.
3 Disadvantages of the switch statement
1) The biggest disadvantage of the switch statement is that it does not terminate automatically after the statement that follows each case label is executed.
2) because the break statement actually jumps out of the most recent layer of the loop or switch statement, break may cause the switch statement to jump out of the end prematurely.
4 symbol overloading in the C language
| Symbol |
Significance |
| Static |
Inside the function, the value representing the variable remains constant across the calls At the level of the function, it means that the function is visible only to this file |
| extern |
Used for function definitions, indicating global visibility (redundant) Used for variables that indicate that it is defined elsewhere |
| void |
As the return type of the function, indicating that no value is returned In a pointer declaration, a type that represents a generic pointer In the parameter list, indicating no parameters |
| * |
Multiplication operator For pointers, indirect references In a declaration, represents the pointer |
| & |
Bitwise AND operator Fetch address operator |
| = |
Assignment character |
| == |
Comparison operators |
<= <<= |
Less operator Left-shift compound assignment operator |
| < |
Less operator #include指令的左定界符 |
| () |
In the function definition, the enclosing form parameter table Call a function Change the order of operations of an expression Convert a value to a different type (coercion type conversion) Define a macro with parameters Operand enclosing the sizeof operator (if it is a type name) |
5 priority in the C language
| Priority issues |
An expression |
The result that people may mistakenly think |
Actual results |
. The priority is higher than *. operator To eliminate this problem |
*p.f |
P refers to the field F (*p) of the object. F |
P takes an F offset, as an lvalue, and then Dereference operation. * (P.F) |
| [] above * |
int *ap[] |
The AP is a pointer to an int array int (*AP) [] |
The AP is an array of an int lvalue with an element int * (ap[]) |
| function () above * |
int *FP () |
The FP is a function pointer, and the function returns Int,int (*FP) () |
FP is a function that returns int*,int* (FP ()) |
| = = and! = above the bitwise operator |
(val&mask!=0) |
(Val&mask)!=0 |
val& (mask!=0) |
| = = and! = above the value of the assignment |
C=getchar ()!=eof |
(C=getchar ())!=eof |
C= (GetChar ()!=eof) |
| Arithmetic operators higher than shift operators |
Msb<<4+lsb |
(msb<<4) +LSB |
msb<< (4+LSB) |
| The comma operator has the lowest precedence in all operators |
i=1,2 |
I= (ON) |
(I=1), 2 |
The binding is used only in expressions where more than two operators of the same precedence are present, to disambiguate. In fact, you'll notice all the same operators that have the same precedence, and they all have the same binding character.
6 Returns a pointer to a local object
char * localized_time (char * filename) { struct TM *TM_PTR; struct stat Stat_block; char buffer[120 ]; Stat (filename, &stat_block); Tm_ptr =localtime (&stat_block.st_mtime); Strftime (buffer, sizeof (Buffer, " %a%b%e%T%Y ,TM_PTR); return buffer;}
The problem is the last line, which is the row that returns to buffer. Buffer is an array of automatically allocated memory, which is a local variable of the function. When the control flow leaves the scope of declaring an automatic variable (that is, a local variable), the automatic variable is invalidated. This means that even if a pointer to a local variable is returned, at the end of the function, because the variable has been destroyed, no one knows what the content of the address pointed to by the pointer is.
In the C language, automatic variables allocate memory on the stack. When functions or blocks of code that contain automatic variables are exited, the memory they occupy is reclaimed, and their contents are bound to be overwritten by the next called function. It all depends on where the previous automatic variable is located in the stack, what variables the activity function declares, what content is written, and so on. The contents of the original auto variable address may be overwritten immediately or later.
There are several scenarios for solving this problem:
1 Returns a pointer to a string constant. For example:
char* func () {return "only works for simple Strings";}
2 An array that uses a global declaration. For example:
Char *fun () {
...
My_global_array[i] =
...
return my_global_array;
}
This applies to the case where you create a string, and it's easy to use. Its disadvantage is that it is possible for anyone to modify the global array at any time, and the next invocation of the function will overwrite the contents of the array.
3 use a static array. For example:
char * func ()
{
static Char buffer[20];
...
return buffer;
}
This will prevent anyone from modifying this array. Only a function that has a pointer to the array can modify this static array. However, the next invocation of the function overwrites the contents of the array, so the caller must use or back up the contents of the array before this. As with global arrays, large buffers are a waste of memory if idle.
4 explicitly allocates some memory, saving the returned value. For example:
char* func () {
Char *s=malloc (120);
...
return s;
}
This method has the advantage of a static array and creates a new buffer each time it is called, so subsequent calls to the function do not overwrite the previous return value. It is suitable for multithreaded code. The downside is that programmers have to assume responsibility for memory management. Depending on the complexity of the program, this task can be easy or complex. If the memory is still in use and a "Memory leak" (memory that is no longer in use) is released, an incredible bug is created.
5 The best solution to use is to require the caller to allocate memory to hold the function's return value. In order to improve security callers should specify the size of the buffer at the same time.
void func (char* result,int size) {
...
strncpy (result, "that ' t is in the data Segment,bob", size);
}
Buffer=malloc (size);
Func (buffer,size);
...
Free (buffer);
Memory management is easiest if programmers can perform both "malloc" and "free" operations in the same block of code. This can be achieved with this solution.
7 How the declaration was formed
Unlawful statement:
- The return value of a function cannot be a function, so it is illegal to like Foo () ().
- The return value of a function cannot be an array, so it is illegal to like Foo () [].
- There is no function in the array, so it is illegal like foo[] ().
Legal statement:
- The return value of the function is allowed to be a function pointer, such as: Int (*fun ()));
- The return value of the function allows a pointer to an array, such as: Int (*foo ()) [];
- A function pointer is allowed in the array, such as int (*foo[]) ()
- There are other arrays allowed in the array, so you often see int foo[][]
8 Use of typedef
The difference between typedef and define:
First, the macro type name can be extended with other type specifiers, but not for the type name defined by the typedef. As shown below:
#define PEACH INT
unsigned peach i; No problem
typedef int Banana;
unsigned banana i; Error! Illegal
Second, in declarations of successive variables, a type defined with a typedef guarantees that all variables in the declaration are of the same type, while types defined with # define cannot be guaranteed. As shown below:
#define INT_PTR int *;
INT_PTR Chalk,cheese;
After macro expansion, the second line becomes:
int * Chalk,cheese;
This makes chalk and cheese a different type: chalk is a pointer to int, and cheese is an int. Instead, in the following code:
typedef char* CHAR_PTR;
Char_ptr Bentley,rolls_royce;
The types of Bentley and Rolls_royce remain the same. Although the previous type names have changed, they are of the same type and are pointers to char.
// The following two declarations have a similar form structint// statement 1structint weight,price_per_lb; } veg; // Statement 2
But they represent a completely different meaning, statement 1 declares the structural expression "fruit" and the structure type "fruit" declared by TypeDef, with the actual effect as follows:
struct fruit mandarin; Using structural labels Fruit
Fruit Mandarin; Using struct type fruit
Statement 2 declares the structure label veg and the variable veg, only the structure label can be used in a later declaration, such as
struct veg potato;
If you try to use a veg cabbage such a declaration, it will be an error. This is somewhat similar to the following wording:
int i;
I J;
9 differences between declarations and definitions
Remember that the C language object must have and have only one definition, but it can have multiple extern declarations.
A definition is a special declaration that creates an object; The declaration simply describes the name of the object created elsewhere, and it allows you to use that name.
The definition can only appear in one place to determine the type of object and allocate memory for the object to be created. For example, int my_array[100];
A declaration can appear more than once to describe the type of object used to refer to an object defined elsewhere (for example, in another file): extern int my_array[];
Differentiate definitions and declarations
Just remember the following to distinguish between definitions and declarations:
A declaration is equivalent to a common declaration: It declares not itself, but rather describes the objects created elsewhere.
The definition is equivalent to a special declaration: It allocates memory for an object.
The extern object declaration tells the compiler the type and name of the object, and the memory allocation for the object is elsewhere. There is no need to provide information about the length of the array, since memory is not allocated to arrays in the declaration. For multidimensional arrays, you need to provide the length of the dimension other than the leftmost dimension-this gives the compiler enough information to generate the appropriate code.
10 access to arrays and pointers
The C language introduces the term "modifiable lvalue", which indicates that an lvalue is allowed to appear to the left of an assignment statement, a strange term that is distinguished from the array name, which is also used to determine the position of the object in memory and an lvalue, but it cannot be an object to assign. Therefore, the array name is an lvalue, but not a modifiable left-hand value.
Lvalue: The symbol that appears to the left of the assignment is sometimes referred to as the left value.
Right value: The symbol that appears to the right of the assignment is sometimes called the right value.
The compiler assigns an address (lvalue) to each variable. This address is known at compile time, and the variable is stored at this address at run time. Conversely, the value stored in the variable (its right value) is known only at run time. If a value stored in a variable is needed, the compiler reads the value of the variable from the specified address and stores it in the register.
The key point here is that the address of each symbol is known at compile time. So, if the compiler needs an address (which may also need to be offset) to do something, it can do it directly, without requiring an additional instruction to get the exact address first. Conversely, for pointers, you must first get its current value at run time before you can dereference it (as one of the steps to find later).
Conversely, if you declare extern char *p, it tells the compiler that p is a pointer to an object that is a character. In order to obtain this character, it is necessary to get the content of the address p, and take it as the address of the character and obtain the character from this address. Pointers are much more flexible to access, but require an additional extraction:
11 other differences between arrays and pointers
Another way to compare arrays and pointers is to compare the characteristics of both.
The difference between an array and a pointer
| Pointer |
Array |
The address where the data is saved |
Save data |
To access data indirectly, first obtain the contents of the pointer, take it as an address, and then extract the data from this address. If the pointer has a subscript [i], add the contents of the pointer to I as the address from which the data is extracted |
Direct access, A[i] simply get the data from the A+i for the address |
| Typically used for dynamic data structures |
Typically used to store a fixed number of elements with the same data type |
| The associated function is malloc () free () |
Implicit allocation and deletion |
| Usually points to anonymous data |
is the data name itself |
When you define a pointer, the compiler does not allocate space to the object that the pointer points to, it simply allocates the space of the pointer itself, unless the pointer is assigned a string constant to initialize when defined. For example, the following definition creates a string constant with memory assigned to it:
Char *p= "breadfruit";
Note that this is true only for string constants. You cannot expect to allocate space for constants such as floating-point numbers, such as:
float *pip=3.141; Error
The string constants created when the pointer is initialized are defined as read-only. If you attempt to modify the value of this string through a pointer, the program will have undefined behavior.
Arrays can also be initialized with string constants:
Char a[]= "gooseberry";
As opposed to pointers, arrays initialized by string constants can be modified.
C Expert Programming Summary