Function and program structure of the strongest move

1. Write in front

Previously, we learned to process data and manage simple processes, but the program can't have only one method. In this way, the program is more chaotic, so we need to split and combine the structure of the program accordingly, so it's easier to maintain.

2. Basic knowledge of function

Let's write a program that contains a specific string, or "print" it first.

We can divide this program into three parts instead of putting it into a whole, so that the scalability of the program will be high.

It can be roughly divided into three parts, which can be implemented with the function getline. There are still unprocessed lines. Print this line with printf function. Then you need a function to determine whether the line change contains the specified pattern.

The specific codes are as follows:

#include <stdio.h>

#define MAXLINE 1000

int getLine(char line[], int max);

int strIndex(char source[], char searchfor[]);

char pattern[] = "ould";

main() {
    char line[MAXLINE];
    int found = 0;
    while (getLine(line, MAXLINE) > 0)
        if (strIndex(line, pattern) >= 0) { //If you find a match, print it directly
            printf("%s", line);
            found++;
        }
    return found;
}

//Access what we enter
int getLine(char s[], int lim) {
    int c, i;
    i = 0;
    while (--lim > 0 && (c = getchar()) != EOF && c != '\n')
        s[i++] = c;
    if (c == '\n')
        s[i++] = c;
    s[i] = '\0';
    return i;
}

// Match the value we entered, and return the corresponding function directly if found
int strIndex(char s[], char t[]) {
    int i, j, k;
    for (i = 0; s[i] != '\0'; i++) {
        for (j = i, k = 0; t[k] != '\0' && s[j] == t[k]; j++, k++);
        if (k > 0 && t[k] == '\0')
            return i;
    }
    return -1;
}

The above program is to complete the program we need to write. It is basically a function to do one thing. No matter how the later requirements change, the program only needs to modify a little part. If the program has only one main method, it is also difficult to modify.

3. Functions that return non integer values

So far, we have used functions that return int types. Can we use other types of return values. Of course, we can see the following code:

#include <ctype.h>

double atof(char s[]) {
    double val, power;
    int i, sign;
    // Handle leading spaces
    for (i = 0; isspace(s[i]); i++);
    // Is the interpretation negative
    sign = (s[i] == '-') ? -1 : 1;
    if (s[i] == '+' || s[i] == '-')
        i++;
    for (val = 0.0; isdigit(s[i]); i++)
        val = 10.0 * val + (s[i] - '0');
    if (s[i] == '.')
        i++;
    for (power = 1.0; isdigit(s[i]); i++) {
        val = 10.0 * val + (s[i] - '0');
        power *= 10;
    }
    return sign * val / power;
}

Note: if a function is used directly without declaration, its return value is int by default. So the best function is to define it first and then use it. Otherwise, the compiler check will fail.

Format of function:

Return value type function name(Parameter declaration table){
Declarations and statements
}

4. External variables

C language program can be regarded as composed of a series of external objects, which may be variables or functions. The adjective external is opposite to internal. Internal is used to describe the function parameters and variables defined inside the function. External variables are defined outside functions, so they can be used in many functions.

Sometimes when we need to share some variables, we need to define the variables as global, which can reduce the parameters of our function, but it will also bring the problem of concurrency. Without sharing, there will be no harm.

Let's look at a calculator program with the functions of addition (+), subtraction (-), multiplication (*) and division (/). Let's first look at the inverse Polish notation

(1-2)*(4+5) The inverse Polish is expressed as 1 2 - 4 5 + *

The implementation of calculator program is very simple. Each operand is pushed into the stack at one time; When an operator arrives, the corresponding number of operands are popped from the stack, the operator is applied to the popped operands, and the result of the operation is pushed into the stack.

In fact, it is easy to convert the above code into code. If the program is placed in a source file, the specific form is as follows:

#include ...
#define ...
main(){...}
void push(double f){...}
double pop(void){...}
int getop(char s []){...}

Let's first look at the main method. The specific implementation is as follows:

#include <stdio.h>
#include <stdlib.h>  /* for  atof() */

#define MAXOP   100  /* max size of operand or operator */
#define NUMBER  '0'  /* signal that a number was found */

int getop(char []);

void push(double);

double pop(void);

