Study notes on C and Pointers

  • 2020-04-01 21:39:35
  • OfStack

With the previous foundation, this article is just a record of some previously unnoticed and worth learning knowledge.

The first chapter

The authors argue that using #if 0... #endif is better because it cannot be nested. But there is no explanation for //.

The second chapter

A three-letter word that USES two question marks and one symbol for another, more like an escape character. Looked up some information, its use and compiler, understand can be, to prevent the string constant is wrong interpretation.


??( ==> [   ??< ==> {   ??= ==> #  
??) ==> ]   ??> ==> }   ??/ ==>   
??! ==> |   ??' ==> ^    ??- ==> ~  

For deeply nested functions, the authors recommend breaking them up into several functions without indenting them too much.

Chapter iii data

For the complex use of static, when it is used for variable declarations outside of function definitions or code blocks, static is used to modify the connection property of the identifier from external to internal, but the storage type and scope of the identifier are not affected. Functions or variables declared in this way can only be accessed from the source file where they are declared. When it is used for variable declarations inside code blocks, static is used to change the storage type of a variable from an automatic variable to a static variable, but the link properties and scope of the variable are not affected. Variables declared in this way are created before the execution of the program and remain in place throughout the execution of the program, rather than being created each time the code block is executed and destroyed when it is finished.

 

Chapter 5 operators and expressions

Shift operations, when the number of bits moved is negative, the specific result is compiler dependent or undefined, such as a< < Minus 5 could be 27 to the left.

Shaped like a + = 1 operation efficiency is higher than a = a + 1, equivalent to a (2 * (y - 6 * f (x))) = a (2 * (y - 6 * f (x))] + 1 and a (2 * (y - 6 * f (x))] + = 1, compared to the latter without double counting subscript.

The form sizeof x is allowed. Sizeof () does not evaluate an expression, so there is no assignment to a in sizeof(a=b+1).

Access to members of Pointers to structures using only -> .

 

Chapter 6 pointer

An uninitialized pointer causes an error. Int * a; *a = 12, this causes the contents of the address to which a points to to be modified, with unpredictable results.

The authors argue that returning a NULL pointer when an element is not found, for example, is a common trick of C, but goes against the principle of software engineering: "it is dangerous to use a single value to represent two different meanings, because in the future it is easy to not know which is its true purpose." The safe policy is to return two values, the status value for success and the element value that was found when the lookup was successful.

 

Chapter 7 functions

Function prototype declaration with no arguments should be written like this: int func(void); The goal is not to be confused with old style declarations.

Recursive solutions are clearer than non-recursive solutions, and the simplicity of the recursive implementation compensates for the overhead of a complex problem that is difficult to implement iteratively. Fibonacci is a common example of recursion, but the redundancy computes a lot, is expensive, and is not actually as iterative as the implementation.


Fibonacci Iterative implementation of  
long fibonacci(int n)
{
    long result;
    long previous_result;
    long next_older_result;
    result = previous_result = 1;

    while( n > 2) {
       n -= 1;
       next_older_result = previous_result;
       previous_result = result;
       result = previous_result + next_older_result;
     }
    return result;
}

Use of variable-parameter lists: the header file stdarg.h declares a type va_list and three macros va_start, va_arg, and va_end. The value of the parameter is accessed by declaring a variable of type va_list in conjunction with these macros. The function declares a var_arg variable to access the undetermined part of the parameter list, which is initialized by va_start. The first parameter is the name of the va_list variable, and the second parameter is the last parameter with a name before the ellipsis. The initialization process points the var_arg variable to the first parameter in the variable parameters section. Va_arg takes two parameters: the type of the next parameter in the va_list and the parameter list. Va_arg returns the value of the parameter and causes var_arg to point to the next variable parameter. End of access calls va_end.

#include <stdarg.h>
float average( int n_values, ... )
{
    va_list var_arg;
    int count;
    float sum = 0;
    
    va_start( var_arg, n_values) ;
    
    for (count = 0; count < n_values; count += 1) {
        sum += va_arg( var_arg, int);
    }
    
    va_end(var_arg);
    return sum/n_values;
}

Variable-parameter macros do not determine the number of parameters and the type of parameters, and the latter may cause an increase in the default parameter type. The solution to both of these problems is to use named arguments, which is why there is always a named argument in the mutable argument list.

 

Chapter 8 arrays

An int array [10]. Int * ap = array + 2; After that, ap[0] is legal in C, which is equivalent to array[2], and ap[-1] is also legal, which is array[1].