/* reverse Polish calculator */
main() {
    int type;
    double op2;
    char s[MAXOP];
    while ((type = getop(s)) != EOF) {
        switch (type) {
            case NUMBER:
                push(atof(s));
                break;
            case '+':
                push(pop() + pop());
                break;
            case '*':
                push(pop() * pop());
                break;
            case '-':
                op2 = pop();
                push(pop() - op2);
                break;
            case '/':
                op2 = pop();
                if (op2 != 0.0)
                    push(pop() / op2);
                else
                    printf("error: zero divisor\n");
                break;
            case '\n':
                printf("\t%.8g\n", pop());
                break;
            default:
                printf("error: unknown command %s\n", s);
                break;
        }
    }
    return 0;
}

Since the + and * operators satisfy the commutative law, the number of times the operands pop up here is irrelevant. However, the left and right operands of the - and / operators must be distinguished, so we pop it into a temporary variable here.

Let's take another look at the two methods of push and pop. The specific code is as follows:

#define MAXVAL  100  /* maximum depth of val stack */
int sp = 0;          /* next free stack position */
double val[MAXVAL];  /* value stack */
/* push:  push f onto value stack */
void push(double f) {
    if (sp < MAXVAL)
        val[sp++] = f;
    else
        printf("error: stack full, can't push %g\n", f);
}

/* pop:  pop and return top value from stack */
double pop(void) {
    if (sp > 0)
        return val[--sp];
    else {
        printf("error: stack empty\n");
        return 0.0;
    }
}

Since we need to share a stack here, we use external variables here. Therefore, we define the stack and stack top pointer that push and pop functions must share outside the two functions.

Finally, let's look at the implementation of getop function. Change the function to obtain the next operation or operand. The specific code is as follows:

#include <ctype.h>

int getch(void);

void ungetch(int);

/* getop:  get next character or numeric operand */
int getop(char s[]) {
    int i, c;
    while ((s[0] = c = getch()) == ' ' || c == '\t');
    s[1] = '\0';
    if (!isdigit(c) && c != '.')
        return c;      /* not a number */
    i = 0;
    if (isdigit(c))    /* collect integer part */
        while (isdigit(s[++i] = c = getch()));
    if (c == '.')      /* collect fraction part */
        while (isdigit(s[++i] = c = getch()));
    s[i] = '\0';
    if (c != EOF)
        ungetch(c);
    return NUMBER;
}

The above code just needs to skip spaces and tabs. If the next character is not a NUMBER or decimal point, return; Otherwise, collect these numeric strings and return NUMBER to identify that the NUMBER has been collected.

The getch function is used to read in the next character to be processed, while the ungetch function is used to put the character back into the input. In this way, when calling the getch function later, the character put back by the ungetch function is returned before reading in the new input.

The previous cooperation between the two functions is also very simple. The ungetch function puts the characters to be pressed back into a shared buffer. When the buffer is not empty, the getch function reads the characters from the buffer; When the buffer is empty, the getch function calls the getchar function to read characters directly from the input.

The specific codes are as follows:

#define BUFSIZE 100
char buf[BUFSIZE];    /* buffer for ungetch */
int bufp = 0;         /* next free position in buf */
int getch(void)  /* get a (possibly pushed-back) character */
{
    return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)   /* push character back on input */
{
    if (bufp >= BUFSIZE)
        printf("ungetch: too many characters\n");
    else
        buf[bufp++] = c;
}

5. Scope rules

When creating programs in C language, these programs may be distributed in multiple files, so we have the following problems.

  • How to declare variables to ensure that they are declared correctly at compile time?
  • How to arrange the position of the declaration to ensure that the parts of the program can be connected correctly when loading?
  • How to organize the statements in the procedure to ensure that there is only one copy?
  • How to initialize external variables?

The scope of the name refers to the part of the program that can use the name. For the automatic variable declared at the beginning of the function, its scope is the function that declares the change quantity name. Each local variable with the same name declared in different functions has no relationship before. The same is true for the parameters of the function. In fact, it can be regarded as a local variable.

The scope of an external variable or function starts at the place where it is declared and ends at the end of all its files.

Note: if you want to use the variable before the definition of the external variable, or the definition of the external variable is not in the same source file as the use of the variable, you must forcibly use the keyword extern in the corresponding variable declaration. External variables defined without extern directly open up the corresponding space, while variables defined with extern do not establish variables or allocate storage units for them.

6. Header file

We can divide the original program into five parts and put the main function main in the file main C, put the push and pop functions and the external variables they use in the second file stack C medium; Put the getop function in the third file getop C. Put the getch and ungetch functions in the fourth file getch C. At the same time, extract the public part and put it in calc.h. the details are as follows:

7. Static variables

Some variables are only used by functions in the source file where they are located, and other functions cannot access them. The variables and functions defined by static declaration can limit the scope of the objects declared later to the rest of the compiled source file.

8. Register variables

The register declaration tells the compiler that the variables it declares are used more frequently in the program. The idea is to put the register variable in the register of the machine, which can make the program smaller and execute faster.

The register is on the CPU and directly interacts with the CPU. Its efficiency is the highest, but its space is relatively small, so it stores some high-frequency variables.

9. Program block structure

Defining functions in functions is not allowed in C language. However, variables can be defined in a function in the form of a block structure. The declaration of a variable can follow not only the curly bracket at the beginning of the function, but also any other left curly bracket that identifies the beginning of a compound statement. Variables declared in this way can hide variables with the same name outside the program block, have no relationship between them, and exist until the right curly bracket matching the left curly bracket appears.

10. Initialization

Without display initialization, external variables and static variables will be initialized to 0, while the initial values of automatic variables and register variables are not defined.

For external variables and static variables, the initialization expression must be a constant expression and only initialized once.

Each time a variable or a function block is initialized, it is automatically initialized. The expression can not be a constant expression. The expression can contain any value defined before the expression again, including function calls.

11. Recursion

Functions can call themselves directly or indirectly. We can see the following code, an example of quick sorting, as follows:

void swap(int v[], int i, int j);

/* qsort:  sort v[left]...v[right] into increasing order */
void qsort(int v[], int left, int right) {
    int i, last;
    if (left >= right) /* do nothing if array contains */
        return;        /* fewer than two elements */
    swap(v, left, (left + right) / 2); /* move partition elem */
    last = left;                     /* to v[0] */
    for (i = left + 1; i <= right; i++)  /* partition */
        if (v[i] < v[left])
            swap(v, ++last, i);
    swap(v, left, last);            /* restore partition  elem */
    qsort(v, left, last - 1);
    qsort(v, last + 1, right);
}

/* swap:  interchange v[i] and v[j] */
void swap(int v[], int i, int j) {
    int temp;
    temp = v[i];
    v[i] = v[j];
    v[j] = temp;
}

Given an array, select an element from it, and divide the other elements into two subsets with the element as the boundary. All elements in one subset are smaller than the element, and all elements in the other subset are greater than or equal to the element. This process is performed recursively for such two subsets. When the number of elements in a subset is less than 2, the subset does not need to be sorted again and the recursion is terminated.

12.C preprocessor

12.1 documents include

#include "file name"
#Include < file name >

If the file name is enclosed in quotation marks, look for the file where the source file is located. If the file is not found in this location, or if the file name is enclosed by angle brackets < and >, the file will be found according to the response rule, which is related to the specific implementation.

12.2 macro replacement

#define name replace text

This is the simplest macro replacement, and all subsequent occurrences of nametags will be replaced with replacement text.

Let's start with the following examples:

The replaced text can be arbitrary, as follows:

#define forever for(;;)

The above example is mainly that for() can be replaced by forever; 😉

#define  max(A, B)  ((A) > (B) ? (A) : (B))
use
x=max(p+q,r+s);
replace with
x = ((p+q) > (r+s) ? (p+q) : (r+s));
#define  dprint(expr)   printf(#expr " = %g\n", expr)
use
dprint(x/y)
replace with
printf("x/y = &g\n", x/y);

12.3 conditions include

Conditional statements control the preprocessing itself. The value of this conditional statement is calculated during the execution of preprocessing. This method provides a means for selectively including different codes according to the calculated Tianjian value in the compilation process.

The format is as follows:

#if expression
#enif expression
#else expression
#endif

The above condition contains that as long as the expression is true, it only corresponds to the following statements.

13. Write at the end

This blog mainly introduces the structure of functions and programs. The next section is the core content of C language, including the pointer of C language.

Keywords: C C++

Added by AutomatikStudio on Sat, 12 Feb 2022 06:47:30 +0200