Pointers are more efficient than arrays: the ap++ of the for loop is more efficient than the array[a] = 0 in the loop body, where the multiplication is performed only once for multiplying 1 by the length of the data type, whereas the latter is computed each time.



int array[10], a;
for ( a = 0 ; a< 10; a +=1 )
    array[a] = 0;

int array[10], *ap;
for ( ap = array ; ap< array + 10; ap ++ )
    *ap = 0;

Arrays, especially large arrays, can take a considerable amount of initialization time, so when the initialization of an array is partial to a function or block of code, consider whether it is worthwhile for the program to reinitialize it each time. If not, declare the array static.

For example, for the array int matrix[3][10], it is wrong to declare int *mp = matrix, because the matrix is not a pointer to an integer, but a pointer to an integer array. Int (*p)[10] = matrix is ok, and p points to the first row of the matrix to achieve line-by-line access to the array. If you need to access each one individually, use int * PI = &matrix[0][0] or int * PI = matrix[0] to point it to the first element. And int (*p)[] = matrix; Is incorrect, its value adjusts to the length of the empty array, an error that some compilers cannot catch. Function pass arguments are similar.

The multidimensional array is explicitly initialized so that only the first dimension can be inferred and the other dimensions cannot be omitted.

 

Chapter 9 strings, characters, and bytes

  Careful use of unsigned Numbers: strlen returns unsigned Numbers, so if(strlen(x) - strlen(y)> = 0)... Always true. In this case, it should be if(strlen(x)> = strlen (y))... Or cast it to an int.

Strtok holds local state information for the functions it handles, so it cannot be used to parse two strings at once.

When a string function encounters a NULL byte closure operation and wants to work with non-string data without this restriction, you can use another set of related functions: memcpy, memmove, memcmp, memchr, memset.

 

Chapter x structure and association

Functions with arguments of structure, passing a pointer is more efficient than calling a value, because the latter requires a copy of the structure. F (type_struct * s) {s - > X}; The call is f(&s). If members of this structure are accessed more than three times, it is more effective to declare as a register variable. To avoid inappropriate modifications, declare the parameter const and assign the return value to the original structure (or one of its members).

Bit segments are simply understood as a special structure that specifies the length of a member.

 

Chapter 13 advanced Pointers

The use of callbacks can solve problems similar to comparing unknown types of data, and this is the first time callbacks have been systematically recognized.

 

Chapter 14 preprocessor

To eliminate the danger of multiple inclusions, write the following in each header file:


#ifndef _HEADRNAME_H
#define _HEADRNAME_H 1

#endif

_HEADRNAME_H is defined as 1 the first time it is included, and is ignored the second time it is included. You can even write it as #define _HEADRNAME_H. But multiple containment should still be avoided if possible.

 

Chapter 15 input/output function

Freopen is used to open(or reopen) a particular FILE stream, prototype: FILE *freopen(char const *filename,char const *mode, FILE *stream), where the last parameter is the stream to be opened. It first tries to close the stream, then re-opens it with the specified file and pattern, returns NULL on failure, and returns a third parameter on success.

Ungetc returns the previously read character to the stream so that it can be read back later. There is an example of character processing in the C programming language that USES it. When fseek, fsetpos, or rewind changes the position of the stream, all returned characters are discarded.

The difference between gets and puts and fgets and fputs is that when gets reads a line of input, it does not store a newline at the end of the buffer, and when puts writes a string, it adds another newline to the output after the string is written. Also, gets doesn't judge the buffer length, which can be dangerous.

Feof determines whether the stream is at the end of the file, ferror reports the error state of the stream, and clearerr resets the error flag for the specified stream.

Tmpfile creates a temporary file to hold the data and is deleted at the end of the program. The name of the temporary file is created by tmpnam.

 

Chapter 16 standard function libraries

Volatile is a type modifier designed to modify variables that are accessed and modified by different threads, preventing the compiler from "optimizing" the program in a way that might modify the meaning of the program.

Vprintf, vfprintf, and vsprintf are used to print a variable-parameter list that functions like the corresponding prinft, but takes a variable-parameter list arg.

Getenv gets the environment variable and returns it with a pointer if found, otherwise returns NULL.

Locales are a set of specific parameters that may vary from country to country, with the aim of enhancing C's worldwide universality, not described in detail.

 

The classic abstract data types in chapter 17 and the runtime environment in chapter 18 are already familiar, while the latter is tightly integrated with assembly and is just a cursory glance at the book.


Related articles: