C language: Notes from introduction to advanced (full version)

Posted by dhie on Mon, 17 Jan 2022 18:05:43 +0100

Β Β 

Β 

The full text is about 20w words (errors, typographical problems or inaccuracies are inevitable in the first draft)

πŸ’­ preface

This series is suitable for readers who have been exposed to C language or have a basic understanding of C language. It is suitable for reviewing, consolidating and consolidating the foundation. There are 18 chapters in total. Each chapter is divided into several sections. Some chapters are equipped with supporting exercises, and this series comes with three sets of written C language test questions and detailed analysis of answers. The first chapter is displayed in the form of links due to the number of words. Due to the limited level of the author and the urgency of time, it is inevitable that there are errors and inaccuracies in this teaching. I also want to know these errors. I sincerely hope the readers can criticize and correct them! It took four months from the update of the first chapter to the last chapter of the blog. It is inevitable that there will be inconsistencies in typesetting, code style and icon application. Please understand. There are many expression packs in the article, which aims to reduce the boredom of readers in the reading process. Some expression packs can vividly remember some important knowledge points, but there are few expression packs in some chapters. If I have the opportunity, I will continue to improve this series of tutorials. This is the first release. There are still many deficiencies to be improved. Thank you for your support.

  πŸ“Œ This article is an integrated article, about 200000 words. Due to its large length, if you find it difficult to read it, if you want to selectively study and read, you can subscribe to the column—— Vitamin C language , you can select the corresponding chapter for viewing.

πŸ“œ References / materials

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

Lin Rui High quality C/C + + Programming Guide [M] 1.0. Electronic industry, July 24, 2001

Chen Zhengchong Deep anatomy of C language [M] Third edition Beijing University of Aeronautics and Astronautics Press, 2019

Yu Jiazi / Shi fan / PAN Aimin Self cultivation of programmers [M] Electronic Industry Press, 2009-4

Baidu Encyclopedia [EB / OL] []. https://baike.baidu.com/.

Bit technology Fundamentals of C language [EB / OL] 2021[2021.8.31]. .

Bit technology Advanced C language [EB / OL] 2021[2021.8.31]. .

Chapter 1 - getting to know C language

[simply go through the basic part of C language] all the knowledge points, so far! (only more than 10000 words)_# define _CRT_SECURE_NO_WARNINGS 1-CSDN blog just a few days ago, the column from C language introduction to advanced part - "vitamin C language" was finally completed. The full text consists of 18 chapters with three written test exercises. The drawback is that the first and second chapters are shown in the form of screenshots. Since I wrote the first draft on youdaoyun's notes at the beginning, I wanted to be convenient and easy (youdaoyun's typesetting was really beautiful), so I directly completed chapter I and Chapter II in the form of screenshots. I consider that because it is a screenshot, I can't copy the code in the text, which is inconvenient for readers to copy and paste, so I plan to rewrite the contents of Chapter 1 and Chapter 1, and rearrange the typesetting.https://blog.csdn.net/weixin_50502862/article/details/120265591

Chapter 2 - branches and loops

1, Statement

0x00 what is a statement

πŸ“š In C, a semicolon (;) Separated is a statement.

πŸ’¬ These are statements:

(there are only; statements in one line, which we call "empty statements")

int main(void) {
    printf("hello world!\n"); // sentence;
    3 + 5; // sentence;
    ; // Empty statement;
}

0x01 true and false

πŸ“š Definition: 0 is false, and non-0 is true (for example, 1 is true and 0 is false)

2, Branch statement

0x00 if statement

πŸ’¬ Single if statement demonstration:

int main(void) {
    int age = 0;
    scanf("%d", &age);
    
    if ( age >= 18 )
        printf("adult\n");
    
    return 0;
}

πŸ’¬ if...else Demo:

int main(void) {
    int age = 0;
    scanf("%d", &age);
    
    if ( age >= 18 )
        printf("adult\n");
    else
        printf("under age");
    
    return 0;
}

πŸ’¬ Multi branch presentation:

int main(void) {
    int age = 0;
    scanf("%d", &age);

    if(age<18) {
        printf("juvenile\n");
    }   
    else if(age>=18 && age<30) {
        printf("youth\n");
    }
    else if(age>=30 && age<50) {
        printf("middle age\n");
    } 
    else if(age>=50 && age<120) {
        printf("old age\n");
    } else {
        printf("Please enter the correct age\n");
    }
    
    return 0;
}

πŸ’¬ Judge whether a number is odd:

int main(void) {
    int n = 0;
    scanf("%d", &n);

    if(n % 2 == 0) {
        printf("Not odd\n");
    } else {
        printf("It's an odd number\n");
    }

    return 0;
}

0x01 code block

πŸ“š If the condition holds and multiple statements need to be executed, a code block should be used. A pair of braces is a code block.

πŸ’­ Suggestion: whether it is a single line statement or a multi line statement, it is recommended to add braces.

πŸ’¬ Hidden danger of not enlarging parentheses: hanging else

❓ Will the following code print abc?

int main(void) {
    int a = 0;
    int b = 2;
    if ( a == 1 )
        if ( b == 2 )
            printf("123\n");
    else
        printf("abc\n");
    
    return 0;
}

🚩 Running results: (nothing printed)

πŸ”‘ Parsing: since there are no braces and else is combined with the nearest if (i.e. internal if), it is useless even if else corresponds to the external if.

πŸ’¬ Revision: adding braces can make the logic of the code clearer!

int main(void) {
    int a = 0;
    int b = 2;
    if(a == 1) {
        if(b == 2) {
            printf("hehe\n");
        }
    } else {
        printf("haha\n");
    }

   return 0;   
}

🚩 Operation result: abc

0x02 code style

Code 1: poor readability, but saves space

Code 2: strong readability

Code 3: we hope that hello will not be printed, but in fact, it is printed;

int main()
{
    int num = 0;
    if ( num = 5 ) {
        printf("hello\n");  // =Assignment = = judge equality;
    } 
        
    return 0;
}

πŸ”‘ Analysis: why is this? Because in the if statement, num = 5 is equivalent to re assignment.

πŸ’¬ In order to prevent such a BUG from writing an equal sign into two equal signs, we can write as follows:

int main()
{
    int num = 0;
    if (5 == num) {
        printf("hehe\n");
    }
    
    return 0;
}

In this way, if you accidentally write "=" and can't run, you can easily find the problem. This is a good code style! In the future, if it involves comparing constants and variables to compare whether they are equal or not, we might as well put variables on the right of the double equal sign and constants on the left, so as to prevent accidentally typing an "=" less, resulting in program errors.

πŸ“š About return 0

int test() {
    if (1) {
        return 0; // When return 0 is executed, the following code will not be executed;
    }
    printf("hehe\n");
    return 1;
}

int main(void) {
    test();
    return 0;
}

0x04 switch statement

πŸ“š Introduction: switch statement is a multi branch statement, which is often used in multi branch situations. Composition of a standard switch statement:

β‘  case statement item: followed by constant expression (types can only be integer and enumeration types).

β‘‘ break statement: used to jump out of the switch statement. The actual effect is to divide the statement list into different parts.

β‘’ Default clause: a statement executed by default when all case s cannot match the value of switch.

πŸ“Œ matters needing attention:

1. Remember to add: (colon) after case and default instead of semicolon.

2. if statement can appear in switch statement.

3. switch must be followed by an integer constant expression.

4. There can only be one default after each switch statement.

5. It is not necessary to add default or not.

πŸ“œ Recommendations:

1. Add a break statement after the last case statement to prevent omission when adding statement items in the future.

2. It is recommended to add a default clause to each switch, or even add a break after it.

πŸ’¬ Demonstration of switch usage: the user enters a number x and returns the week (eg. 1 > > > Monday)

int main(void) {
    int day = 0;
    scanf("%d", &day);
    
    switch (day) {
        case 1:
            printf("Monday\n");
            break; // Jump out of switch
        case 2:
            printf("Tuesday\n");
            break;
        case 3:
            printf("Wednesday\n");
            break;
        case 4:
            printf("Thursday\n");
            break;
        case 5:
            printf("Friday\n");
            break;
        case 6:
            printf("Saturday\n");
            break;
        case 7:
            printf("Sunday\n");
            break;
        default: // Statements executed by default;
            break;
    }

    return 0;
}

πŸ’¬ Demonstration of the same result in multiple case s: input 1-5 and output working days; Input 6-7 and output rest days; Other numbers return error

int main(void) {
    int day = 0;
    scanf("%d", &day);
    
    switch (day) {
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
            printf("weekdays\n");
            break;
        case 6:
        case 7:
            printf("Rest Day\n");
            break; // Adding break at the end is a good habit;
        default:
            printf("Input error\n");
            break; // break may not be added here, but it is a good habit to add it;
    }
    return 0;
}

❓ What is the output value of the following code?

int main(void) {
    int n = 1;
    int m = 2;
    switch(n) {
        case 1: 
            m++;
        case 2: 
            n++;
        case 3:
            switch(n) {
                case 1: 
                    n++;
                case 2: 
                    m++;
                    n++;
                    break;
            }
        case 4: 
            m++;
            break;
        default:
            break;
    }
    printf("m = %d, n = %d\n", m, n);

    return 0;
}

πŸ’‘ Answer: m = 5, n = 3

πŸ”‘ Analysis: because n=1, execute the statement m + + of case1 after entering the switch. At this time, m=3. Since there is no break at the end of the statement item, continue to flow down to the statement n + + of case2. At this time, n=2, there is no break, and flow to case3. A switch(n) is embedded in case3. At this time, because n=2, execute the statements m + + and N + + of case2 of the internal switch. At this time, m=4, n=3, followed by break, and jump out of the internal switch, However, there is still no break behind case3 of the external switch, so it flows to case4, M + +. At this time, m=5, and there is finally a break behind. After running, the result is m=5, n=3.

3, Circular statement

0x00 while loop

πŸ“š Definition: enter the cycle when the conditions are met. After entering the cycle, jump out of the cycle when the conditions are not met.

πŸ“Œ Note: the while loop condition will be executed once more than the loop body.

In the while loop, the statements in the loop body will be executed only when the conditional expression is true. During each execution, the loop factor will be modified (otherwise it will become an endless loop). After the modification, if the while conditional expression is true, the loop will continue. If it is not true, the loop will end.

πŸ’¬ while loop: if the expression result is non-0 and true, the loop will be executed

int main(void) {
    while(1)
        printf("hehe\n");

    return 0;
}

🚩 The operation results are as follows:

πŸ’¬ while cycle printing numbers from 1 to 10:

int main(void) {
    int i = 1;
    while(i<=10) {
        printf("%d ", i);
        i++;
    }

    return 0;
}

🚩 Operation results: 1 2 3 4 5 6 7 8 9 10

0x01 break statement

πŸ“š Effect of break statement in while loop:

In a while loop, break is used to permanently terminate the loop.

int main(void) {
    int i = 1;
    while(i <= 10) {
        if(5 == i) // Stop the cycle when i=5;
            break;

        printf("%d ", i);
        i++;
    }

    return 0;
}

🚩 Operation results: 1 2 3 4

0x02 continue statement

πŸ“š continue statement:

int main()
{
    int i = 1;
    while(i<=10) {
        if(i==5) {
            continue;  // Skip to the judgment section;
        } 
        printf("%d ", i);
        i++;
    }
    
    return 0;
}

🚩 Operation result: 1 2 3 4 (the program will always judge)

0x03 getchar and putchar

πŸ“š getchar:

Read a character from a stream or keyboard.

Return value: if correct, return ASCII value; EOF (end of file flag) is returned in case of reading error.

πŸ“š putchar: simply output a character.

πŸ’¬ getchar uses the method to demonstrate: "return what you enter"

int main(void) {
    int ch = getchar();
    putchar(ch); // Output a character;

    return 0;
}

🚩 Operation result: (assuming input a) a

πŸ’¬ Combination of getchar and while: "a program that always reads characters from the keyboard"

int main(void) {
    int ch = 0;
    // ctrl+z - getchar ends reading;
    while ( (ch = getchar()) != EOF ) {
        putchar(ch);
    }

    return 0;
}

❓ What if you want to stop typing?

πŸ’‘ Solution: enter ctrl + z to make getchar end reading.

πŸ’¬ getchar prints only numbers:

int main(void) {
    int ch = 0;
    while( (ch=getchar()) != EOF ) {
        if(ch<'0' || ch>'9') {
            continue; // If it is not a number, jump back to the judgment part and re getchar;
        }
        putchar(ch);
    }

    return 0;
}

πŸ’¬ Clear buffer: after the user enters the password, let the user confirm (Y/N)

int main(void) {
    char password[20] = {0};
    printf("Please input a password:>");
    scanf("%s", password);
    printf("Please confirm the password(Y/N) :>");
    int ch = getchar();
    if(ch == 'Y') {
        printf("Confirmation successful\n");
    } else {
        printf("Confirmation failed\n");
    }

    return 0; 
}

🚩 Operation result: (assuming that the user has entered 123456; Y) confirmation failed

❓ Why is the confirmation failure displayed without user confirmation (Y/N)?

πŸ”‘ Parsing: the input function does not read from the keyboard, but from the buffer; When entering 123456 on the keyboard, press enter. At this time, it is "123456\n". At this time, scanf takes 123456 away and getchar reads "\ n". Because "\ n" is not Y and the result of else is executed, confirmation failure is displayed.

πŸ’‘ Solution: add a "read \ n" getchar() after scanf

int main(void) {
    char password[20] = {0};
    printf("Please input a password:>");
    scanf("%s", password);
    printf("Please confirm the password(Y/N) :>");
    // Clear buffer zone;
    getchar()
    
    int ch = getchar();
    if(ch == 'Y') {
        printf("Confirmation successful\n");
    } else {
        printf("Confirmation failed\n");
    }

    return 0; 
}

🚩 (assuming that the user has entered 123456; Y) confirmation is successful

🚩 (assuming the user entered 123 456; Y) confirmation failed

❓ "The user entered a space and confirmed Y. why did the confirmation fail?"

πŸ”‘ Parsing: a getchar() added just now handles spaces, resulting in no one taking care of the "\ n" behind it;

πŸ’‘ Solution: add loop

int main(void) {
    char password[20] = {0};
    printf("Please input a password:>");
    scanf("%s", password);
    printf("Please confirm the password(Y/N) :>");
    // Clear multiple characters in the buffer;
    int tmp = 0;
    while( (tmp = getchar()) != '\n' ) {
        ;
    }

    int ch = getchar();
    if(ch == 'Y') {
        printf("Confirmation successful\n");
    } else {
        printf("Confirmation failed\n");
    }

    return 0; 
}

🚩 (assuming that the user has entered 123 456; Y) confirmation is successful

0x04 for loop

πŸ“š definition:

β‘  Expression 1: initialization part, used to initialize loop variables.

β‘‘ Expression 2: condition judgment part, which is used to judge the termination of the loop.

β‘’ Expression 3: adjustment part, which is used to adjust the loop condition.

πŸ“Œ matters needing attention:

β‘  In order to prevent the for loop from losing control, it is forbidden to modify the loop variable in the for loop body.

β‘‘ The expression in the for loop can be omitted, but pay attention.

πŸ“œ Recommendations:

β‘  It is suggested to use the wording of "left closed interval, right open interval":

for( i=0; i<10; i++ ) Left closed, right open section βœ…

for( i=0; i<=9; i++ ) Both left and right are closed intervals ❎

β‘‘ Do not modify loop variables in the for loop body to prevent the for loop from losing control.

πŸ’¬ Demonstration of how to use for

β‘  Use while loop to print 1 ~ 10 numbers:

int main(void) {
    int i = 1; // initialization
    while(i<=10) { //Judgment part
        printf("%d ", i);
        i++; // Adjustment part
    }
    return 0;
} 

🚩 Operation results: 1 2 3 4 5 6 7 8 9 10

β‘‘ Use the for loop to print 1 ~ 10 numbers:

int main(void) {
    int i = 0;
    for(i=1; i<=10; i++) {
        printf("%d ", i);
    }
    return 0;
}

🚩 Operation results: 1 2 3 4 5 6 7 8 9 10

πŸ’¬ Effect of break statement in for loop:

int main(void) {
    int i = 0;
    for(i=1; i<=10; i++) {
        if(i==5) {    // When i==5;
            break;    // Jump out of the loop directly;
        }
        printf("%d ", i);
    }
}

🚩 Operation results: 1 2 3 4

❓ What didn't print 5?

πŸ”‘ Parsing: when i==5, break jumps out of the loop, and all statements after break in the loop are no longer executed. printf is after break, so 5 will not be printed naturally;

πŸ’¬ Effect of continue in for loop

continue in if will fall into an endless loop, but it will not:

int main(void) {
    int i = 0;
    for(i=1; i<=10; i++) {
        if(i == 5)
            continue; // Skip to the adjustment section (i + +);
        printf("%d ", i);
    }
}

🚩 Operation results: 1 2 3 4 5 6 7 8 9 10

❓ Why didn't you print 5 here?

πŸ”‘ Analysis: because when i==5, continue jumps to the adjustment part. At this time, i + + and i are 6. Same as above, so 5 will not be printed naturally. When i is 6, if does not hold, continue printing, and the final result is 1 2 3 4 6 7 8 9 10 (skipping the printing of 5);

πŸ’¬ Consequences of modifying loop variables in the for loop:

int main(void) {
    int i = 0;
    for (i=0; i<10; i++) {
        if (i = 5) {
            printf("haha\n");
        }
        printf("hehe\n");
    }

    return 0;
}

🚩   hehehahahehehaha…… πŸ’€ Dead cycle

0x05 nesting of for loops

πŸ“š definition:

β‘  for loops are allowed to be nested;

β‘‘ The external for loop is called the external loop, and the internal for loop is called the internal loop;

πŸ’¬ for nested presentation:

int main(void) {
    int i = 0;
    int j = 0;

    for (i=0; i<10; i++) {
        for (j=0; j<10; j++) {
            printf("hehe\n");
        }
    }
    // 10x10 == 100
    return 0;
}

🚩 (100 hehe printed)

0x06 omission of for loop

πŸ“š Omission of for loop:

β‘  The initialization, judgment and adjustment parts of the for loop can be omitted.

β‘‘ Omission of judgment part - judgment part is always true dead cycle πŸ’€.

β‘’ If you are not very skilled, it is recommended not to omit.

πŸ’¬ Omission of judgment part:

int main(void) {
    // The judgment part is always true dead cycle
    for(;;) {
        printf("hehe\n");
    }

    return 0;
}

🚩 hehehehehehe…… πŸ’€ Dead cycle

πŸ’¬ Disadvantages of omission

Suppose we want the following code to print 9 hehe:

int main(void) {
    int i = 0;
    int j = 0;
    for(; i<3; i++) {
        for(; j<3; j++) {
            printf("hehe\n");
        }
    }

    return 0;
}

🚩 Running result: hehe hehe (only 3 printed)

πŸ”‘ Analysis: because i=0, the internal for prints hehe three times. At this time, j=3. At this time, i + +, j is still 3 because it is not initialized. However, the judgment part requires J < 3. Naturally, it will not be printed. The program ends.

❓ How many times should I cycle?

int main(void) {
    int i = 0;
    int k = 0;
    int count = 0;
    for(i=0,k=0; k=0; i++,k++) {
        k++;
        count++;
    }
    printf("count: %d", count);
    
    return 0;
}

πŸ’‘ Answer: count = 0, a total of 0 cycles.

πŸ”‘ Analysis: the judgment part k=0. It is false when the value is 0, so it will not cycle once.

0x07 do...while loop

πŸ“š Definition: before checking whether the while() condition is true, the loop will first execute a statement within do {}, and then check whether the condition is true in while(). If the condition is true, it will repeat do While this loop until while() is false.

πŸ“Œ matters needing attention:

β‘  do... Characteristics of while loop: the loop body is executed at least once.

β‘‘ do...while has limited usage scenarios, so it is not often used.

β‘’ Simply put, whether the condition is true or not, execute a cycle first, and then judge whether the condition is correct.

πŸ’¬ do...while usage demonstration:

int main(void) {
    int i = 1;
    do {
        printf("%d ", i);
        i++;
    } while(i<=10);
    
    return 0;
}

🚩 Operation results: 1 2 3 4 5 6 7 8 9 10

πŸ’¬ break statement in do Effects in the while loop:

int main(void) {
    int i = 1;
    do {
        if(i==5) {
            break;
        }
        printf("%d ", i);
        i++;
    } while(i<10);
    
    return 0;
}

🚩 Operation results: 1 2 3 4

πŸ’¬ continue statement in do Effects in the while loop:

int main(void) {
    int i = 1;
    do {   
        if(i == 5)
            continue;
        printf("%d ", i);
        i++;
    }
    while(i<=10);
    
    return 0;
}

0x08 goto statement

πŸ“š C language provides goto statements that can be abused at will and labels that mark jump. The most common use is to terminate the processing of structures nested in some depth.

"goto statement is controversial"

1. goto statements are really harmful and should be avoided as much as possible.

2. In theory, goto statement is not necessary. In practice, it is easy to write code without goto statement.

3. It is not a wise way to completely avoid using goto statements. Using goto statements in some places will make the program flow clearer and more efficient.

πŸ“Œ Note: goto statements can only jump within one function.

πŸ’¬ Consider using goto:

for(...) {
    for(...) {
        for(...) {
            // HOW TO ESCAPE?
        }
    }
}

πŸ’¬ Understand the characteristics of goto statement:

int main(void) {
flag:
    printf("hehe\n");
    printf("haha\n");
    goto flag;

    return 0;
}

  🚩 hehehahahehehaha ( πŸ’€ Dead cycle)

πŸ’¬ goto actual combat: a shutdown program

Functions provided by C language for executing system commands: system()

Shutdown command: shutdown - s - t} 60 (shutdown after 60 seconds)

Cancel shutdown: shutdown - a

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void) {
    char input[20] = {0}; // Store the input information;
    system("shutdown -s -t 60"); // Shutdown command;
    printf("[System prompt] The computer will shut down in one minute (cancel command:/cancel) \n");
    
again:
    printf("C:\\Users\\Admin> ");
    scanf("%s", &input);
    if(strcmp(input, "/cancel") == 0) {
        system("shutdown -a"); // Cancel shutdown;
        printf("[System prompt] Cancelled.\n");
    } else {
        printf("'%s' Not an internal or external command, unknown command.\n", input);
        printf("\n");
        goto again;
    } 

    return 0;
}

Chapter 3 - functions

This chapter will explain the definition and usage of C language functions, draw and analyze the difficult recursive part in detail, and give a simple description of stack and stack overflow. Similarly, considering that it is currently in the basic stage, this chapter is equipped with exercises for readers to consolidate.

1, Functions

Definition of 0x00 function

πŸ“š In mathematics, f(x) = 2*x+1, f(x, y) = x + y are functions

In a computer, a function is a part of code in a large program, which is composed of one or more statement blocks;

It is responsible for completing a specific task and has relative independence compared with other codes;

πŸ“Œ matters needing attention:

1. Function design should pursue "high cohesion and low coupling";

(that is, the internal implementation of the function body has been modified, so try not to have an impact on the outside, otherwise: the code is inconvenient to maintain)

2. When designing functions, try to release the resources applied by the one who applies;

3. For return, a function can only return one result;

4. Different function terms have different scopes, so defining the same name in different functions will not cause conflict;

5. Functions can be called nested, but cannot be defined nested. Functions cannot be defined in functions;

7. The definition of a function can be placed anywhere, but the declaration of the function must be placed before the use of the function;

πŸ“œ Proverb:

1. Function parameters should not be too many. The fewer parameters, the better;

2. Use less global variables, which can be accessed by each method, so it is difficult to ensure the correctness and security of data;

0x01 main function

(it will not be repeated here. See Chapter I for details)

πŸ“Œ matters needing attention

1. C language stipulates that in a source program, the position of main function can be arbitrary;

2. if you call those functions before the main function, you must declare the calling function before the main function, or include the header file of the called function.

0x02 library function

❓ Why are there library functions?

πŸ“š "Although library functions are not business code, they may be used by every programmer in the development process. In order to support portability and improve program efficiency, library functions are provided in the C language basic library to facilitate programmers' software development."

πŸ“Œ Note: the use of library functions must include the corresponding header file;

πŸ“œ Proverb: cultivate a good habit of finding and learning;

πŸ’‘ Learning library function

1. MSDNοΌ›

2. c++: www.cplusplus.comοΌ›

3. Rookie tutorial: C language tutorial | rookie tutorialοΌ›

πŸ”Ί Simple summary:

IO function, string operation function, character operation function, memory operation function, time / date function, mathematical function and other library functions;

πŸ’¬ Refer to the documentation and learn several library functions:

"strcpy - string copy"

#include <stdio.h>
#include <string.h> // Required HeaderοΌ›

int main()
{
    char arr1[20] = {0}; // strDestinationοΌ›
    char arr2[] = "hello world"; // strSourceοΌ›
    strcpy(arr1, arr2);
    printf("%s\n", arr1);

    return 0;
}

🚩 >>> hello world

πŸ’¬ Refer to the documentation and try to learn several library functions:

"memset - memory settings"

#include <stdio.h>
#include <string.h> // Requested Header

int main()
{
    char arr[] = "hello world"; // dest
    memset(arr, 'x', 5); // (dest, c, count)

    printf("%s\n", arr);

    return 0;
}

🚩 >>> xxxxx world

0x03 custom function

❓ What is a custom function?

"As the name suggests, it is all designed by ourselves, giving programmers a lot of room to play"

πŸ“š Like other functions, user-defined functions have function names, return value types, and function parameters;

1. ret_type is the return type;

2. func_name is the function name;

3. Parallel is a function parameter;

πŸ’¬ Demonstration of custom functions

"Requirement: write a function to find the larger value of two values"

int get_max(int x, int y) {  // We need it to return a value, so the return type is int;
    int z = 0;
    if (x > y)
        z = x;
    else
        z = y;

    return z; // Returns z - a larger value;
}

int main()
{
    int a = 10;
    int b = 20;
    // Function call;
    int max = get_max(a, b);
    printf("max = %d\n", max);

    return 0;
}

🚩 >>> max = 20

0x04 parameters of function

πŸ“š Actual parameter (actual parameter)

1. The parameters really passed to the function are called arguments (arguments can be constants, variables, expressions, functions, etc.);

2. No matter what type of quantity the argument is, there must be certain values when calling the function, so as to transfer these values to the formal parameter;

πŸ“š Formal parameter (formal parameter)

1. After the parameter is instantiated, it is equivalent to a temporary copy of the argument. Modifying the parameter will not change the argument;

2. Formal parameters are instantiated only when the function is called;

3. The formal parameters are automatically destroyed after the function is called, and are only valid in the function;

πŸ“Œ matters needing attention:

1. Formal parameter and argument can have the same name;

2. The formal parameters of the function are generally passed by pressing the parameter stack;

3. "Formal parameters are lazy": formal parameters are instantiated only when they are called to open up memory space;

0x05 function call

πŸ“š Value passing call

1. When calling by value, the formal parameter is a temporary copy of the argument;

2. The formal parameters and arguments of the function occupy different memory blocks respectively, and the modification of the formal parameters will not affect the arguments;

3. The formal parameter and the argument do not use the same memory address;

πŸ“š Address call

1. The parameters can be operated through formal parameters during address transfer;

2. Address calling is a way to call a function by passing the memory address of a variable created outside the function to the function parameters;

3. Directly operate the variables outside the function inside the function (establish a real relationship between the variables inside and outside the function);

πŸ’¬ Exchange the contents of two variables

// void, indicating that this function does not return any value and does not need to return;
void Swap(int x, int y) {
    int tmp = 0;
    tmp = x;
    x = y;
    y = tmp;
}

int main()
{
    int a = 10;
    int b = 20;
    // Write a function - exchange the values of 2 integer variables
    printf("Before exchange: a=%d b=%d\n", a, b);
    Swap(a, b);
    printf("After exchange: a=%d b=%d\n", a, b);

    return 0;
}

🚩 >>> Before exchange: a = 10, B = 20; after exchange: a = 10, B = 20

❓ "Why is there no exchange effect? Is there something wrong?"

πŸ”‘ Parsing: when Swap is called, the argument is passed to the formal parameter. In fact, the formal parameter is a temporary copy of the argument. Because changing formal parameters cannot change arguments, there is no exchange effect;

πŸ’‘ Solution: use addressable calls (using pointers)

// Because two integer addresses are passed in the past, it needs to be received with int *;
void Swap2(int* pa, int* pb) {  // Address calling;
    int tmp = *pa; // *Dereference pa;
    *pa = *pb;
    *pb = tmp;
}

int main()
{
    int a = 10;
    int b = 20;
    printf("Before exchange: a=%d b=%d\n", a, b);
    Swap2(&a, &b); // The address is passed in;
    printf("After exchange: a=%d b=%d\n", a, b);
    
    return 0;
}

0x06 nested call to function

πŸ“š Functions can be organically synthesized;

void new_line() {
    printf("hehe ");
}
void three_line() {
    int i = 0;
    for (i=0; i<3; i++)
        new_line(); // three_line calls new three more times_ lineοΌ›
}
int main()
{
    three_line(); // Call three_lineοΌ›
    
    return 0;
}

🚩 >>> hehe hehe hehe

0x07 chained access to function

πŸ“š Take the return value of one function as an argument to another function

int main()
{
    /* strlen - Find string length */
    int len = strlen("abc");
    printf("%d\n", len);
    
    printf("%d\n", strlen("abc")); // Chain access
    
    
    /* strcpy - String copy */
    char arr1[20] = {0};
    char arr2[] = "bit";
    
    strcpy(arr1, arr2);
    printf("%s\n", arr1);
    
    printf("%s\n", strcpy(arr1, arr2)); // Chain access
    
    return 0;
}

πŸ’­ Interview questions

"What is the result?"

int main() 
{
    printf("%d", printf("%d", printf("%d", 43)));

    return 0;
}

🚩 >>> 4321

πŸ”‘ Analysis: the printf function is used to print, but it also has a return value. The return value of printf is the length of the returned character; Printf calls printf and then printf("%d", 43). First print out 43, return character length 2, print out 2, printf("%d", printf("%d", 43)) returns character length 1, and print out 1; So it is 4321;

"We can try to find the details of printf function in MSDN"

0x08 declaration and definition of function

πŸ“š Declaration of function

1. To tell the compiler what the function name, parameter and return type are, but whether they exist or not doesn't matter;

2. The function must ensure "declaration before use", and the declaration point of the function can be up to the end;

3. The function declaration should be generally placed in the header file;

πŸ“š Definition of function: refers to the specific implementation of the function and explains the function implementation of the function;

int main()
{
    int a = 10;
    int b = 20;
    
/* Declaration of function */
    int Add(int, int);

    int c = Add(a, b);
    printf("%d\n", c);

    return 0;
}

/* Definition of function */
int Add(int x, int y) {
    return x + y;
}

2, Recursion of function

0x00 definition of recursion

πŸ“š The program call itself is called recursion

1. The recursive strategy only needs a small number of programs to describe the multiple repeated calculations required in the problem-solving process, which greatly reduces the amount of code;

2. The main way of thinking about recursion is to make big things small;

πŸ“Œ matters needing attention:

1. There is a jump out condition, and each recursion must approach the jump out condition;

2. The recursion level should not be too deep to avoid stack overflow;

πŸ’¬ Recursive demonstration

"Receive an integer value and print each bit in sequence (eg. input 1234, output 1 2 3 4)"

void space(int n) 
{
    if (n > 9)
    {
        space(n / 10);
    }
    printf("%d ", n % 10);
}

int main()
{
    int num = 1234;
    space(num);

    return 0;
}

🚩 >>> 1 2 3 4

πŸ”‘ Resolution:

0x01 stack overflow

πŸ“š Stack overflow - stackoverflow

1. If the water is full, it will overflow, and the stack also has a capacity limit. When it exceeds the limit, it will overflow;

2. Stack overflow can be understood as "eating too much and vomiting", and queue overflow is "eating too much and pulling";

3. Programmer's knowledge: Stack Overflow - Where Developers Learn, Share, & Build Careers

πŸ’€ harm:

1. When the stack overflows, the nonexistent RAM space will be accessed, resulting in code flying. At this time, the context data at the time of overflow cannot be obtained, and useful information cannot be provided for subsequent program modification;

2. Causing security threats. Common attack types include: modifying the return address of the function to point to the attack code. When the function call ends, the program jumps to the address set by the attacker, modifying the function pointer, and long jumping the buffer to find the overflow buffer;

πŸ’¬ Demonstration of stack overflow;

void test(int n) {
    if(n < 10000) {
        test(n + 1);
    }
}

int main()
{
    test(1);

    return 0;
}

0x02 recursive usage

πŸ’¬ Handwritten strlen function

1. "Create temporary variable count method"

int my_strlen(char* str) {
    int count = 0;
    while (*str != '\0') {
        count++;
        str++;
    } 
    return count;
}

int main()
{
    char arr[] = "abc";
    int len = my_strlen(arr);  // The first element address is passed;
    printf("len = %d\n", len);

    return 0;
}

🚩 >>> len = 3

2. "Use recursion instead of creating temporary variables"

/*
my_strlen("abc");
1 + my_strlen("bc");
1 + 1 + my_strlen("c");
1 +1 + 1 + my_strlen("");
1 + 1 + 1 + 0
3
*/

int rec_strlen(char* str) {
    if (*str != '\0')
        return 1 + rec_strlen(str+1);
    else
        return 0;
}

int main()
{
    char arr[] = "abc";
    int len = rec_strlen(arr);
    printf("len = %d\n", len);

    return 0;
}

🚩 >>> len = 3

0x03 recursion and iteration

❓ What is iteration

"Repeat the loop in the program until a certain condition is met, also known as iteration"

πŸ“š Iterative method: also known as rolling method, it is a process of constantly using the old value of variables to deduce new values;

πŸ’¬ Find the factorial of n (without considering overflow);

"Factorial formula: n! = n(n-1)"

int Fac(int n) {
    if (n <= 1)
        return 1;
    else
        return Fac(n-1) * n;
}

int main()
{
    int n = 0;
    scanf("%d", &n);
    int ret = Fac(n);
    printf("%d\n", ret);

    return 0;
} 

πŸ’¬ Find the nth Fibonacci number (without considering overflow);

"Fiboracci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55..."

int Fib(int n) {
    if (n <= 2)
        return 1;
    else
        return Fib(n-1) + Fib(n-2);
}

int main()
{
    int n = 0;
    scanf("%d", &n);

    int ret = Fib(n);
    printf("The first%d The number of fiboracci is%d\n", n, ret);

    return 0;
}

🚩 >>> (assuming input 10) the 10th Fibonacci number is 55

>>>(assuming input 20) the 20th Fibonacci number is 6765

>>>(assuming 50 is entered) (the program seems to be stuck while running)

0x04 non recursive

❓ We found a problem. If Fib is used to calculate the 50th Fibonacci number, it will take a long time;

Using Fic function to find the factorial of 10000 (regardless of the correctness of the result), the program will crash;

πŸ”‘ The reason why it takes a long time is that many calculations are repeated during the call of Fib function. For example, when calculating the 50th Fibonacci number, it is necessary to calculate the 49th Fibonacci number, and when calculating the 49th Fibonacci number, it is necessary to calculate the 48th Fibonacci number... And so on;

πŸ’‘ Optimization method: Rewrite recursion to non recursion;

πŸ“œ Proverb:

1. Many problems are explained in recursive form, just because it is clearer than non recursive form;

2. However, the iterative implementation of these problems is often more efficient than the recursive implementation, although the readability of the code is slightly worse;

3. When a problem is very complex and difficult to implement iteratively, the simplicity of recursive implementation can compensate for the runtime overhead;

πŸ’¬ Write in a non recursive manner;

1Β 1Β 2Β 3Β 5Β 8Β 13Β 21Β 34Β 55...

aΒ bΒ c

int Fib(int n) {
    int a = 1;
    int b = 1;
    int c = 1;

    while (n > 2) {
        c = a + b;
        a = b;
        b = c;
        n--;
    }           
    return c;
}


int main()
{
    int n = 0;
    scanf("%d", &n);
    int ret = Fib(n);

    printf("%d\n", ret);

    return 0;
}

πŸ’¬ Non recursive factorization

int fac(int n) {
    int ret = 1;
    while(n > 1) {
        ret *= n;
        n -= 1;
    }
    return ret;
}

int main()
{
    int n = 0;
    scanf("%d", &n);

    int ret = fac(n);
    printf("%d\n", ret);

    return 0;
}

3, Practice

0x00 exercise 1

1. Write a function to judge whether a number is a prime number;

2. Write a function to judge whether a year is a leap year;

3. Write a function to realize the binary search of an integer ordered array;

4. Write a function. Every time this function is called, the value of num will be increased by 1;

πŸ’¬ Write an is_ The prime () function can judge whether a number is a prime number;

"Prime number refers to a natural number that has no other factors except 1 and itself among natural numbers greater than 1."

#include <stdio.h>

int is_prime(int n) {
    int i = 0;
    for(i=2; i<n; i++) {
        if(n % i == 0)
            return 0;
    }
    if(i == n)
        return 1;
    else
        return 0;
}

int main()
{
    int n = 0;
    scanf("%d", &n);

    int ret = is_prime(n);
    if(ret == 1) {
        printf("%d It's a prime\n", n);
    } else {
        printf("%d Not prime\n", n);
    }

    return 0;
}

πŸ’¬ Write an is_ leap_ The year function determines whether a year is a leap year;

int is_leap_year(int y) {
    if((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0))
        return 1;
    else
        return 0;
}

int main()
{
    int year = 0;
    printf("Please enter the year: ");
    scanf("%d", &year);

    if(is_leap_year(year) == 1)
        printf("%d Year is a leap year\n", year);
    else
        printf("Not a leap year\n");

    return 0;
}

πŸ’¬ Write a function to realize the binary search of an integer ordered array;

" intΒ arr[]Β =Β {1,2,3,4,5,6,7,8,9,10}; "

int binary_search(int arr[], int k, int sz) {
    int left = 0;
    int right = sz - 1;

    while(left <= right) {
        int mid = (left + right) / 2;
        if(arr[mid] < k)
            left = mid + 1;
        else if(arr[mid] > k)
            right = mid - 1;
        else
            return mid;
    }
    return -1;
}

int main()
{
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int sz = sizeof(arr) / sizeof(arr[0]);
    int k = 0;
    printf("Please enter a value to find: ");
    scanf("%d", &k);

    int ret = binary_search(arr, k, sz);
    if(ret == -1)
        printf("can't find\n");
    else
        printf("Found, subscript%d\n", ret);

    return 0;
}

πŸ’¬ Write a function. Every time you call this function, you will increase the value of num by 1;

void Add(int* pnum) {
    (*pnum)++;
}

int main()
{
    int num = 0;

    Add(&num);
        printf("%d\n", num);
    Add(&num);
        printf("%d\n", num);
    Add(&num);
        printf("%d\n", num);

    return 0;
}

🚩 >>> 1 2 3

0x01 exercise 2

1. Implement a function to judge whether a number is a prime number, and print prime numbers between 100 and 200 using the function implemented above;

2. Exchange two integers and implement a function to exchange the contents of two integers;

3. Customize the multiplication formula table, implement a function, print the multiplication formula table, and specify the number of rows and columns of the formula table;

πŸ’¬ Implement a function to judge whether a number is a prime number;

"Use the function implemented above to print prime numbers between 100 and 200, and print out the total number of prime numbers"

int is_prime(int n) {
    int j = 0;
    for(j=2; j<n; j++) {
        if(n % j == 0)
            return 0;
    }

        return 1;
}

int main()
{   
    int i = 0;
    int count = 0;
    for(i=100; i<=200; i++) {
        if(is_prime(i) == 1) {
            count++;
            printf("%d ", i);
        } 
    }
    printf("\n Altogether%d Prime number", count);

    return 0;
}

🚩 >>> 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 there are 21 primes in total

πŸ’¬ Swap two integers;

"Implement a function to exchange the contents of two integers"

void Swap(int* pa, int* pb) {
    int tmp = 0;
    tmp = *pa;
    *pa = *pb;
    *pb = tmp;
}

int main()
{
    int a = 10;
    int b = 20;
    printf("Before exchange: a=%d, b=%d\n", a, b);

    Swap(&a, &b);
    printf("After exchange: a=%d, b=%d\n", a, b);

    return 0;
}

🚩 >>> Before exchange: a=10, b=20, after exchange: a=20, b=10

Custom multiplication formula table;

"Implement a function to print the multiplication formula table, and specify the number of rows and columns of the formula table"

(eg. input 9, output 9 * 9 formula table, output 12, output 12 * 12 multiplication formula table.)

void formula_table(int line)
{
    int i = 0;
    for(i=1; i<=line; i++) {
        int j = 0;
        for(j=1; j<=i; j++) {
            printf("%dx%d=%-2d ", j, i, i*j);
        } 
        printf("\n"); 
    }
    
}
int main()
{
    int line = 0;
    printf("Please define the number of rows: > ");
    scanf("%d", &line);
    formula_table(line);

    return 0;
}

0x02 exercise 3

1. String reverse order, non recursive implementation and recursive implementation;

2. Write a function DigitSum(n), enter a non negative integer, and return the sum of the numbers that make up it;

3. Write a function to realize the k power of n and use recursion;

πŸ’¬ String reverse order

Write a function reverse_string(char * string)οΌ›

Reverse the characters in the parameter string instead of printing in reverse order;

Requirement: string operation function in C function library cannot be used;

(eg. char arr[] = "abcdef"; after reverse order, the contents of the array become: fedcba)

Non recursive implementation:

int my_strlen(char* str) {
    if(*str != '\0') {
        return 1 + my_strlen(str + 1);
    }
    return 0;
}

void reverse_string(char* str) {
    int len = my_strlen(str);
    int left = 0;
    int right = len - 1;

    while(left < right) {
        char tmp = str[left];
        str[left] = str[right];
        str[right] = tmp;
        left++;
        right--;
    }
}

int main()
{
    char arr[] = "abcdef";
    
    reverse_string(arr);
    printf("%s\n", arr);

    return 0;
}

🚩 >>> fedcba

Recursive implementation:

1. [] writing method

int my_strlen(char* str) {
    int count = 0;
    while(*str != '\0') {
        count++;
        str++;
    }
    return count;
}

void reverse_string(char *str) {
    int len = my_strlen(str);
    int left = 0; // Leftmost subscript
    int right = len - 1; // Rightmost subscript
    
    char tmp = str[left];
    str[left] = str[right];
    str[right] = '\0';

    // Judgment conditions
    if(my_strlen(str + 1) >= 2) {
        reverse_string(str + 1);
    }

    str[right] = tmp;
}

int main()
{
    char arr[] = "abcdef";
    
    reverse_string(arr);
    printf("%s\n", arr);

    return 0;
}

2. * writing method

int my_strlen(char* str) {
    if(*str != '\0') {
        return 1 + my_strlen(str + 1);
    }
    return 0;
}

void reverse_string(char* str) {
    int len = my_strlen(str);

    char tmp = *str;
    *str = *(str + len-1);
    *(str + len-1) = '\0';

    if(my_strlen(str + 1) >= 2) {
        reverse_string(str + 1);
    }
    *(str + len-1) = tmp;
}

int main()
{
    char arr[] = "abcdef";
    
    reverse_string(arr);
    printf("%s\n", arr);

    return 0;
}

πŸ’¬ Write a recursive function DigitSum(n), enter a non negative integer, and return the sum of the numbers that make up it;

"If you call DigitSum(1729), you should return 1 + 7 + 2 + 9, and its sum is 19" (eg. input: 1729, output: 19)

int digit_sum(int n) {
    if (n > 9) {
        return digit_sum(n / 10) + (n % 10);
    } else {
        return 1;
    }    
}

int main()
{
    int n = 1729;

    int ret = digit_sum(n);
    printf("%d\n", ret);

    return 0;
}

🚩 >>> 19

πŸ”‘ Resolution:

digit_sum(1729)

digit_sum(172) + 9

digit_sum(17) + 2 + 9

digit_sum(1) + 7 + 2 + 9

1+7+2+9 = 19

πŸ’¬ Write a function to achieve the k power of n, using recursive implementation

"Recursively implement the k-th power of n"

double Pow(int n, int k) {
    if (k == 0)
        return 1.0;
    else if(k > 0)
        return n * Pow(n, k-1);
    else // k < 0
        return 1.0 / (Pow(n, -k));
}
int main()
{
    int n = 0;
    int k = 0;
    scanf("%d^%d", &n, &k);
    
    double ret = Pow(n, k);
    printf("= %lf\n", ret);

    return 0;
}

🚩 >>> (assuming input 2 ^ 3) 8.000000 (assuming input 2 ^ - 3) 0.125000

πŸ”‘ Resolution:

1. k=0, the result is 1;

2. K > 0, because the k-th power of n is equal to N times the k-th power of N - 1, you can "make things small" through this;

3. When k < 0 and K is a negative exponential power, it can be reduced to 1 / n^k

Chapter 4 - arrays

preface

This chapter will explain the array of C language, starting with one-dimensional array. I have learned three chapters, so this chapter also adds two simple games: Sanzi chess and mine sweeping. Readers can try to write them to increase programming interest and improve modular programming ideas.

1, One dimensional array

0x00 what is an array

πŸ“š Array, that is, a set of elements of the same type;

0x01 creation of one-dimensional array

πŸ“š Array creation

      β‘  type_t: The element type of the array;

      β‘‘ arr_name: array name;

      β‘’ const_n: A constant expression that specifies the size of the array;

πŸ“Œ matters needing attention

β‘  when creating an array, constants should be given in [] and variables cannot be used;

β‘‘ if the content in array [] does not specify the size (not filled in), it needs to be initialized;

πŸ’¬ One dimensional array creation method demonstration

πŸ’¬ const_ A constant must be given in N, and variables cannot be used

int main()
{
    int count = 10;
    int arr[count]; // error
    
    return 0;
}
#define N 10

int main()
{
    int arr2[N]; // yes
    
    return 0;
}

0x02 initialization of one-dimensional array

πŸ“š Initialization: set some reasonable initial values for the contents of the array while creating the array;

πŸ’¬ Initialization demo

int main()
{
    int arr1[10];  // Create an int type array of size 10
    char arr2[20]; // Create a char type array of size 20
    float arr3[1]; // Create a float type array of size 1
    double arr4[] = {0}; // Create a double type array of unspecified size (initialization required)

    return 0;
}

πŸ’¬ Character array initialization

int main()
{
    char ch1[5] = {'b', 'i', 't'};
    char ch2[] = {'b', 'i', 't'};

    char ch3[5] = "bit";  // 'b', 'i', 't', '\0', '0'
    char ch4[] = "bit";  // 'b', 'i', ''t, '\0'

    return 0;
}

πŸ’¬ Two ways of initializing character arrays

Double quotation marks are written with slash 0, and curly braces are written without slash 0 (need to be added manually)

int main()
{
    char ch5[] = "bit"; // b. I, t, \ 0 [with slash 0]
    char ch6[] = {'b', 'i', 't'}; // b i t [without slash 0]
    printf("%s\n", ch5);
    printf("%s\n", ch6);

    return 0;
}

Without \ 0, strlen does not know when to end when reading. Strlen: it stops when it encounters slash 0

int main()
{
    char ch5[] = "bit"; // b. I, t, \ 0 [with slash 0]
    char ch6[] = {'b', 'i', 't'}; // b i t [without slash 0]
    printf("%d\n", strlen(ch5));
    printf("%d\n", strlen(ch6));

    return 0;
}

🚩 >>> 3. Random value

πŸ’‘ Of course, you can manually add a slash 0 to him, so it won't be a random value;

int main()
{
    char ch5[] = "bit"; // b. I, t, \ 0 [with slash 0]
    char ch6[] = {'b', 'i', 't', '\0'}; // b. I, t, + '\ 0' [manually add slash 0]
    printf("%d\n", strlen(ch5));
    printf("%d\n", strlen(ch6));

    return 0;
}

🚩 >>>  3     3

0x03 use of one-dimensional array

πŸ“š Subscript reference operator: [], i.e. array access operator;

πŸ“š Array size calculation method: divide the size of the entire array by the size of a letter

πŸ’¬ Print one-dimensional array

You can use the for loop to print the array one by one

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int sz = sizeof(arr) / sizeof(arr[0]);

    int i = 0;
    for(i = 0; i < sz; i++)
        printf("%d ", arr[i]);
    
    return 0;
}

🚩 >>>  1 2 3 4 5 6 7 8 9 10

πŸ”Ί Summary:

β‘  the array is accessed by subscript, which starts from 0;

β‘‘ the size of the array can be calculated;

0x04 storage of one-dimensional array in memory

πŸ“š Print in address format:% p (hexadecimal printing)

πŸ’¬ Storage method of one-dimensional array

int main()
{
    int arr[10] = {0};
    
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    for(i = 0; i < sz; i++)
        printf("&arr[%d] = %p\n", i, &arr[i]);

    return 0;
}

🚩 The operation results are as follows:

πŸ’‘ A closer look at the output results shows that as the array subscript increases, the address of the element also increases regularly;

πŸ”Ί Conclusion: arrays are stored continuously when they are in memory;

2, Two dimensional array

0x00 creation of two-dimensional array

πŸ“š Two dimensional array [row] [column]

      β‘  const_n1: Line

      β‘‘ const_n2: column

πŸ’¬ Creation of two-dimensional array

int main()
{
    int arr[3][4];     // Create an int type two-dimensional array with 3 rows and 4 columns;

    /*
        0 0 0 0
        0 0 0 0
        0 0 0 0
    */

    char arr[3][5];    // Create a char type two-dimensional array with 3 rows and 5 columns;
    double arr[2][4];  // Create a double two-dimensional array with 2 rows and 4 columns;

    return 0;
}

0x01 initialization of two-dimensional array

πŸ“š Initialization: set some reasonable initial values for the contents of the array while creating the array;

πŸ“Œ matters needing attention:

β‘  when initializing a two-dimensional array, rows can be omitted, but columns cannot be omitted;

β‘‘ two dimensional arrays are also stored continuously in memory;

πŸ’¬ Initialization demo

int main()
{
    int arr[3][4] = {1,2,3,4,5};
    
    /*
        1 2 3 4
        5 0 0 0
        0 0 0 0
    */

    int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; // Full initialization
    int arr2[3][4] = {1,2,3,4,5,6,7}; // Incomplete initialization - followed by 0;

    int arr3[3][4] = {{1,2}, {3,4}, {4,5}}; // appoint;

    /*
        1 2 0 0
        3 4 0 0
        4 5 0 0
    */

    return 0;
}

πŸ’¬ About "rows can be omitted, columns cannot be omitted"

int main()
{
    int arr1[][] = {{2,3}, {4,5}};  // error
    int arr2[3][] = {{2,3}, {4,5}}; // error
    int arr2[][4] = {{2,3}, {4,5}}; // √

    return 0;
}

0x03 use of two-dimensional array

πŸ’¬ Print 2D array

It also uses two for loops to print by subscript

int main()
{
    int i = 0;
    int j = 0;
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 4; j++)
            printf("%d", arr4[i][j]); // Two dimensional array [row] [column];
        printf("\n"); // Line feed;
    }
}

πŸ’¬ Storage of two-dimensional array in memory

int main()
{
    int arr[3][4];
    int i = 0;
    int j = 0;
    for(i = 0; i < 3; i++) {
        for(j = 0; j < 4; j++)
            printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
    }

    return 0;
}

🚩 The operation results are as follows:

πŸ’‘ After carefully examining the output results, we can see that the two-dimensional array is also stored continuously in memory;

πŸ”Ί Conclusion: two-dimensional arrays are also stored continuously in memory;

3, Array as function parameter

0x00 about array name

πŸ“š The array name is the address of the first element (with two exceptions)

β­• Exception 1:

sizeof (array name) calculates the size of the entire array

πŸ’¬ verification

int main()
{
    int arr[10] = {0};
    printf("%d\n", sizeof(arr));

    return 0;
}

🚩 >>>  40

β­• Exception 2:

&The array name {represents the entire array, and the address of the entire array is taken out

0x01 Bubble Sort

πŸ“š The core idea of bubble sorting: compare two adjacent elements, and exchange if the conditions are met;

β‘  confirm the number of trips first;

β‘‘ write down a bubble sorting process;

β‘’ exchange at last;

πŸ“Œ matters needing attention:

β‘  int arr [] is essentially a pointer, int * arr;

β‘‘ when an array passes parameters, it actually passes the address of the first element of the array;

β‘’ sz variable cannot be in bubble_sort is calculated internally, which needs to be calculated externally and then passed in;

πŸ’¬ Bubble sort: please write a bubble_sort() function, ascending, int arr[] = {9,8,7,6,5,4,3,2,1,0};

#include <stdio.h>

void bubble_sort (int arr[], int sz) // The formal parameter arr is essentially a pointer int* arr
{
    /* Number of confirmed trips */
    int i = 0;
    for(i = 0; i < sz; i++)
    {
        /* A bubble sort of work */
        int j = 0;
        for(j = 0; j <= (sz-1-i); j++) // -1: The last trip doesn't need to be arranged, -i: subtract the trips already passed
        {
            /* If the front number is larger than the back number, swap */
            if(arr[j] > arr[j + 1])
            {
                /* Create temporary variable exchange method */
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}

int main(void)
{
    int arr[] = {9,8,7,6,5,4,3,2,1,0};
    int sz = sizeof(arr) / sizeof(arr[0]);

    /* Bubble sorting */
    bubble_sort(arr, sz); // When an array passes parameters, the address of the first element is passed
    
    /* Print array */
    int i = 0;
    for(i=0; i<=sz; i++)
        printf("%d ", arr[i]);

    return (0);
}

🚩 >>>  0 1 2 3 4 5 6 7 8 9 10

⚑ Algorithm optimization: we can set a variable to judge whether the array is orderly. If it is already orderly, there is no need to bubble sort;

#include <stdio.h>

void bubble_sort (int arr[], int sz)
{
    int i = 0;
    for(i = 0; i < sz; i++)
    {
        int j = 0;
        int falg = 1; // Mark 1, assuming that this bubbling sequence has been ordered
        for(j = 0; j <= (sz-1-i); j++)
        {
            if(arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                flag = 0; // Still unordered, marked as 0
            }
        }
        if(flag == 1)
            break; // It's already in order, so there's no need to bubble sort
    }
}

int main(void)
{
    int arr[] = {9,8,7,6,5,4,3,2,1,0};
    int sz = sizeof(arr) / sizeof(arr[0]);

    /* Bubble sorting */
    bubble_sort(arr, sz);
    
    /* Print array */
    int i = 0;
    for(i=0; i<=sz; i++)
        printf("%d ", arr[i]);

    return (0);
}

4, Implement tic tac toe

0x00 game introduction

Sanzi chess is a traditional folk game, also known as Jiugong chess, circle fork, one dragon, Jingzi chess, etc. Connect the diagonals of the square and put three pieces on the opposite sides in turn. As long as you walk your three pieces into a line, the other party will lose. However, there are many times when there is a draw.

0x01 implementation idea

πŸ“š Sub module: when the contemporary code quantity is large and there are many functions, we can split the code and sub module to realize each function;

      β‘   test.c test the logic of the game;

      β‘‘  game.c. implementation of game related functions;

      β‘‘  game.h) the inclusion of game related function declarations, symbol declarations and header files;

0x02 game interface

πŸ’¬ test.c

The code realizes the game interface part

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"

void load_game_menu()
{
    printf("\n");
    printf("*********************************\n");
    printf("********** 1. Start the game ***********\n");
    printf("********** 0. Exit the game ***********\n");
    printf("*********************************\n");
}

int main(int argc, char const* argv[])
{
    int input = 0;
    do
    {
        /* Load game menu */
        load_game_menu();
        printf("Please select: ");
        scanf("%d", &input);
        switch (input)
        {
            case 1:
                printf("\ntest: The game begins\n");
                // game();
                break;
            case 0:
                printf("\n Exit the game\n");
                break;
            default:
                printf("\n Input error, please re-enter!\n");
                break;
        }
    } while (input);

    return( 0 );
}

🚩 The operation results of this part are as follows (after completing some functions, run it to find bugs in time. The earlier you find bugs, the easier it is to find bugs)

0x03 create chessboard & initialize chessboard

Write the game() function, create a chessboard, and then initialize the chessboard function

πŸ’¬ test.c

void game()
{
    /* Create chessboard */
    char board[ROW][COL];
    /* Initialize checkerboard - initialize spaces */
    init_board(board, ROW, COL);
}

πŸ’¬ game.h

#include <stdio.h>

/* Macro definition */
#define ROW 3
#define COL 3

/* Function declaration */
void init_board(char board[ROW][COL], int row, int col);
void print_board(char board[ROW][COL], int row, int col);

πŸ’¬ game.c

#include "game.h"

void init_board(char board[], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' '; // Initialize to space
		}
	}
}

0x04 print chessboard

print_ The board () function essentially prints the contents of the array

Draw a chessboard using a loop

πŸ’¬ test.c

void game()
{
	//Store data - 2D array
	char board[ROW][COL];
	//Initialize checkerboard - initialize spaces
	init_board(board, ROW, COL);

	//Print the chessboard - essentially print the contents of the array
	print_board(board, ROW, COL);
}

πŸ’¬ game.h

#include <stdio.h>

/* Macro definition */
#define ROW 3
#define COL 3

/* Function declaration */
void init_board(char board[ROW][COL], int row, int col);
void print_board(char board[ROW][COL], int row, int col);

πŸ’¬ game.c

void print_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
		if (i < row - 1)
			printf("---|---|---\n");
	}
}

🚩 Operation results:

❓ What if you change the size of the chessboard?

πŸ’‘ Code optimization

void print_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		if (i < row - 1)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		printf("\n");
		}
	}
}

🚩 We modify ROW and COL to 5 to see if we can generate a 5x5 chessboard;

0x05 design player round

πŸ’¬ test.cΒ  Β Β  game ( )

void game()
{
	//Store data - 2D array
	char board[ROW][COL];
	//Initialize checkerboard - initialize spaces
	init_board(board, ROW, COL);

	//Print the chessboard - essentially print the contents of the array
	print_board(board, ROW, COL);

	//Players play chess
	player_round(board, ROW, COL);
}

πŸ’¬ game.hΒ  Β  player_round ( )

void player_round(char board[ROW][COL], int row, int col);

πŸ’¬ game.c Β Β  player_round ( )

void player_round(char board[ROW][COL], int row, int col)
{
	/* Create coordinates */
	int x = 0;
	int y = 0;
	/* Ask players to fall */
	printf("\n[Player turn]\n");

	while (1)
	{
		printf("Please settle down: ");
		scanf("%d %d", &x, &y);
		/* Judge the legitimacy of coordinates */
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			/* Determine whether the coordinates are occupied */
			if (board[x - 1][y - 1] == ' ') // Player lost coordinates - 1
			{
				/* play chess */
				board[x - 1][y - 1] = '*';
				break;
			}
			else
				printf("[Tips] There are already pieces here, please re-enter!\n");
		}
		else
			printf("[Tips] Illegal coordinates, please re-enter!\n");
	}
}

0x06 design computer round

πŸ’¬ test.c. store random number seeds in main(), game()

int main(int argc, char const* argv[])
{
    srand((unsigned int)time(NULL)); // Seed random number
    ...
}
void game()
{
    /* Create chessboard */
    char board[ROW][COL];
    /* Initialize checkerboard - initialize spaces */
    init_board(board, ROW, COL);
    /* Print chessboard */
    print_board(board, ROW, COL);
    /* Players play chess */
    player_round(board, ROW, COL);
    /* Computer chess */
    computer_round(board, ROW, COL);
}

πŸ’¬ game.h. introduce the necessary header file of random number, computer_round ( )

#include <time.h>
#include <stdlib.h>
void computer_round(char board[ROW][COL], int row, int col);

πŸ’¬ game.c Β Β  computer_round ( )

void computer_round(char board[ROW][COL], int row, int col)
{
	printf("[Computer round]");

	while (1)
	{
		/* Random coordinates */
		int x = rand() % row;
		int y = rand() % col;
		/* Judge whether the coordinates are occupied */
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

0x07 game state design (integrating player turn and computer turn)

πŸ’¬ test. (c) when the player and the computer are finished, print the chessboard and update the latest chessboard data

void game()
{
	//Store data - 2D array
	char board[ROW][COL];
	//Initialize checkerboard - initialize spaces
	init_board(board, ROW, COL);

	//Print the chessboard - essentially print the contents of the array
	print_board(board, ROW, COL);

	while (1)
	{
		//Players play chess
		player_round(board, ROW, COL);
		print_board(board, ROW, COL);

		//Computer chess
		computer_round(board, ROW, COL);
		print_board(board, ROW, COL);

	}

0x08 judging winning & declaring winning conditions

πŸ’¬ test.cΒ  Β Β  game ( )

void game()
{
	//Store data - 2D array
	char board[ROW][COL];
	//Initialize checkerboard - initialize spaces
	init_board(board, ROW, COL);

	//Print the chessboard - essentially print the contents of the array
	print_board(board, ROW, COL);
	char ret = 0;//Accept game status
	while (1)
	{
		//Players play chess
		player_round(board, ROW, COL);
		print_board(board, ROW, COL);
		//Determine whether the player wins the game
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
		//Computer chess
		computer_round(board, ROW, COL);
		print_board(board, ROW, COL);
		//Judge whether the computer wins the game
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*')
	{
		printf("The player won\n");
	}
	else if (ret == '#')
	{
		printf("The computer won\n");
	}
	else
	{
		printf("it ends in a draw\n");
	}
	print_board(board, ROW, COL);
}

πŸ’¬ game.hΒ  Β  is_win ( )

char is_win(char board[ROW][COL], int row, int col);

πŸ’¬ game.c Β Β  is_win ( )

char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	/* Judge three lines */
	for (i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
		{
			return  board[i][1];//
		}
	}

	/* Judge three columns */
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
		{
			return board[1][i];
		}
	}

	/* Judge diagonal */
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}

	/* Judge the draw */
	//If the chessboard is full, return 1, and if not, return 0
	int ret = is_full(board, row, col);
	if (ret == 1)
	{
		return 'Q';
	}

	/* continue */
	return 'C';
}

πŸ’¬ game.cΒ Β Β  is_full

int is_full(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
			{
				return 0; // The chessboard is not full
			}
		}
	}
	return 1; // The chessboard is full
}

0x09 code operation

🚩 Player wins

🚩 Computer wins

🚩 it ends in a draw

5, Minesweeping

0x00 game introduction

Minesweeping is a popular puzzle game, which was released in 1992.

The goal of the game is to find out all non thunder grids according to the numbers in the click grid in the shortest time, and avoid stepping on thunder. Stepping on a thunder means losing the whole game.

0x01 implementation idea

πŸ“š Sub module:

      β‘   test.c) test the logic of the game;

      β‘‘  game.c. implementation of game related functions;

      β‘‘  game.h) the inclusion of game related function declarations, symbol declarations and header files;

0x02 game interface

πŸ“š Idea:

β‘  design the start page and provide the following options: start the game, exit the game (and check whether there is an input error);

β‘‘ in order to play one and continue to play, use do The while function allows the user to run the program without entering 0;

β‘’ import the header file game h. Put all header files, function declarations, macro definitions, etc. in game H medium;

πŸ’¬ test.c. main function, LoadGameMenu function

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

void LoadGameMenu()
{
    printf("\n");
    printf("*********************************\n");
    printf("********** 1. Start the game ***********\n");
    printf("********** 0. Exit the game ***********\n");
    printf("*********************************\n");
}

int main()
{
    int input = 0;
    
    do {
        LoadGameMenu();
        printf("Please select: ");
        scanf("%d", &input);
        switch (input) {
            case 1:
                printf("Start the game\n");
                break;
            case 0:
                printf("Exit the game\n");
                break;
            default:
                printf("Selection error, please re select\n");
                break;
        }
    } while (input);

    return (0);
}

πŸ’¬ game.h

#include <stdio.h>

🚩 The code runs as follows

0x03 initialize 9x9 chessboard

πŸ“š Idea:

β‘  design the Game function to call the function to realize the Game function;

β‘‘ create two two-dimensional arrays to store the information of arranged mines and the information of checked mines respectively;

β‘’ initialize them, and the information of the deployed mine is represented by 0 (temporarily set 0 as non mine and 1 as mine), and the information of the checked mine is represented by *;

β‘£ since a 9x9 minesweeping chessboard is required and the corresponding coordinates need to be displayed outside, the size of the actual array should be 11x11;

β‘€ define ROW and COL. in order to modify the chessboard later, ROWS = ROW+2, COLS = COL+2;

πŸ’¬ test.c (main function, Game function)

void Game()
{
    char mine[ROWS][COLS] = { 0 }; // Store the information of the arranged mine
    char show[ROWS][COLS] = { 0 }; // Store the information of thunder
    /* Initialize chessboard */
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');
}

int main()
{
    ...
    
            case 1:
                Game(); // The Minesweeper game
                break;
    ...

    return (0);
}

πŸ’¬ game.h

#include <stdio.h>

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

/* Initialize chessboard */
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

πŸ’¬ test.c (InitBoard function)

void InitBoard(
	char board[ROWS][COLS],
	int rows,
	int cols,
	char set
	)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++) {
		for (j = 0; j < cols; j++) {
			board[i][j] = set;
		}	
	}
}

0x04 print chessboard

πŸ“š Idea:

β‘  design a function to print the chessboard and print the chessboard;

β‘‘ although the mine chessboard cannot be seen by players, we also print the mine chessboard for testing;

β‘’ since the chessboard is 9x9 and cannot go beyond 9x9, the incoming should be ROW and COL, not ROWS and COLS;

πŸ’¬ test.c (Game function)

void Game()
{
    char mine[ROWS][COLS] = { 0 }; // Store the information of the arranged mine
    char show[ROWS][COLS] = { 0 }; // Store the information of thunder
    /* Initialize chessboard */
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');
    /* Print chessboard */
    DisplayBoard(mine, ROW, COL);
    DisplayBoard(show, ROW, COL);
}

int main() {...}

πŸ’¬Β  game.h

...
/* Print chessboard */
void DisplayBoard(char board[ROWS][COLS], int row, int col);

πŸ’¬ game.c (DisplayBoard function)

void DisplayBoard (
	char board[ROWS][COLS],
	int row,
	int col
	)
{
	int i = 0;
	int j = 0;
	printf("\n---------------------\n"); // Dividing line
	/* Print column number */
	for (i = 0; i <= col; i++) {
		if (i == 0) {
			printf("  "); // Remove the junction zero part of xy in the upper left corner
			continue;
		}
		printf("%d ", i);
		if (i == 9)
		{
			printf("┆"); // Print vertical border
		}
	}
	printf("\n");
	for (i = 1; i <= row; i++) {
		/* Print line number */
		printf("%d ", i);
		for (j = 1; j <= col; j++) {
			/* print contents */
			printf("%c ", board[i][j]);
			if (j == 9) {
				printf("┆");  // Print vertical border
			}
		} 
		printf("\n"); // Wrap after printing a line
	}
	printf("---------------------\n"); // Dividing line
}

🚩 The operation results are as follows

0x05 layout lightning

πŸ“š Idea:

β‘  randomly generate several mines (temporarily 10), which can be realized by rand function; (srand is placed in the main function);

β‘‘ since the mine laying should be arranged inside the 9x9 chessboard and cannot go outside the 9x9, the incoming should be ROW and COL;

β‘’ in order to test whether the mine layout is successful, we print out the mine chessboard first;

β‘£ define the number of mines. In order to test, 10 mines are arranged;

πŸ’¬ test.c (Game function)

void Game()
{
    char mine[ROWS][COLS] = { 0 }; // Store the information of the arranged mine
    char show[ROWS][COLS] = { 0 }; // Store the information of thunder
    /* Initialize chessboard */
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');
    /* Print chessboard */
    // DisplayBoard(mine, ROW, COL);
    DisplayBoard(show, ROW, COL);
    /* Lay thunder */
    SetMine(mine, ROW, COL);
    DisplayBoard(mine, ROW, COL); // Print it out temporarily
}

int main()
{
    srand((unsigned int)time(NULL)); // Seed random number
    ...
}

πŸ’¬ game.h

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define EASY_COUNT 10

...

/* Set mine */
void SetMine(char mine[ROWS][COLS], int row, int col);

πŸ’¬ game.c (SetMine function)

void SetMine (
	char mine[ROWS][COLS],
	int row, 
	int col
	)
{
	/* Arrange 10 mines */
	int count = EASY_COUNT;
	while (count) {
		/* Generate random Subscripts */
		int x = rand() % row + 1; // The remaining row becomes a single digit
		int y = rand() % col + 1; // The remaining col becomes a single digit
		if (mine[x][y] == '0') {  // Judge whether there is thunder in a certain coordinate
			mine[x][y] = '1'; // Set 1 as mine
			count--;
		}
	}
}

🚩 Operation results

0x06 platoon chare

πŸ“š Idea:

β‘  let the player check the thunder, input the coordinates of the thunder to check, and judge whether the coordinates entered by the player are legal;

β‘‘ if there is thunder on the input coordinates (1), the game will be declared failed, and the chessboard will be printed to let the player understand;

β‘’ if there are no mines on the input coordinates, count the number of mines around, and display the number of mines on the coordinates to display the information of finding mines;

β‘£ the method of counting the surrounding lightning is shown in the figure below. count the coordinates of up, down, left, right, left, right and right with xy as the center;

β‘€ ROWS and COLS are passed in here. Even if xy is on the edge, the array will not cross the boundary when calculating around xy;

πŸ’¬ test.c (Game function)

void Game()
{
    char mine[ROWS][COLS] = { 0 }; // Store the information of the arranged mine
    char show[ROWS][COLS] = { 0 }; // Store the information of thunder
    /* Initialize chessboard */
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');
    /* Print chessboard */
    //DisplayBoard(mine, ROW, COL);
    DisplayBoard(show, ROW, COL);
    /* Lay thunder */
    SetMine(mine, ROW, COL);
    //DisplayBoard(mine, ROW, COL);
    /* Check thunder */
    FindMine(mine, show, ROW, COL);
}

int main() {...}

πŸ’¬ game.h

...
/* Check thunder */
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col);

πŸ’¬ game.c (FineMine function and get_mine_count function)

static int get_mine_count (
	char mine[ROWS][COLS], 
	int x, 
	int y
	)
{
	/*
	*    (x-1, y-1)  (x-1, y)  (x-1, y+1)
	* 
	*    ( x , y-1)  ( x , y)  ( x , y+1)
	* 
	*    (x+1, y-1)  (x+1, y)  (x+1, y+1)
	*/

	return (
		mine[x - 1][y] +
		mine[x - 1][y - 1] +
		mine[x][y - 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] +
		mine[x][y + 1] +
		mine[x - 1][y + 1] - 8 * '0'
		);
}

void FindMine (
	char mine[ROWS][COLS],
	char show[ROWS][COLS],
	int row,int col
	)
{
	/*
	* notes:
	*	1. Enter the coordinates of the screen
	*	2. Check the coordinates for thunder
	*		(1)It's ray. - I'm sorry it blew up. - the game is over
	*		(2)Not mine - there are several mines around the statistical coordinates - store the information of the troubleshooting class
	*/
	
	int x = 0;
	int y = 0;

	while (1) {
		printf_s("\n Please enter the coordinates to check: "); // x(1~9) y(1~9)
		scanf_s("%d%d", &x, &y);
		/* Judge the legitimacy of coordinates */
		if (x >= 1 && x <= row && y >= 1 && y <= col) {
			if (mine[x][y] == '1') {
				/* It's ray. Declare the game a failure */
				printf_s("\n I'm sorry you were killed\n");
				DisplayBoard(mine, row, col);
				break;
			}
			else {
				/* It's not ray. How many rays are there in the x, y coordinates */
				int count = get_mine_count(mine, x, y);
				show[x][y] = count+'0'; // ASCII to character
				/* Display the troubleshooting information */
				DisplayBoard(show, row, col);
			}
		}
		else {
			printf("\n Illegal coordinates, please re-enter!\n");
		}
	}
}

0x07 set victory condition

πŸ“š Idea:

β‘  add a counter win to count the number of thunder checked. When the number is equal to the number of thunder, it means that the thunder has been discharged and the game is declared a victory;

β‘‘ the condition of while cycle can be set to enter the cycle as long as win is still less than 9x9 lightning reduction number;

πŸ’¬ game.c (FineMine function)

void FindMine (
	char mine[ROWS][COLS],
	char show[ROWS][COLS],
	int row,int col
	)
{
	/*
	* notes:
	*	1. Enter the coordinates of the screen
	*	2. Check the coordinates for thunder
	*		(1)It's ray. - I'm sorry it blew up. - the game is over
	*		(2)Not mine - there are several mines around the statistical coordinates - store the information of the troubleshooting class
	*/
	
	int x = 0;
	int y = 0;
	int win = 0;

	while (win<row*col - EASY_COUNT) {
		printf_s("\n Please enter the coordinates to check: "); // x(1~9) y(1~9)
		scanf_s("%d%d", &x, &y);
		/* Judge the legitimacy of coordinates */
		if (x >= 1 && x <= row && y >= 1 && y <= col) {
			if (mine[x][y] == '1') {
				/* It's ray. Declare the game a failure */
				printf_s("\n I'm sorry you were killed\n");
				DisplayBoard(mine, row, col);
				break;
			}
			else {
				/* It's not ray. How many rays are there in the x, y coordinates */
				int count = get_mine_count(mine, x, y);
				show[x][y] = count+'0'; // ASCII to character
				/* Display the troubleshooting information */
				DisplayBoard(show, row, col);
				win++;
			}
		}
		else {
			printf("\n Illegal coordinates, please re-enter!\n");
		}
	}
	
	if (win == row * col - EASY_COUNT) {
		printf("Congratulations on your success!\n");
		DisplayBoard(mine, row, col);
	}
}

0x08 code operation

🚩 Mine coordinates

🚩 Illegal input of coordinates

🚩 I'm sorry you were killed

Chapter 5 - operators

preface:

This chapter will give an in-depth explanation of C language operators, and take out each operator separately. Finally, some simple exercises are added with detailed analysis.

1, Arithmetic operator

0x00 overview

πŸ“Œ matters needing attention:

β‘  except for the% operator, several other operators can act on integers and floating-point numbers;

β‘‘ for the / operator, if both operands are integers, perform integer division;

β‘’ for the / operator, as long as there is a floating-point number, the floating-point number division is executed;

β‘£ the two numbers of the% operator must be integers;

0x01 integer division

πŸ“š Definition: for / operands, if both operands are integers, perform integer division;

❓ Integer division: that is, an integer is divided by another integer, and the result is only integer;

πŸ’¬ Code demonstration:

int main()
{
    int a = 5 / 2; // 5 ÷ 2 = quotient 2 remaining 1
    printf("a = %d\n", a); // πŸ‘ˆ  What is the output?

    return 0;
}

🚩 Operation result: a = 2

0x02 floating point division

πŸ“š Definition: as long as floating-point numbers appear, floating-point division is executed;

❓ Floating point division: the result will retain the decimal part (given the corresponding% premise);

πŸ’¬ Code demonstration:

int main()
{
    double a = 5 / 2.0; // 5 ÷ 2 = 2.5, if there is one floating-point number, the condition is true, and the floating-point number division is executed
    printf("a = %lf\n", a); // πŸ‘ˆ  What is the output?

    return 0;
} 

🚩 Operation result: a = 2.500000

0x03 modulo operator

πŸ“š Definition: modulo operation is to find the remainder of the division of two numbers. The two operands must be non-zero integers;

πŸ“Œ Precautions:

β‘  the two operands must be integers;

β‘‘ both operands cannot be 0 (meaningless);

πŸ’¬ Code demonstration:

int main()
{
    int a = 996 % 10; // 996 mod 10 = 6
    int b = 996 % 100; // 996 mod 100 = 96
    printf("%d\n", a);
    printf("%d\n", b);

    return 0;
}

🚩 Operation result: 6 # 96

❌ Error presentation:

int main()
{
    double a = 5 % 2.0; // ❌  Operand must be an integer
    printf("a = %lf\n", a);

    return 0;
} 

🚩 Running result: error: invalid operations to binary% (have 'int' and 'double')

int main()
{
    int a = 2 % 0; // ❌  Operand cannot be 0
    printf("%d\n", a);

    return 0;
} 

🚩 Running result: warning: division by zero [- wdiv by zero]

0x04 distinction between integer division and floating point division

πŸ’¬ Code demonstration: we want 1.2

int main()
{
    int a = 6 / 5;
    printf("%d\n", a);
    
    return 0;
}

  🚩 Operation result: 1 (but the operation result is 1)

❓ Is it because we print with% d?

int main()
{
    float a = 6 / 5;
    printf("%f\n", a);
    
    return 0;
}

🚩 Operation result: 1.000000 (still not the desired 1.2, the operation result is 1.000000)

(angry, incompetent, furious)

πŸ’‘ Analysis: in fact, the problem is not whether you can put the next decimal in a, but the result of 6 / 5 is already 1 (integer division is executed);

πŸ”‘ Solution: change 6 to 6.0, or 5 to 5.0, or both, and let it perform floating-point division;

int main()
{
    float a = 6 / 5.0;
    printf("%f\n", a);
    
    return 0;
}

🚩 Operation result: 1.200000

❓ Although the code can run, the compiler reports a warning. Let's see what's going on:

πŸ”‘ Parsing: if you write out this number directly (6.0 or 5.0), the compiler will default to double

Then the result of a after calculation will also be double (double precision floating-point number);

If the value of a double precision floating-point number is placed in a single precision floating-point number, the precision may be lost,

The kind compiler issued such a warning, which is normal;

πŸ’‘ If you don't want to see such a warning, you can do this:

int main()
{
    float a = 6.0f / 5.0f; // πŸ‘ˆ  "Fixed" is float single precision floating point number
    printf("%f\n", a);
    
    return 0;
}
int main()
{
    double a = 6.0 / 5.0;  // πŸ‘ˆ  Change to double
    printf("%lf\n", a);
    
    return 0;
}

πŸ“š Loss of accuracy:

β‘  when the number of significant digits exceeds 7, it will be rounded off and more accuracy will be lost;

β‘‘ when running large numerical operation, overflow may occur and wrong results will be obtained;

2, Shift operator

0x00 overview

πŸ“š Concept: shift operators are divided into "shift left operator" and "shift right operator";

πŸ“Œ Precautions:

β‘  the operand of the shift operator must be an integer;

β‘‘ for operators, do not move negative digits (this is the behavior defined by the standard);

β‘’ the left shift operator has the effect of multiplying 2, and the right shift operator has the effect of dividing 2 (left multiply 2, right divide 2);

0x01 shift left operator

πŸ“š Shift rule: discard on the left and fill 0 on the right; (the number on the left) πŸ‘΄ Climb, as for how far you climb, it depends on the operands)

πŸ’¬ Code demonstration:

int main()
{
    int a = 2;
    int b = a << 1; // Move the binary bit of a to the left by 1 bit;
    printf("b = %d\n", b); // 4 (the shift left operator has the effect of multiplying 2)
    
    /*
           00000000000000000000000000000010
         0|000000000000000000000000000010+0  (Discard on the left and fill 0 on the right)
    */
        
    return (0);
}

🚩 Operation result: b = 4

πŸ”‘ Schematic shift left operator:

0x02 shift right operator

πŸ“š Shift rules: two kinds of shift rules;

β‘  arithmetic shift right: discard the right and fill the original symbol bit on the left (usually arithmetic shift right);

β‘‘ logical shift right: discard on the right and fill 0 on the left;

πŸ“Œ matters needing attention:

β‘  arithmetic shift right is the default in the C compiler. If it is a signed signed type, you should pay attention to it;

β‘‘ when unsigned type is used, the results of arithmetic right shift and logical right shift are the same;

int main()
{
    int a = 10;
    int b = a >> 1; // Move the binary bit of a one bit to the right
    printf("b = %d\n", b); // 5 (the shift right operator has the effect of dividing by 2)

    /*
           00000000000000000000000000001010
          0+0000000000000000000000000000101|0
    */

    return 0;
}

🚩 Operation result: b = 5

πŸ”‘ Analysis: in order to understand what is arithmetic right shift and what is logical right shift, we have to understand the binary representation of integers:

0x03 binary representation of integers (preliminary understanding)

πŸ“š Negative number - 1 should be stored in memory, where binary complement is stored;

πŸ“Œ Binary representation of integer (original inverse complement):

β‘  original code: the binary sequence written directly according to the value is the original code;

β‘‘ inverse code: the sign bit of the original code remains unchanged, and the other positions are reversed by bit, that is, the inverse code (if you don't know what is reversed by bit, we will talk about it later);

β‘’ complement: inverse code + 1 is the complement; (the complement is stored in the memory)

πŸ“œ Original code, inverse code and complement code of - 1:

πŸ’¬ Back to the above question, if the logical right shift is adopted when moving right:

int main()
{
    int a = -1;
    int b = a >> 1;
    printf("b = %d\n", b);

    return 0;
}

🚩 Operation result: b = -1

πŸ”‘ Graphic logic shift right and arithmetic shift right:

❌ Error demonstration: operand cannot be negative!

int main()
{
    int num = 10;
    num >> -1; // ❌   a<<1 ??   Garbage code

    return 0;
}

🚩 Running result: warning: right shift count is negative [- wshift count negative]

3, Bit operator

0x00 overview

πŸ“š Bitwise operators: bitwise and, bitwise OR, bitwise XOR;

πŸ“Œ Note: the operands of bit operators must be integers;

0x01 bitwise AND&

πŸ“š Definition: by binary and by bit, the result bit is 1 only when the corresponding two binary bits are 1; (the results are true only if they are all true)

πŸ’¬ Code demonstration: usage of bitwise AND

int main()
{
    int a = 3;
    int b = 5;
    int c = a & b;  // Both a and b are true
    printf("%d", c);

    return 0;
}

🚩 Operation results: 1

0x02 bitwise OR

πŸ“š Definition: as long as one of the two corresponding binary bits is 1, the result bit is 1; (as long as one is true, the result is true)

πŸ’¬ Code demonstration: usage of bitwise OR

int main()
{
    int a = 0;
    int b = 5;
    int c = a | b; // One of a and b is true
    printf("%d\n", c);
    
    return 0;
}

🚩 Operation result: 5

int main()
{
    int a = 0;
    int b = 0;
    int c = a | b; // Both a and b are false
    printf("%d\n", c);

    return 0;
}

🚩 Run result: 0

0x03 bitwise XOR^

πŸ“š Definition: the same is 0 and the difference is 1; (the same up and down is false, and the difference is true)

πŸ’‘ Qiao Ji: do you think bitwise XOR is difficult to remember? Try to remember that πŸ‘‡

"Are the lovers heterosexual? Back to 1, not back to 0" 0 1 yes, 1 0 yes, 1 1 no, 0 0 No;

β€» XOR: a ⊕ B = (⊕ a ∧ b) ∨ (a ∧ b) if the values of a and B are different, the XOR result is 1, otherwise the result is 0;

πŸ’¬ Code demonstration: usage of bitwise XOR

int main()
{   
    int a = 3;
    int b = 5;
    int c = a ^ b; // a and b are different
    printf("%d\n", c);

    return 0;
}

🚩 Operation result: 6

int main()
{   
    int a = 3;
    int b = 3;
    int c = a ^ b; // a and b are the same
    
    printf("%d\n", c);

    return 0;
}

🚩 Run result: 0

Application of 0x04 bit operator

πŸ“ƒ Interview question: exchange the values of two int variables, and the third variable cannot be used;

(i.e. a=3, b=5, a=5, b=3 after exchange)

1. Temporary variable method - this method is prohibited in this topic, but it is recommended to use this method in work;

int main()
{
    int a = 3;
    int b = 5;
    printf("Before exchange: a = %d, b = %d\n", a, b);
    int tmp = a; // Create a temporary variable to store a
    a = b; // a becomes b
    b = tmp; // b becomes the original a
    printf("After exchange: a = %d, b = %d\n", a, b);

    return 0;
}

🚩 Operation result: before exchange: a = 3, b = 5; After exchange: a=5, b=3

2. Addition and subtraction exchange method - defective: it may overflow (exceeding the storage limit of integer)

int main()
{
    int a = 3;
    int b = 5;
    printf("Before exchange: a = %d, b = %d\n", a, b)
    a = a + b;
    b = a - b;
    a = a - b;
    printf("After exchange: a = %d, b = %d\n", a, b);

    return 0;
}

🚩 Operation result: before exchange: a = 3, b = 5; After exchange: a=5, b=3

πŸ”‘ Analysis: Step 1: 3 + 5 = 8, step 2: 8 - 5 = 3, step 3: 8 - 3 = 5, at this time, a = 5, b = 3;

3. XOR exchange method - disadvantages: poor readability and low execution efficiency;

int main()
{
    int a = 3;
    int b = 5;
    printf("Before exchange: a = %d, b = %d\n", a, b);
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
    printf("After exchange: a = %d, b = %d\n", a, b);

    return 0;
}

🚩 Operation result: before exchange: a = 3, b = 5; After exchange: a=5, b=3

πŸ”‘ Resolution:

πŸ’¬ Write code to realize: find the number of 1 in the binary of an integer stored in memory

1. General solution - modular division

int main()
{   
    int num = 0;
    int count = 0;
    scanf("%d", &num);
    
    /* There are several 1's in the complement of num */
    while(num != 0) {
        if(num % 2 == 1) {
            count++;
        }
        num = num / 2;
    }
    printf("%d\n", count);

    return 0;
}

🚩 Operation result: (hypothetical input 3) 2

πŸ”‘ Resolution:

2. Shift operator + bitwise and combination

πŸ’­ Idea:

β‘  use the for loop for 32 / 64 times;

β‘‘ for each if judgment, the result of shifting num right by i bits is bitwise and 1. If it is true, it will be 1, count + +;

β‘’ if it is false, enter the next cycle and print count at last;

int main()
{
    int num = 0;
    int count = 0;
    scanf("%d", &num);

    int i = 0;
    /* 32 Bit system, at least 32 cycles */
    for(i=0; i<32; i++) {
        if( ((num >> i) & 1) == 1 )  // If num shifts the result of i-bit right and 1 bitwise and, it is true
            count++;
    }
    printf("%d\n", count);

    return 0;
}

🚩 Operation result: (hypothetical input 3) 2

4, Assignment operator

0x00 overview

πŸ“š Usage: used to re assign the value of a variable;

0x01 general assignment

πŸ“š Assignment method:

πŸ’¬ The assignment operator is a great operator. It allows you to get a value you were not satisfied with before:

int main()
{
    int weight = 120; // Weight 120, not satisfied, I want to lose weight!
    weight = 89; // Assign value if you are not satisfied~

    double salary = 10000.0; // Me: boss! I want a raise!
    salary = 20000.0; // Boss: OK, no problem!

    return 0;
}

0x02 continuous assignment

πŸ“š Definition: continuous assignment, i.e. assigning multiple values at one time;

πŸ“œ Suggestion: it is recommended not to use continuous assignment, which will make the code less readable and not easy to debug;

πŸ’¬ Code demonstration: how to use continuous assignment;

int main()
{
    int a = 10;
    int x = 0;
    int y = 20;
    a = x = y+1;// πŸ‘ˆ  continuous assignment 

    x = y+1;
    a = x;
    // πŸ‘†  This writing is clearer, crisp and easy to debug

    return 0;
}

0x03 compound assignment character

πŸ“š Meaning: compound assignment operator is designed to reduce the amount of code input;

πŸ“Œ Precautions:

β‘  x = x + 10 is equivalent to x += 10;

β‘‘ it can improve the cleanliness of the code and make the code cleaner;

πŸ’¬ Code demonstration: use of compound assignment character

int main()
{
    int x = 10;

    x = x + 10; 
    x += 10; //Writing method of compound assignment character (equivalent to above)

    return 0;
}

5, Monocular operator

0x00 overview

❓ What is a monocular operator?

πŸ’‘ An operator that has only one operand in an operation is called a unary operator;

0x01 logic reverse operation!

πŸ“š Function: it can turn true into false and false into true;

πŸ’¬ Usage of logical inverse operation:

int main()
{
    int a = 10;
    printf("%d\n", !a); // Change true to false, 0

    int b = 0;
    printf("%d\n", !b); // Change false to true, 1
    
    return 0;
}

🚩 Run result: 0 # 1

πŸ’¬ Most common usage:

int main()
{
    int flag = 5;

    if ( flag ) //  flag != 0 -> hehe
        printf("hehe\n");  // flag is true, print hehe
        
    if ( !flag ) // flag == 0 -> haha
        printf("haha\n"); // flag is false, print haha

    return 0;
}

🚩 Running result: hehe

0x02 negative value-

πŸ“š Function: set a number to negative;

πŸ’¬ Usage of negative values:

int main()
{
    int a = 10;
    a = -a; // Put a minus sign before a
    printf("%d", a);
    
    return 0;
}

🚩 Operation result: - 10

0x03 positive value+

πŸ“š Function: generally omitted, the same as in mathematics;

πŸ’¬ The plus sign is generally not written:

int main()
{
    int a = +5; // It's usually omitted, just like in mathematics
    printf("%d", a);

    return 0;
}

🚩 Operation result: 5

0x04 address fetching operator & and dereference operator*

πŸ“š Understanding:

β‘  the address operator can be understood as express delivery;

β‘‘ the dereference operator can be understood as the dereference operator;

(the pointer section will explain in detail)

πŸ’¬ Usage demonstration:

int main()
{
    int a = 10;
    int* pa = &a; // Take the address operator (then store the address in int* pa)
    *pa = 20; // The dereference operator finds the object it points to through the value stored in p;
    // *p is a. if * p is assigned to 20, a will become 20;
    
    return 0;
}

πŸ”‘ Resolution:

β‘  first, int* pa is a pointer variable (if you don't know what a pointer is, it can be understood as an express package temporarily);

β‘‘ the express package contains the memory address. We use the address fetching operator & fetch the address of a and store it in this package (int * PA = & A);

β‘’ at this time, we want to modify the value of A. We need to open the package for modification. We can modify a to a new value (* pa = 20) through the dereference operator *;

0x05 type length of operand sizeof()

πŸ“š Function: calculate the memory space occupied by variables, in bytes;

πŸ“Œ matters needing attention:

β‘  the expressions in sizeof brackets do not participate in the operation;

β‘‘ sizeof is not a function in essence, so parentheses can be omitted, but parentheses cannot be omitted when sizeof is followed by a type;

πŸ’¬ sizeof usage:

int main()
{
    int a = 10;
    char c = 'a';
    char* pc = &c;
    int arr[10] = {0};

    /* sizeof The amount of memory space occupied by the calculated variable, in bytes */
    printf("%d\n", sizeof(a)); //4;
    printf("%d\n", sizeof(int)); //4;

    printf("%d\n", sizeof(c)); //1;
    printf("%d\n", sizeof(char)); //1;

    printf("%d\n", sizeof(pc)); //4;    In 32-bit system
    printf("%d\n", sizeof(char*)); //4;

    printf("%d\n", sizeof(arr)); //40; 4x10=40
    printf("%d\n", sizeof( int [10] )); //40;

    return 0;
}   

πŸ’¬ What is the result of the following code?

int main()
{
    short s = 0;
    int a = 10;

    printf("%d\n", sizeof(s = a + 5));
    printf("%d\n", s);
}

🚩 Running result: 2} 0

❓ Why s or 0? s = a + 5, shouldn't s be 15

πŸ”‘ Analysis: 15 πŸ”¨ 15. The expression in sizeof parentheses does not participate in the operation!

πŸ’¬ How many (32 bits) are (1), (2), (3) and (4) after the following codes are output?

void test1(int arr[]) //The first element is passed from the parameter
{
    printf("%d\n", sizeof(arr)); // (3)
}
void test2(char ch[])
{
    printf("%d\n", sizeof(ch));  // (4)
}
int main()
{
    int arr[10] = {0};
    char ch[10] = {0};
    printf("%d\n", sizeof(arr)); // (1)
    printf("%d\n", sizeof(ch));  // (2)
    test1(arr);
    test2(ch);

    return 0;
}

πŸ’‘ Answer: (1) 40 (2) 10 (3) 4 (4) 4

πŸ”‘ Resolution:

β‘  (1) the size of an int is 4, the size of the array is 10, 4x10 = 40, so the answer is 40;

β‘‘ (3) the size of a char is 1, the size of the array is 10, and 1x10 = 10, so the answer is 10;

β‘’ (3) (4) the array name is passed as a parameter. Although it is the address of the first element, the address of the first element is also the address

So take a pointer to receive it. In essence, arr and ch are pointers, and the size of the pointer,

It is 4 bytes or 8 bytes (see the operating system for specific bytes). The title is 32 bits, so the answer is 4;

❌ Error demonstration:

int main()
{
    /* sizeof Parentheses cannot be omitted when types are followed */
    int a = 10;
    printf("%d\n", sizeof a );     // Can be omitted βœ…
    printf("%d\n", sizeof int);    // error!  Cannot be omitted ❌

    return 0;
}

🚩 Running result: error: expected expression before 'int' printf ('% d \ n', sizeof int);

0x06 reverse by bit~

πŸ“š Function: reverse a number by bit, 0 becomes 1, 1 becomes 0;

πŸ“Œ Precautions:

β‘  reverse by bit, 1 ~ 0 are interchanged, including symbol bits;

β‘‘ after the bit is reversed, it is a complement;

πŸ’¬ Clever use of bit inversion: change the third number of binary bits of a number from right to left to 1;

int main()
{
    int a = 11;
    a = a | (1<<2);

    //    00000000000000000000000000001011    11
    //  |000000000000000000000000000 let him and "this number" by digit or
    //-------------------------------------    
    //    0000000000000000000000000000001111 this bit becomes 1

    // How to create "this number"?
    //    1<<2;
    //    00000000000000000000000000000001   1
    //    00000000000000000000000000000100 move him two places to the left, and 1 will be here
    //    a|(1<<2)
    //    00000000000000000000000000001011
    //  | 00000000000000000000000000000100  
    //-------------------------------------
    //    00000000000000000000000000001111  
    printf("%d\n", a); //15

    a = a & ( ~ (1<<2) );
    // How to change it back? ↓ change this bit to 0
    //    0000000000000000000000000000001111 let him and 0 bitwise AND
    //  |11111111111111111111111111111011 give him a bit and a "such number"
    //-------------------------------------
    //    0000000000000000000000000000000000001011 restores this bit to 0 again

    //  1 < < 2, ditto
    //    000000000000000000000000000000000 can be obtained by inversing the number bit by bit one thousand and eleven
    //  ~
    //    11111111111111111111111111111011

    //  a& ~
    //    00000000000000000000000000001111   15
    //  & 11111111111111111111111111111011
    //-------------------------------------
    //    00000000000000000000000000001011   11
    printf("%d\n", a); //11
    
    return 0;
}

🚩 Operation result: 15 , 11

0x07 front and rear++

πŸ“š definition:

β‘  pre + +: add before use;

β‘‘ post + +: use first and then add;

πŸ’¬ Code demonstration: Post + + usage

int main()
{
    int a = 10;
    printf("%d\n", a++); // Post + +: use first, then++
    printf("%d\n", a); // a has now changed to 11

    return 0;
}

🚩 Operation result: 10 to 11

πŸ’¬ Code demonstration: the usage of pre + +

int main()
{
    int a = 10;
    printf("%d\n", ++a); // Pre + +: first + +, then use
    printf("%d\n", a);

    return 0;
}

🚩 Operation result: 11} 11

0x08 front and rear--

πŸ“š definition:

β‘  pre -: reduce first and then use;

β‘‘ post + +: use first and then subtract;

πŸ’¬ Code demonstration: usage of post -

int main()
{
    int a = 10;
    printf("%d\n", a--);
    printf("%d\n", a);

    return 0;
}

🚩 Operation result: 10 # 9

πŸ’¬ Code demonstration: usage of post -

int main()
{
    int a = 10;
    printf("%d\n", --a);
    printf("%d\n", a);

    return 0;
}

🚩 Operation result: 9} 9

0x09 cast type

πŸ“š Function: forced type conversion can convert variables from one type to another data type;

πŸ“Œ Precautions:

πŸ’¬ Code demonstration: use of cast

int main()
{
    int a = (int)3.14;

    return 0;
}

6, Relational operator

πŸ“Œ Precautions: be careful during programming = and = = errors caused by careless writing;

πŸ’¬ Code demonstration: generally used in conditional statements

int main()
{
    int a = 3;
    int b = 5;
    
    if(a < b) {
        ...
    }
    if(a == b) {
        ...
    }
    if(a <= b) {
        ...
    }
    if(a != b) {
        ...
    }
    
    return 0;
}

7, Logical operator

0x00 logic and&&

πŸ“š Note: the result is true only when logic and, a and b are true; (all true is true)

πŸ’¬ Code demonstration:

1. When both a and b are true, the result is true, c = 1;

int main()
{
    int a = 3;
    int b = 5;
    int c = a && b; // True is returned only when both logic and "and" a and b are true
    printf("%d\n", c);
    
    return 0;
}

🚩 Operation result: 1 (true)

2. As long as one of a and b is false, the result is false, c = 0;

int main()
{
    int a = 0;
    int b = 5;
    int c = a && b;
    printf("%d\n", c);

    return 0;
}

🚩 Operation structure: 0 (false)

0x01 logical or||

πŸ“š Note: if one of a and b is true, the result is true; (true if true)

πŸ’¬ Code demonstration:

1. As long as one of a and b is true, the result is true;

int main()
{
    int a = 0;
    int b = 5;
    int c = a || b; //True is returned only when both logic and "and" a and b are true
    printf("%d\n", c);

    return 0;
}

🚩 Operation result: 1 (true)

2. When a and b are false at the same time, the result is false;

int main()
{
    int a = 0;
    int b = 0;
    int c = a || b; //True is returned only when both logic and "and" a and b are true
    printf("%d\n", c); // 0

    return 0;
}

🚩 Operation result: 0 (false)

0x02 exercise

πŸ“ƒ Written test questions: (from 360)

❓   1. What is the result of the program output

int main()
{
    int i = 0, a=0,b=2,c=3,d=4;
    i = a++ && ++b && d++;
    printf("a=%d\n b=%d\n c=%d\n d=%d\n", a, b, c, d);

    return 0;
}

🚩 Operation result: a=1; b=2οΌ› c=3οΌ› d=4

πŸ”‘ Resolution:

First, the initial value of i is 0. When i = a + + & & + + b & & D + +, a + + is executed first, and the initial value of a is 0. Because it is the reason of post + +, at this time, a is still 0. When logic and encounter 0, it will not continue to execute, so the subsequent + + b and D + + do not count. When printing, because of a + +, a=1, and the printed result is a=1,b=2,c=3,d=4;

❓   2. What is the output of the program

int main()
{
    int i = 0, a=0,b=2,c=3,d=4;
    i = a++ || ++b || d++;
    printf("a=%d\n b=%d\n c=%d\n d=%d\n", a, b, c, d);

    return 0;
}

🚩 Operation result: a=1; b=3οΌ› c=3οΌ› d=5

πŸ”‘ Resolution:

i=0, when I = a + + ||+ + b || D + +, execute a + + first. Because it is a post + +, a is still 0 at this time, but because it is a logical or, it will continue to go down, + + b is the front + + and b is 3. If it is true, it will not continue to run down. D + + does not count. When printing, because a + +, D + +, a=1, the printed results are a=1,b=3,c=3,d=4;

πŸ”Ί Summary:

1. Logic and: stop in case of false; (as long as the left is false, the right is not)

2. Logic or: stop when you encounter the truth; (as long as the left is true, the right is not)

8, Conditional operator

πŸ“š Definition:

β‘  if the result of expression 1 is true, calculate expression 2;

β‘‘ if the result of expression 1 is false, calculate expression 3;

πŸ“Œ Note: do not write the ternary operator too complex, otherwise the readability will be poor;

πŸ’¬ Code demonstration:

1. if...else:

int main()
{
    int a = 3;
    int b = 0;
    
    if (a > 5)
        b = 1;
    else 
        b = -1;
        
    return 0;
}   

2. Convert the above code into a conditional expression:

int main()
{
    int a = 3;
    int b = 0;
    
    b = a>5 ? 1 : -1; // Conditional Operator 
    
    return 0;
}

πŸ’¬ Use conditional expressions to find the larger of two numbers:

int main()
{
    int a = 10;
    int b = 20;
    int max = 0;
    
    max = (a>b ? a : b );
    printf("max = %d", max);

    return 0;
}

9, Comma expression

❓ What is a comma expression

πŸ’‘ Comma expression, as the name suggests, multiple expressions separated by commas;

πŸ“š Definition: execute from left to right. The result of the whole expression is the result of the last expression;

πŸ’¬ Code demonstration: usage of comma expression

int main()
{
    int a = 1;
    int b = 2;
    int c = (a>b,  a=b+10,   a,    b = a+1);
//          No result 12 no result 12 + 1 = 13

    printf("%d\n", c);

    return 0;
}

🚩 Operation result: 13

πŸ’¬ Comma expression for judgment condition

if(a = b + 1, c = a / 2, d > 0)  // After execution from left to right, if d > 0, the condition is true

πŸ’¬ Application of comma expression: simplifying code structure

10, Subscript references, function calls, and structure members

0x00 subscript reference operator []

πŸ’¬ This is very simple. You can directly the code:

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    //             0 1 2 3 4 5 6 7 8 9
    printf("%d\n", arr[4]); // 5
    //                 ↑ the box here is the subscript reference operator;
    // The operands of [] are 2: arr, 4

    return 0;
}

0x01 function call operator ()

πŸ“š Function: accept one or more operands;

β‘  the first operand is the function name;

β‘‘ the remaining operands are the parameters passed to the function;

πŸ’¬ Code demonstration: function call operator

int Add(int x, int y)
{
    return x + y;
}

int main()
{
    int a = 10;
    int b = 20;
    int Add(a, b); // In this case, () is the function call operator;

    return 0;
}

0x02 structure member access operator - point operator

πŸ“š Role: access structure members;

😒 If you forget what structure is, you can go back to Chapter 1 (getting to know C language)

Β Β Β Β  https://blog.csdn.net/weixin_50502862/article/details/115426860οΌ›

πŸ’¬ Code demonstration: use of point operator

struct Book {
    char name[20];
    char id[20];
    int price;
};

int main()
{
    struct Book b = {"C language", "C20210509", 55};
    printf("title:%s\n", b.name);
    printf("Book No.:%s\n", b.id);
    printf("price:%d\n", b.price);
    
    return 0;
}

🚩 Running result: Book Title: C language
Book No.: C20210509
Pricing: 55

0x03 structure member access operator - arrow operator - >

πŸ“š Function: access members through structure pointers;

πŸ’¬ Code demonstration

1. It can still be written with dot operators, but it is slightly redundant; ❎ (yes, but not recommended)

πŸ“Œ Precautions: (* P) name βœ…    *p.name ❌ Pay attention to priority issues!

struct Book {
    char name[20];
    char id[20];
    int price;
};

int main()
{
    struct Book b = {"C language", "C20210509", 55};
    struct Book* pb = &b;
    printf("title:%s\n", (*pb).name);
    printf("Book No.:%s\n", (*pb).id);
    printf("price:%d\n", (*pb).price);
    
    return 0;
}

2. Use the arrow operator, which is more intuitive; βœ…

struct Book {
    char name[20];
    char id[20];
    int price;
};

int main()
{
    struct Book b = {"C language", "C20210509", 55};
    struct Book* pb = &b;
    printf("title:%s\n", pb->name);
    printf("Book No.:%s\n", pb->id);
    printf("price:%d\n", pb->price);

    return 0;
}

Chapter XI expression evaluation

The order in which expressions are evaluated is partly determined by the priority and associativity of operators. Similarly, the operands of some expressions may need to be converted to other types during the job search process.

0x00 implicit type conversion

❓ What is integer lift:

β‘  the integer arithmetic operation of C is performed at least with the precision of the default integer;

β‘‘ in order to obtain this precision, the characters and short integer operands in the expression are converted to ordinary integers before use. This conversion is called integer promotion;

β‘’ integer promotion: promote according to the sign bit of the data type of the variable;

πŸ”‘ Graphic integer lift:

❓ Then the problem comes again. How to improve the integer?

πŸ’‘ Integer promotion is carried out according to the sign bit of the data type of the variable;

πŸ“š Explanation of integer lifting (please read the steps in the notes carefully):

int main()
{
    // We found that both a and b are char types, and neither of them reaches the size of an int
    // πŸ”Ί An integer promotion will occur here
    char a = 3;
    //      00000000000000000000000000000011
    //      00000011 - a can only put 8 bits (truncated) because it is of char type πŸ”ͺ
    char b = 127;
    //      00000000000000000000000001111111
    //      01111111 - b ditto, truncated, 8 bits stored πŸ”ͺ

    char c = a + b;
    // Let's first look at the a symbol: char is signed and is a positive number. It is promoted according to the sign bit of the original variable
    // Then look at the b symbol: char is signed, which is a positive number. It is also supplemented by 0 when it is promoted
    //      000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    //   +  00000000000000000000000001111111
    // -------------------------------------
    //      00000000000000000000000010000001 (this result should be stored in c, and only 8 bits can be stored in c)
    // So truncate πŸ”ͺ
    // 10000010 - c (stored in C)   

    
    /* Then we have to print it */    
    printf("%d\n", c);
    // πŸ”Ί At this time, c an integer lift occurs:
    // Let's look at the sign of c. char is signed. It's a negative number. The high bit carries out integer promotion and complements 1
    //      10000010 - c / / then integer lift
    //      11111111111111111111111110000010 (results after 1 is completed)

    // πŸ”Ί Note: This is a negative number. The original inverse complement is different!
    // The printed code is the original code and the memory is the complement code. Now start to push back:
    //      11111111111111111111111110000010 (complement)
    //      11111111111111111111110000001 - inverse code (complement-1)
    //      00000001111110 - original code
    //      ==  -126

    return 0;
}

🚩 Operation result: - 126

πŸ’¬ Chestnut 1 of integer promotion: what is the result of running the following code (experience the existence of integer promotion)

int main()
{
    char a = 0xb6;
    short b = 0xb600;
    int c = 0xb600000;
    if(a == 0xb6)
        printf("a"); //nothing
    if(b == 0xb600)
        printf("b"); //nothing
    if(c == 0xb600000)
        printf("c"); //c
    return 0;
}

🚩 Operation result: c

❓ Why aren't a and b printed out

πŸ”‘ Resolution:

β‘  because a in the expression is of char type, integer promotion is required because it does not reach the integer size;

β‘‘ of course, the comparison after promotion will not be equal, so a and short will not be printed. Similarly, c will not be printed;

β‘’ there is another explanation: char a cannot be saved, so it is not 0xb6, so it is not printed;

πŸ’¬ Chestnut 2 of integer promotion: what is the result of running the following code (experience the existence of integer promotion)

int main()
{
    char c = 1;
    printf("%u\n", sizeof(c));  // 1
    printf("%u\n", sizeof(+c)); // 4 after integer promotion, it is equal to calculating the size of an integer
    printf("%u\n", sizeof(-c)); // 1
    printf("%u\n", sizeof(!c)); // 4  gcc-4
    
    return 0;
}

πŸ”‘ Resolution:

β‘  sizeof(c), C is char type, and the result is naturally 1;

β‘‘ sizeof(+c), + c participates in the operation, and the integer will be promoted, which is equivalent to calculating the size of an integer, so it is 4;

β‘’ sizeof(-c), the same as above, so it is 4;

β‘’ sizeof (! C), it is worth mentioning that the result of some editors may not be 4, but according to gcc, the answer is 4;

πŸ”Ί Conclusion:

β‘  from the above example ba, we can conclude that integer promotion does exist;

β‘‘ integer lifting is not required for those larger than int, and integer lifting is required for those smaller than int;

0x02 arithmetic conversion

πŸ“š Definition: if the operands of an operand belong to different types,

Then the operation cannot be performed unless one of the operands is converted to the type of the other operand;

πŸ“š Ordinary arithmetic conversion: if an operand type is low in the following table,

Then it must be converted to the type of another operand before the operation can be performed;

🌰 For example: (if variables of type int and float are put together, then convert int to float)

πŸ“Œ Note: the arithmetic conversion should be reasonable, otherwise it will cause potential problems;

πŸ’¬ Precision loss problem:

int main()
{
    float f = 3.14;
    int num = f; // Implicit conversion, there will be precision loss
    printf("%d\n", num); // 3

    return 0;
}

🚩   3

0x03 properties of operator

πŸ“š There are three factors that affect the evaluation of complex expressions:

β‘  operator priority;

β‘‘ associativity of operators;

β‘’ whether the evaluation sequence is controlled;

❓ Which of the two adjacent operators should be executed first? Depending on their priority, if they have the same priority,

πŸ’¬ Code demonstration: priority determines the calculation order

int main()
{
    int a = 3;
    int b = 5;
    int c = a + b * 7; // Priority decision: multiply before add

    return 0;
}

πŸ’¬ Code demonstration: the priority is the same. At this time, the priority does not work, and the combination determines the order

int main()
{
    int a = 3;
    int b = 5;
    int c = a + b + 7; // First on the left, then on the right

    return 0;
}

πŸ“š Operator priority table:

OperatordescribeUsage exampleCombination typeAssociativityControl evaluation order
( )Aggregation group(expression)Same as expressionN/A

no

( )function callrexp(rexp, ..., rexp)rexpL-Rno
[ ]Subscript referencerexp[rexp]lexpL-R

no

.Access structure memberslexp.member_namelexpL-Rno
->Access structure pointer memberrexp->member_namelexpL-R

no

++Suffix self incrementlexp++rexpL-R

no

--Suffix subtractionlexp--rexpL-Rno
!Logical inversion!rexprexpR-Lno
~Bitwise inversion~rexprexpR-Lno
+Monocular, indicating positive value+rexprexpR-Lno
-Monocular, indicating negative value-rexprexpR-Lno
++Prefix self increment++lexprexpR-Lno
--Prefix subtraction--lexprexpR-Lno
*Indirect access*rexplexpR-Lno
&Get address&lexprexpR-Lno
sizeofTake its length, expressed in bytesSizeof rexp szieof (type)

rexp

R-Lno
(type)Type conversion(type) rexprexpR-Lno
*multiplicationrexp*rexprexpL-Rno
/divisionrexp/rexp

rexp

L-Rno
%Integer remainderrexp%rexprexpL-Rno
+additionrexp+rexprexpL-Rno
-subtractionrexp-rexprexpL-Rno
<<Left shiftrexp<<rexprexpL-Rno
>>Right shiftrexp>>rexprexpL-Rno
>greater thanrexp>rexprexpL-Rno
>=Greater than or equal torexp>=rexprexpL-Rno
<less thanrexp<rexprexpL-Rno
<=Less than or equal torexp<=rexprexpL-Rno
==be equal torexp==rexprexpL-Rno
!=Not equal to

rexp!=rexp

rexpL-Rno
&Bit andrexp&rexprexpL-Rno
^Bit exclusive orrexp^rexprexpL-Rno
|Bit orrexp|rexprexp

L-R

no
&&Logic andrexp&&rexprexpL-Ryes
||Logical orrexp&&rexprexpL-Ryes
?:Conditional Operator rexp?rexp:rexprexpL-Ryes
=assignmentlexp=rexprexpN/V

yes

+=Plus equalslexp+=rexprexpR-Lno
-=Minus equalslexp-=rexprexpR-Lno
*=Multiply equallexp*=rexprexpR-Lno
/=Division equalslexp /= rexprexpR-Lno
%=With Take moldlexp %= rexprexpR-Lno

<<=

With Shift leftlexp <<= rexprexpR-Lno
>>=With Shift rightlexp >>= rexprexpR-Lno
&=With Andlexp &= rexprexpR-Lno
^=With XORlexp ^= rexprexpR-Lno
|=With orlexp |= rexprexpR-Lno
,commarexp, rexprexpL-Ryes

❌ Question expression:

❌ Illegal expression: (from C and pointer)

int main()
{
    int i = 10;
    i = i-- - --i * ( i = -3 ) * i++ + ++i;
    printf("i = %d\n", i);

    return 0;
}

πŸ”‘ Analysis: comparable to 10000 ways of writing fennel beans,

The running result of this kind of code depends on the environment. Don't write this kind of code!

task

0x00 multiple choice questions

❓ Which of the following is the bitwise operator ();

Β Β Β Β Β  A. &Β Β Β Β Β  B. &&Β Β Β Β Β  C. ||Β Β Β Β Β  D. !

0x01 analysis code

❓ What is the result of running the following code

#include <stdio.h>

int main()
{
	int a, b, c;
	a = 5;
	c = ++a;
	b = ++c, c++, ++a, a++;
	b += a++ + c;
	printf("a = %d b = %d c = %d\n:", a, b, c);

	return 0;
}

0x02 swap two variables (do not create temporary variables)

πŸ’¬ It is not allowed to create temporary variables and exchange the contents of two integers;

0x03 count the number of 1 in binary

πŸ’¬ Enter an integer and write a function to return the number of 1 in the 32-bit binary representation of the number, where the negative number is represented by complement.

(eg. 15 0000 1111 4 1)

πŸ‚ OJ link of niuke.com: Number of 1 in binary__ Niuke networkοΌ›

0x04 find the number of different bits in two binary numbers

πŸ’¬ Programming implementation: how many bits are different in the binary expression of m and n of two int (32-bit) integers?

(eg. input 1999 2299} output 7)

πŸ‚ OJ link of niuke.com: Two integers have different numbers of binary bits__ Niuke networkοΌ›

0x05 print odd and even digits of integer binary

πŸ’¬ Description: obtain all even and odd bits in an integer binary sequence, and print out the binary sequence respectively;

answer

0x00 question 1: multiple choice question

πŸ’‘ Correct answer: A

πŸ”‘ Resolution:

0x01 question 2: running results of the following codes

πŸ’‘ Correct answer: a = 9 , b = 23 , c = 8

πŸ”‘ Resolution:

0x02 question 3: do not create temporary variables to exchange two integer contents

πŸ’¬ It is not allowed to create temporary variables and exchange the contents of two integers;

πŸ’‘ Reference answer:

void Swap (
    int* pa,
    int* pb
    )
{
    *pa = *pa ^ *pb;
    *pb = *pa ^ *pb;
    *pa = *pa ^ *pb;
}

int main()
{
    int a = 10;
    int b = 20;
    printf("Before exchange: a = %d  b = %d\n", a, b);
    Swap(&a, &b);
    printf("After exchange: a = %d  b = %d\n", a, b);

    return 0;
}

0x03 question 4: count the number of 1 in binary

πŸ’¬ Enter an integer and write a function to return the number of 1 in the 32-bit binary representation of the number, where the negative number is represented by complement.

(eg. 15 0000 1111 4 1)

πŸ’‘ Reference answer:

1. Modular division

int CountNum1(int n)
{
    int count = 0;
    while(n) {
        if(n % 2 == 1) {
            count++;
        }
        n /= 2;
    }
    return count;
}

int main()
{
    int num = 0;
    scanf("%d", &num);
    int ret = CountNum1(num);
    printf("%d\n", ret);

    return 0;
}

2. Shift operator + bitwise and combination

int CountNum1(int n)
{
    int count = 0;
    int i = 0;
    for(i=0; i<32; i++) {
        if( ((n>>i) & 1) == 1) {
            count++;
        }
    }
    return count;
}

int main()
{
    int num = 0;
    scanf("%d", &num);
    int ret = CountNum1(num);
    printf("%d\n", ret);

    return 0;
}

3.Β  &=

int CountNum1(int n) 
{
    int count = 0;
    while(n) {
        n = n & (n - 1);
        count++;
    }
    return count;
}

int main()
{
    int num = 0;
    scanf("%d", &num);
    int ret = CountNum1(num);
    printf("%d\n", ret);

    return 0;
}

0x04 question 5: find the number of different bits in two binary numbers

πŸ’¬ Programming implementation: how many bits are different in the binary expression of m and n of two int (32-bit) integers?

(eg. input 1999 2299} output 7)

1. > > & shift by bit and

int main()
{
    int m = 0;
    int n = 0;
    scanf("%d %d", &m, &n);

    int count = 0;
    int i = 0;
    for(i=0; i<32; i++) {
        if( ((m >> i) & 1) != ((n >> i) & 1) ) {
            count++;
        } 
    }
    printf("%d\n", count);

    return 0;
}

2. XOR method, and then count how many 1 in the binary

int NumberOf1(int n) 
{
    int count = 0;
    while(n) {
        n = n & (n - 1);
        count++;
    }
    return count;
}

int main()
{
    int m = 0;
    int n = 0;
    scanf("%d%d", &m, &n);

    int count = 0;
    int ret = m ^ n; // The same is 0 and the difference is 1
    // Statistics show that there are several 1s in the binary of ret, which shows that there are several different positions in the binary of m and n
    count = NumberOf1(ret);
    printf("%d\n", count);

    return 0;
}

0x05 question 6: print odd and even digits of integer binary

πŸ’¬ Description: obtain all even and odd bits in an integer binary sequence, and print out the binary sequence respectively;

int main()
{
    int n = 0;
    scanf("%d", &n);
    // Gets the odd and even digits in the binary of n
    int i = 0;
    // Print even digits
    for(i=31; i>=1; i -= 2) {
        printf("%d ", (n >> i) & 1);
    }
    printf("\n");
    // Print odd digits
    for(i=30; i>=0; i-=2) {
        printf("%d ", (n >> i) & 1);
    }

    return 0;
}

Chapter 6 - pointers

preface:

This chapter is the beginning of the pointer part, which will explain the very important pointer in C language. At the end of this chapter, capable readers can further study the advanced part of the pointer. The pointer project is equipped with some written test questions. It is recommended to try.

[vitamin C language] Chapter 10 - Advanced pointer (Part 1)

[vitamin C language] Chapter 10 - Advanced pointer (Part 2)

Analysis of "Zuan code Hunter" online hand tearing code drawing [C pointer written test question]

1, Definition of pointer

​

0x00 what is a pointer

❓ Let's first look at the definition:

A pointer is an object in a programming language. Using an address, its value directly points to a value stored in another place in the computer memory. Since the required variable unit can be found through the address, it can be said that the address points to the variable unit. Therefore, the address visualization is called "pointer". It means that the memory unit with its address can be found through it.

πŸ“š Simply put: the pointer is the address, and the address is the pointer;

πŸ“Œ matters needing attention:

β‘  the pointer is a variable used to store the address (the values stored in the array are treated as addresses);

β‘‘ the size of a small memory unit is 1 byte;

β‘’ the pointer is used to store the address, which uniquely identifies a piece of memory space;

β‘£ the size of pointer is 4 bytes on 32-bit platform and 8 bytes on 64 bit platform;

πŸ’¬ Pointer creation:

int main()
{
    int a = 10;   // Open up a space in memory
    int* pa = &a; // Using the dereference operator &, take the address of variable a
    // πŸ‘†  Store the address of a in the pa variable, where pa is a pointer variable
    return 0;
}

❓ What is a pointer variable

πŸ’‘ Pointer variable is the variable that stores the pointer. Here, int* pa is an integer pointer variable, in which the address of a is stored;

0x02 size of pointer

πŸ’» 32-bit platform: 4 bit, 64 bit platform: 8 bit;

πŸ’¬ Verify the current system pointer size:

int main()
{
    printf("%d\n", sizeof(char*));
    printf("%d\n", sizeof(short*));
    printf("%d\n", sizeof(int*));
    printf("%d\n", sizeof(double*));

    return 0;
}

2, Pointer type of pointer

0x00 pointer type

πŸ“š Both int pointer and char pointer can store a;

int main()
{
    int a = 0x11223344;
    int* pa = &a;
    char* pc = &a;
    printf("%p\n", pa);
    printf("%p\n", pc);

    return 0;
}

🚩 Running results: their running results are the same

0x01 meaning of pointer type

πŸ“š The pointer type determines the memory size that can be accessed when the pointer is dereferenced;

​

πŸ’¬ Different pointer types have different access sizes:

int main()
{
    int a = 0x11223344;
    int* pa = &a; // 44 33 22 11 (as for why it's upside down, I'll talk about it later.)
    *pa = 0;// 00 00 00 00
    
    char* pc = &a; // 44 33 22 11
    *pc = 0; // 00 33 22 11
    // Only one byte has been changed in memory

    // The dereference operation is different
    // The integer pointer operates on 4 bytes, making 4 bytes 0
    // The character pointer can put the address into memory,
    // However, during dereference operation, only one byte can be moved

    return 0;
}

0x02 pointer plus or minus integer

πŸ“š Theorem: the pointer type determines the pointer step size (how far the pointer goes one step);

​

πŸ’¬ Code verification: the pointer type determines the pointer step size;

int main()
{
    int a = 0x11223344;
    int* pa = &a;
    char* pc = &a;

    printf("%p\n", pa);   // 0095FB58
    printf("%p\n", pa+1); // 0095FB5C +4

    printf("%p\n", pc);   // 0095FB58
    printf("%p\n", pc+1); // 0095FB59 +1
    
    return 0;
}

0x03 pointer to modify array elements

πŸ’¬ Change all the elements in the array to 1

1. Use integer pointer:

int main()
{
    int arr[10] = {0};
    int* p = arr; //Array name - first element address

    /* modify */
    int i = 0;
    for(i=0; i<10; i++) {
        *(p+i) = 1; //Success, the elements in arr become 1
    }

    /* Print */
    for(i=0; i<10; i++) {
        printf("%d ", arr[i]);
    }

    return 0;
}

🚩  1 1 1 1 1 1 1 1 1 1

2. Use character pointer:

int main()
{
    int arr[10] = {0};
    char* p = arr; //Array name - first element address

    /* modify */
    int i = 0;
    for(i=0; i<10; i++)
    {
        *(p+i) = 1; // One byte changed, only ten bytes changed
    }
    
    return 0;
}

πŸ’‘ Resolution:

​

πŸ”Ί Summary:

β‘  the type of pointer determines how much permission is available to dereference the pointer (several bytes can be operated);

β‘‘ for example, the pointer dereference of char * can only access 1 byte, while the pointer dereference of int * can access 4 bytes

3, Wild pointer

0x00 concept of field pointer

​

πŸ“š Concept: Wild pointer means that the position pointed to by the pointer is unknown (random, incorrect and without definite limit);

The wild pointer points to a random memory space, which is not controlled by the program;

0x01 causes of field pointer

πŸ“š reason:

β‘  the pointer is not initialized;

β‘‘ pointer cross-border access;

β‘’ the space pointed by the pointer has been released;

πŸ’¬ Pointer not initialized

∡ local variables are not initialized and default to random values:

int main()
{
    int a;//Local variables are not initialized and default to random values
    printf("%d", a);

    return 0;
}

In the same way, if the local pointer variable is not initialized, it defaults to the random value:

int main()
{
    int *p; //Local pointer variables are initialized to random values
    *p = 20; //Just find an address in the memory and save it

    return 0;
}

πŸ’¬ Pointer out of bounds access

The pointer is out of bounds. When it exceeds the arr management range, a wild pointer will be generated:

int main()
{
    int arr[10] = {0};
    int *p = arr;
    int i = 0;
    for(i=0; i<12; i++)
    {
        //When the pointer goes beyond the scope of arr management, p is called a wild pointer
        p++;
    }

    return 0;
}

πŸ’¬ The space pointed to by the pointer has been freed

int* test()
{
    int a = 10;

    return &a;
}

int main()
{
    int *pa = test();
    *pa = 20;

    return 0;
}

πŸ”‘ Resolution:

β‘  a temporary variable a (10 - 0x0012ff44) is created as soon as it enters the test function. This a is a local variable. It is created when it enters the range. Once it goes out, it is destroyed. Destruction means that the memory space is returned to the operating system, and this space (0x0012ff44) is no longer owned by a;

β‘‘ when you enter this function, you create a and have an address. Ruturn & A returns the address. However, as soon as the function ends, this space does not belong to you. When you use it, this space has been released and the space pointed to by the pointer is pointed out. This situation will lead to the problem of wild pointer;

β‘’ as long as the address of the temporary variable is returned, there will be problems (unless the variable is out of this range, it will not be destroyed);

0x02 how to avoid wild pointer

​

πŸ’¬ Pointer initialization

int main()
{
    int a = 10;
    int* pa = &a;  // initialization
    int* p = NULL; // Use NULL when you don't know what value to give
     
    return 0;
}

πŸ’¬ Pointer to space release time set to NULL

int main()
{
    int a = 10;
    int *pa = &a;
    *pa = 20;

    //Let's say that we have operated on a and the pa pointer is no longer going to use it
    pa = NULL; //Set to null pointer
    
    return 0;
}

πŸ’¬ Check the validity of the pointer before use

int main()
{
    int a = 10;
    int *pa = &a;
    *pa = 20;

    pa = NULL; 
    //*pa = 10;  Crash, access error, cannot access when pointer is null
    if(pa != NULL) { // Check if the pointer is not a null pointer
        *pa = 10; // Only after passing the inspection
    }  
        
    return 0;
}

4, Pointer operation

0x00 pointer plus integer

πŸ’¬ Pointer plus integer: print 1 2 3 4 5 6 7 8 9 10

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    int* p = arr; // Point to the first element of the array - 1

    for(i=0; i<sz; i++) {
        printf("%d ", *p);
        p = p + 1; //p + + points to 2 after the first loop + 1
    }

    return 0;
}

🚩  1 2 3 4 5 6 7 8 9 10

0x01 pointer minus integer

πŸ’¬ Pointer minus integer: print 10 8 6 4 2

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    int* p = &arr[9]; // Gets the address of the last element of the array
    
    for(i=0; i<sz/2; i++) {
        printf("%d ", *p);
        p = p - 2;
    }
    return 0;
}

0x02 pointer post++

#define N_VALUES 5

int main()
{
    float values[N_VALUES];
    float *vp;
    // Pointer + - integer; Relational operation of pointer
    for(vp = &values[0]; vp < &values[N_VALUES] {
        *vp++ = 0; // Adjusted here, post++
    }

    return 0; 
}

0x03 pointer minus pointer

πŸ“š Note: the number of elements between elements is obtained by subtracting the pointer from the pointer;

πŸ“Œ Note: when the pointer subtracts the pointer, they must point to the same space (such as the space of the same array);

πŸ’¬ Pointer minus pointer:

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    printf("%d\n", &arr[9] - &arr[0]); // Get the number of elements between the pointer and the pointer

    return 0;
}

🚩  9

❌ Error presentation: not in the same memory space

int ch[5] = {0};
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\n", &arr[9] - &ch[0]); // Meaningless, the result is unpredictable

πŸ’¬ Handwritten strlen function (implemented by pointer method):

int my_strlen(char* str)
{
    char* start = str;
    char* end = str;
    while(*end != '\0') {
        end++;
    }
    return end - start; //return
}
int main()
{
    //strlen - find string length
    //Recursive simulation implements strlen - counter mode 1 and recursive mode 2

    char arr[] = "abcdef";
    int len = my_strlen(arr); //arr is the address of the first element
    printf("%d\n", len);

    return 0;
}

⚑ Simplification (how library functions are written):

int my_strlen(const char* str)
{
    const char* end = str;
    while(*end++);
    return (end - str - 1);
}

int main()
{
    char arr[] = "abcdef";
    int len = my_strlen(arr);
    printf("%d\n", len);

    return 0;
}

0x04 relational operation of pointer (compare size)

πŸ’¬ Pointer subtraction pointer:

#define N_VALUES 5

int main()
{
    float values[N_VALUES];
    float *vp;

    for(vp=&values[N_VALUES]; vp> &values[0]; ) {
        *--vp = 0; //Front--
    }
    
    return 0;
}

⚑ Simplification (this is easier to understand. The above code * -- vp starts to access at the position after the maximum index):

int main()
{
    float values[5];
    float *vp;

    for(vp=&values[N_VALUES]; vp> &values[0]; vp--) {
        *vp = 0;
    }
    
    return 0;
}

❗ In fact, it is feasible on most compilers, but we should avoid it because the standard does not guarantee its enforceability

πŸ”‘ Analysis: the standard stipulates that the pointer pointing to the array element is allowed to be compared with the pointer pointing to the memory location after the last element of the array, but it is not allowed to be compared with the pointer pointing to the memory location before the first element;

​

5, Pointers and arrays

0x00 array name

πŸ“š In most cases, the array name is the first element address;

πŸ’¬ In most cases, the array name is the first element address: arr is equivalent to & arr [0]

int main()
{
    int arr[10] = {0};
    printf("%p\n", arr); // The array name is the address and the first element address
    printf("%p\n", &arr[0]); // The results are the same as above
    printf("%p\n", &arr);  // Look at the "exceptions" below

    return 0;
}

🚩 00EFF8E0} 00EFF8E0} 00EFF8E0 (this is the address of the entire element)

πŸŒ‚ Exceptions:

1. & array name (& ARR): the array name is not the address of the first element, but represents the whole array:

πŸ’¬ & Array name - the array name represents the entire array:

int main()
{
    int arr[10] = {0};
    printf("%p\n", arr);
    printf("%p\n", arr+1);

    printf("%p\n", &arr[0]); 
    printf("%p\n", &arr[0]+1);

    printf("%p\n", &arr);
    printf("%p\n", &arr + 1); // +1, in units of the whole element

    return 0;
}

🚩 The operation results are as follows:

​

2. sizeof (array name): calculates the size of the entire array in bytes

πŸ’¬ sizeof (array name): the array name represents the entire array:

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int sz = sizeof(arr) / sizeof(arr[0]);
    printf("%d\n", sz);
    
    return 0;
}

🚩  10

πŸ”Ί Summary: array name is the address of the first element (except & array name} and sizeof array name);

0x01 access array with pointer

πŸ’¬ p+i calculates the address of array arr subscript i:

int main()
{
    int arr[10] = {0};
    int* p = arr;  //At this point, the arr array can be accessed through the pointer
    int i = 0;

    for(i=0; i<10; i++) {
        printf("%p == %p\n", p+i, &arr[i]);
    }

    return 0;
}

🚩 The operation results are as follows:

​

πŸ’¬ Generate 0 1 2 3 4 5 6 7 8 9

int main()
{
    int arr[10] = {0};
    int* p = arr;  // At this point, the arr array can be accessed through the pointer
    int i = 0;

    printf("Before generation:\n");
    for(i=0; i<10; i++) {
        printf("%d ", arr[i]);
    }

    for(i=0; i<10; i++) {
        *(p+i) = i; // p+1=1, p+2=2, p+3=3...
        // πŸ‘†  arr[i] = i;  Equivalent to
    }

    printf("\n After generation:\n");
    for(i=0; i<10; i++) {
        printf("%d ", arr[i]);
        // πŸ‘†  printf("%d ", *(p + i));  Equivalent to
    }

    return 0;
}

🚩 The operation results are as follows:

​

πŸ’¬ Generate 6 6 6 6 6 6 6 6 6

int main()
{
    int arr[10] = {0};
    int* p = arr;  // At this point, the arr array can be accessed through the pointer
    int i = 0;


    for(i=0; i<10; i++) {
        *(p+i) = 8; // p+1=1, p+2=2, p+3=3...
    }
    for(i=0; i<10; i++) {
        printf("%d ", *(p + i));
    }

    return 0;
}

🚩  6 6 6 6 6 6 6 6 6 6

6, Second Rank Pointer

0x00 concept of secondary pointer

πŸ“š Concept: pointer variable is also a variable. If it is a variable, it has an address. The address of the pointer is stored in the secondary pointer;

​

πŸ’¬ Secondary pointer:

int main()
{
    int a = 10;
    int* pa = &a;
    int** ppa = &pa; // ppa is the secondary pointer
    int*** pppa = &ppa; // pppa is a three-level pointer
    ...
    
    **ppa = 20;
    printf("%d\n", *ppa); // 20
    printf("%d\n", a);  // 20

    return 0;
}

πŸ”‘ For the operation of secondary pointer:

β‘  by dereferencing the address in the ppa, * ppa finds pa, which is actually accessed by * ppa;

β‘‘ * * ppa first finds pa through * ppa, and then dereferences Pa. what * PA finds is a;

7, Pointer to Array

0x00 concept of pointer array

πŸ“š Concept: pointer array is essentially an array, which stores pointers;

πŸ“Œ Note: don't confuse it with array pointer. Array pointer is essentially a pointer;

​

❓ Analyze the following array:

int arr1[5];
char arr2[6];
int* arr3[5];

πŸ”‘ Resolution:

β‘  arr1 is an integer array with 5 elements, and each element is an integer;

β‘‘ arr2 is a character array with 6 elements, and each element is a char type;

β‘’ arr3 is an integer pointer array with 5 elements, and each element is an integer pointer;

Chapter VII - structure

preface:

This chapter will simply learn about structures. Later, we will further learn about structures in the chapter on user-defined types. Because there are few knowledge points in this chapter, the creation and destruction of function stack frames are briefly introduced at the end of the article.

1, Definition of structure

0x00 basic knowledge of structure

πŸ“š Knowledge points:

β‘  structure is a set of values, which are called member variables;

β‘‘ each member of the structure can be a variable of different types;

0x01 declaration of structure

πŸ“Œ Precautions:

β‘  member variables are separated by semicolons, and global variables are separated by commas;

β‘‘ a semicolon must be added after the brace at the end of the structure (even if the global variable is not written);

πŸ’¬ Declaration of structure: use structure to describe a student

Describing a student needs some data: name, age, gender, student number

/* struct Structure keyword Stu - structure label struct Stu - structure type */
struct Stu {
    /* Member variable */
    char name[20]; // Define a structure type
    int age;
    char sex[10];    
    char id[20];
} s1, s2, s3; // S1, S2 and S3 are three global structure variables
//          πŸ‘†  Remind again, you can't lose the semicolon!

int main()
{
    struct Stu s; // Create structure variable
//             πŸ‘†  s is the local structure variable
     
    return 0;
}

0x02 data type definition typedef

typedef struct Stu {
    char name[20];
    int age;
    char sex[10];    
    char id[20];
} Stu;

int main()
{
    Stu s1; // When added, it can be used as a separate type
    struct Stu s2;// Not affect
    
    return 0;
}

2, Use of structure

0x00 structure initialization

πŸ“š Initialization method: use braces to initialize the structure;

typedef struct Stu {
    char name[20]; // Define a structure type
    int age;
    char sex[10];    
    char id[20];
} Stu;

int main()
{
    Stu s0 = {}; // Initialize with braces
    Stu s1 = {"CSDN", 20, "male", "20200408"};
    Stu s2 = {"Vegetarian cattle", 21, "male", "20201214"};

    return 0;
}

0x01 access to structure members

πŸ“š Methods: structural variable access members (members of structural variables) are accessed through point operators;

❓ What is a point operator: Chapter 5 - operators (X. 0x02)

                                           πŸ‘† Jab!

πŸ’¬ The point operator receives two operands:

❓ We can see that the created local variable s contains name and age. How can we access the members of S?

πŸ’‘ By point operator (.) To access:

struct Stu{
    char name[20];
    int age;
};

int main()
{
    struct Stu s = {"mole", 13};
    printf("%s\n", s.name);
//                  πŸ‘†  Use the dot operator to access the name member
    printf("%d\n", s.age);
//                  πŸ‘†  Access age members using point operators

    return 0;
}

🚩  mole  13

0x02 type of structure member

πŸ“š The members of a structure can be scalars, arrays, pointers, or even other structures;

struct Stu {
    char name[20];
    int age;
    char sex[10];    
    char id[20];
};

struct School {
    char school_name[30]; // array
    struct Stu s; // Structure πŸ‘ˆ  No problem at all~
    char *pc; // address
};

int main()
{
    char arr[] = "earth\n";
    struct School JLD = {"Stay at home and go to college", {"Xiao Ming", 18, "male", "20201353"}, arr};
    
    printf("School name - %s\n", JLD.school_name);
    printf("student - full name:%s, Age:%d, Gender:%s, Student number:%s\n",
            JLD.s.name,
            JLD.s.age,
            JLD.s.sex,
            JLD.s.id
            );
    printf("address - %s\n", JLD.pc);
    
    return 0;
}

🚩 The operation results are as follows:

0x03 definition and initialization of structure variables

πŸ“š Definition method:

β‘  when defining a structure (global);

β‘‘ define (local) when creating structural variables;

πŸ’¬ Definition of structure variable:

struct Point {
    int x;
    int y;
} p1; 
//πŸ‘†  Define the variable p1 while declaring the type

int main()
{
    struct Point p2; // πŸ‘ˆ  Define structure variable p2

    return 0;
}

πŸ“š initialization:

πŸ’¬ Define variables and assign initial values at the same time:

struct Point {
    int x;
    int y;
} p1; 

int main()
{
    struct Point p3 = {10, 20}; // Initialization: define variables and assign initial values at the same time

    return 0;
}

πŸ’¬ Structure nesting initialization:

struct Point {
    int x;
    int y;
} p1;

struct Node {
    int data;
    struct Point p;
    struct Node* next;
} n1 = {10, {5, 6}, NULL}; // Structure nesting initialization

int main()
{
    struct Node n2 = {20, {5, 6}, NULL}; // Structure nesting initialization
//                         πŸ‘† x,y

    return 0;
}

0x04 structure parameter transfer

πŸ“š Parameter transmission form:

β‘  transmitting structure (receiving with structure);

β‘‘ address transmission (received with structure pointer);

πŸ“œ If you select a forwarding address, you can access it through the arrow operator

  ❓ what! Forget?

Β  Chapter 5 - operators (X. 0x03)

                                                                          πŸ‘† Poke in and have a look!

πŸ’¬ Transmission structure:

typedef struct Stu {
    char name[20];
    short age;   
    char sex[10];
    char id[20];
} Stu;

void print_by_s(Stu tmp) // πŸ‘ˆ  Receive using structure
{
    printf("full name: %s\n", tmp.name);
    printf("Age: %d\n", tmp.age);
    printf("Gender: %s\n", tmp.sex);
    printf("Student number: %s\n", tmp.id);
}


int main()
{
    Stu s1 = {"Zhang San", 21, "male", "20204344"}; // Initialize with braces
    /* Print structure data */
    print_by_s ( s1 ) ; // Transmission structure

    return 0;
}

🚩 The operation results are as follows:

πŸ’¬ Address:

typedef struct Stu {
    char name[20];
    short age;   
    char sex[10];
    char id[20];
} Stu;

void print_by_a(Stu* ps) // πŸ‘ˆ  Use address reception
{
    printf("full name: %s\n", ps->name);
    printf("Age: %d\n", ps->age);
    printf("Gender: %s\n", ps->sex);
    printf("Student number: %s\n", ps->id);
}

int main()
{
    Stu s1 = {"Zhang San", 21, "male", "20204344"}; // Initialize with braces
    /* Print structure data */
    print_by_a ( &s1 ) ; //Transmission address

    return 0;
}

🚩 The operation results are as follows:

πŸ’‘ We find that the results of this method are the same, so the problem comes:

❓ Which method is better to pass structure and address? Answer: the address is better;

πŸ”‘ Resolution:

β‘  understand everything, because when the function passes parameters again, the parameters need to be pressed on the stack; If the structure is too large when passing a structure object, the system overhead of parameter stack pressing will be large, which will lead to performance degradation!

Secondly, the efficiency of transmitting address is high and easy to modify;

πŸ’­ Briefly introduce the creation and destruction of function stack frames (a blog will be updated later for details):

πŸ”Ί Conclusion: when the structure transmits parameters, the address of the structure should be transmitted;

Chapter 8 - Practical debugging skills

preface:

An excellent programmer is an excellent detective. Every debugging is a process of trying to solve a case... This chapter will take you to learn practical debugging skills in detail! Officially open DEBUG life.

1, Debug

0x00 what is debugging

An excellent programmer is an excellent detective. Every debugging is a process of trying to solve a case

πŸ“š Definition: debugging, also known as debugging, is a process of discovering and reducing program errors in computer programs, electronic instruments and equipment;

0x01 basic steps of debugging

πŸ“š Basic steps:

β‘  find the existence of program errors;

           βœ… People who can find mistakes:

(1) programmers, find out by themselves;

(2) software tester, testing software;

(3) users, the cost is serious;

           πŸ“œ Proverb: be good at admitting your mistakes and can't cover them up;

β‘‘ locate errors by means of isolation and elimination;

            βœ… Be able to know the approximate location, and then determine the cause of the error;

β‘’ propose solutions to correct errors;

β‘£ correct program errors and re debug;

2, Introduction to Debug and Release

0x00 comparison

πŸ“š Debug is usually called debugging version. It contains debugging information and does not make any optimization, which is convenient for programmers to debug programs;

πŸ“š Release is called release version. It often carries out various optimizations to make the program optimal in code size and running speed, so that users can use it better;

πŸ“Œ Note: the Release version cannot be debugged;

πŸ’¬ Run Debug and Release respectively:

int main()
{
    char* p = "hello,world!";
    printf("%s\n", p);

    return 0;
}

🚩 The running results in the Debug environment are as follows:

🚩 The operation results under the Release environment are as follows:

πŸ’‘ We can find that: Release is optimized to make the program optimal in running speed and code size;

πŸ’¬ Comparison between Debug and Release disassembly display:

0x01 Release Optimization

❓ What optimizations did the editor make when debugging with the Release version?

πŸ’¬ See the following code:

int main()
{
    int arr[10] = {0};
    int i = 0;
    for(i=0; i<=12; i++) {
        arr[i] = 0;
        printf("hehe\n");
    }

    return 0;
}

🚩 If you compile in debug mode, the program results in an endless loop:

🚩 If you compile in release mode, the program does not have an endless loop:

πŸ’‘ Because of the optimization of release, the occurrence of dead cycle is avoided;

3, Introduction to Windows environment debugging

0x00 preparation of commissioning environment

πŸ“š Select the debug option in the environment to make the code debug normally;

πŸ“Œ Note: this chapter uses VS2019 for demonstration;

0x01 start debugging (F5)

βœ… Shortcut key: F5

πŸ“š Function: start debugging, often used to directly adjust to the next breakpoint;

πŸ“Œ matters needing attention:

β‘  if you press F5 directly, if there is no blocking, the program will be finished in one breath;

β‘‘ before using F5, use F9 to set breakpoints;

πŸ’¬ Press F5 to start debugging the following code:

int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	int i = 0;
	for (i = 0; i < sz; i++) {
		arr[i] = i + 1;
	}
	for (i = 0; i < sz; i++) {
		printf("%d\n", arr[i]);
	}

	return 0;
}

🚩 The results after operation are as follows:

0x02 breakpoint (F9)

βœ… Shortcut key: F9

πŸ“š Function: create breakpoints and cancel breakpoints. The important function of power failure is to set breakpoints at any position of the program; In this way, the program can be stopped at the desired position, and then it can be executed step by step;

πŸ’¬ Press F9 to set the breakpoint

🚩 At this time, pressing F5 will directly jump to the breakpoint:

0x03 process by process (F10)

βœ… Shortcut key: F10

πŸ“š Function: it is usually used to process a procedure. A procedure can be a function call or a statement;

πŸ’¬ Process by process:

πŸ’‘ Press F10 code once to go to the next step;

0x04 statement by statement (F11)

βœ… Shortcut key: F11 (this is the most commonly used)

πŸ“š Function: execute a statement every time. The observation is more delicate than F10, and you can enter the function;

πŸ“Œ Note: most cases of F10 and F11 are the same. The difference is that F11 can go inside the function when encountering the function, and the interior of the function can also be observed step by step, while F10 jumps out after encountering the function call;

πŸ’¬ Inside the observation function:

πŸ’‘ If you want to observe the inside of the function, use F11 (statement by statement);

0x05 start execution without debugging (Ctrl + F5)

βœ… Shortcut keys: Ctrl + F5

πŸ“š Function: start execution without debugging. If you want the program to run directly without debugging, you can use it directly;

0x06 summary

F5 - start up commissioning

F9 - set / cancel breakpoint

F10 - process by process

F11 - sentence by sentence - more subtle

Ctrl + F5 - run

πŸ“Œ Note: if pressing the shortcut key above does not work, it may be caused by the auxiliary function key (Fn). At this time, press Fn and then press the shortcut key above;

❓ Want to know more shortcuts?

Shortcut keys commonly used in VS   πŸ‘ˆ Poke me!

0x07 view program current information during debugging

πŸ’¬ Viewing method: debug → window (W) → select the corresponding option;

πŸ“Œ Note: the options in the debugging window will be displayed only after debugging;

0x08 viewing breakpoints

πŸ“š Function: when debugging multiple files, you can well manage the breakpoints of multiple files;

0x09 monitoring

πŸ“š Function: after debugging, it is convenient to observe the value of variables;

πŸ“Œ Note: fill in the legal expression;

πŸ’¬ Monitoring actions (add manually):

0x0A automatic window

πŸ“š Function: the editor monitors itself and automatically gives values along with the code;

πŸ“Œ matters needing attention:

β‘  the automatic window has the same effect as monitoring, but the expression in the automatic window will change automatically;

β‘‘ the degree of freedom is low, and the automatic window is monitored by the editor, which you can't control;

0x0B view local variables

πŸ“š Function: view the local variables of the context when the program reaches the current position, and the editor can independently put them into the window for corresponding interpretation. There are only local variables and arrays;

πŸ’¬ To view local variables:

0x0C view memory information

πŸ“š Function: used to observe memory information after debugging;

πŸ’¬ View memory information:

0x0D view call stack

πŸ“š Function: through the call stack, you can clearly reflect the call relationship and position of the function;

πŸ’¬ To view the call stack:

0x0E view assembly information

πŸ“š After debugging, there are two ways to go to assembly:

β‘  the first method: right click the mouse and select "go to disassembly"

β‘‘ the second method: debugging → window → disassembly

πŸ’¬ View disassembly:

0x0F view register information

πŸ“š Function: you can view the practical information of the registers of the current running environment;

πŸ’¬ View register:

0x10 conditional breakpoint

❓ Suppose a loop needs to be cycled 1000 times, and I suspect there will be a problem with the program in the 500th loop. Should I hit the breakpoint and then press F10 500 times? So your fingers can't be broken?

πŸ’‘ Methods: use conditional breakpoints;

πŸ’¬ After the breakpoint is set, right-click the mouse and select the condition:

🐞 After pressing F5, i will change directly to 5:

0x11 understanding of debugging

πŸ“œ Proverb:

β‘  more hands-on and try debugging can make progress;

β‘‘ be sure to master the debugging skills;

β‘’ beginners may spend 80% of their time writing code and 20% of their time debugging.

But a programmer may spend 20% of his time writing programs, but 80% of his time debugging;

β‘£ we are talking about some simple debugging.

In the future, there may be very complex debugging scenarios: multi-threaded program debugging, etc;

β‘€ use more shortcut keys to improve efficiency;

4, Some examples of debugging

0x00 instance 1

πŸ’¬ Implementation code: seek 1+ 2! + 3! ··· + n! (overflow is not considered)

int main()
{
	int n = 0;
	scanf("%d", &n); // 3
	// 1!+ 2!+ 3!
	// 1    2    6  =  9
	int i = 0;
	int ret = 1;
	int sum = 0;
	int j = 0;
	for (j = 1; j <= n; j++) {
		for (i = 1; i <= j; i++) {
			ret *= i;
		}
		sum += ret;
	}
	printf("%d\n", sum);

	return 0;
}

🚩 The operation results are as follows:

❓ The result should be 9, but the output result is 15, and the code is wrong; The code has no syntax errors. The code can run, which belongs to runtime errors, and debugging solves runtime errors;

🐞 At this point, we try to debug:

πŸ’‘ At this point, we found a problem: each factorial should be multiplied from 1, so ret should be set to 1 each time we enter;

int main()
{
	int n = 0;
	scanf_s("%d", &n); // 3
	// 1!+ 2!+ 3!
	// 1    2    6  =  9
	int i = 0;
	int ret = 1;
	int sum = 0;
	int j = 0;
	for (j = 1; j <= n; j++) {
		ret = 1; // Each time you enter, set it to 1 and start multiplying again
		for (i = 1; i <= j; i++) {
			ret *= i;
		}
		sum += ret;
	}
	printf("%d\n", sum);

	return 0;
}

🚩 The operation results are as follows:

πŸ”Ί solve the problem:

β‘  know what the program should be: expected results

β‘‘ when it is found that it does not meet the expectations during commissioning, the problem is found;

0x01 instance 2

πŸ’¬ What is the result of running the following code?

int main()
{
	int i = 0;
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//                       πŸ‘‡  Cross border visit
	for (i = 0; i <= 12; i++) {
		printf("hehe\n");
		arr[i] = 0;
	}
 
	return 0;
}

🚩 The operation results are as follows:

❓ Study the causes of the dead cycle:

πŸ’‘ Resolution:

πŸ”Ί The correct answer to this question: dead loop, because i and arr are local variables. First create i and then create arr, and because the local variables are placed on the stack area, the usage habit of the stack area is to use the high address first and then the low address, so the memory layout is like this (as shown in the figure), and because the address of the array changes from low to high with the increase of subscript, Therefore, when the array is accessed with subscript, as long as it crosses the boundary appropriately, it may cover i, and if i is covered, it will lead to the dead loop of the program;

5, How to write code that is easy to debug (simulate the implementation of strcpy)

0x00 excellent code

0x01 common coding skills

0x02 introduction to strcpy function

/* strcpy: String copy */
#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[20] = "xxxxxxxxxx";
	char arr2[] = "hello";
	strcpy(arr1, arr2);   // String copy (destination string, source string)
	printf("%s\n", arr1); // hello

	return 0;
}

0x03 analog implementation strcpy

πŸ’¬ Example - Simulated Implementation of strcpy

#include <stdio.h>

char* my_strcpy (
	char* dest, // Target string
	char* src   // Source string
	)
{
	while (*src != '\0') {
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src; // Copy '\ 0'
}

int main()
{
	char arr1[20] = "xxxxxxxxxx";
	char arr2[] = "hello";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1); // hello

	return 0;
}

0x04 optimization - improve code simplicity

πŸ’¬ The code of the function part and the + + part can be integrated together:

#include <stdio.h>

char* my_strcpy (char* dest, char* src)
{
	while (*src != '\0') {
		*dest++ = *src++;
	}
	*dest = *src;
}

0x05 optimize - modify while

2. You can even put these codes in while:

(using the final judgment of while, * dest++ = *src + + just copies the slash 0)

char* my_strcpy (char* dest, char* src)
{
	while (*dest++ = *src++) // The slash 0 is copied and the loop stops
		;
}

0x06 optimization - prevent null pointers from being passed in

❓ If NULL pointer is passed in, a BUG will be generated

πŸ’‘ Solution: use assertions

Assertion is a common defensive programming method in language to reduce programming errors;
If the expression value of the calculated expression is false (0), an error message is printed to stderr, and then the program is terminated by calling abort;
Assertions are defined in the form of macros (assert(expression)), not functions;

#include <stdio.h>
#include <assert.h>

char* my_strcpy(char* dest, char* src)
{
	assert(dest != NULL); // Assertion 'dest cannot equal NULL'
	assert(src != NULL);  // Assertion "src cannot equal NULL"

	while (*dest++ = *src++)
		;
}

int main()
{
	char arr1[20] = "xxxxxxxxxx";
	char arr2[] = "hello";
	my_strcpy(arr1, NULL); // πŸ‘ˆ  Experiment: pass in a NULL
	printf("%s\n", arr1);

	return 0;
}

🚩 The operation results are as follows:

0x07 use of const

πŸ’¬ Change the value of num to 20:

int main()
{
	int num = 10;
	int* p = &num;
	*p = 20;
	printf("%d\n", num);

	return 0;
}

🚩  20

πŸ’¬ Put const before int num:

const modifies a variable, which is called a constant variable and cannot be modified, but it is still a variable in essence;

But! But what!!

int main()
{
    const int num = 10;
    int* p = &num;
    *p = 20;

    printf("%d\n", num);

    return 0;
}

🚩 The operation results are as follows

❗ We hope num will not be modified, but it has been changed. Is there no trouble?!

Num actually gave his address to p, and then * p = 20. Modify the value of num through p, not martial virtue!

πŸ’‘ Solution: just add a const before int* p, and * p is useless

int main()
{
    const int num = 10;

    const int* p = &num; 
// If it is placed on the left of * and decorated with * p, it indicates that the content pointed to by the pointer cannot be changed by the pointer
    *p = 20; // ❌  Cannot be modified

    printf("%d\n", num);

    return 0;
}

🚩 The operation results are as follows:

πŸ”‘ Explanation: when const modifies a pointer variable, if const is placed to the left of * and it modifies * p, it means that the content pointed to by the pointer cannot be changed by the pointer;

❓ If we add another variable n = 100, we don't & num, we & n, can we?

int main()
{
    const int num = 10;
    int n = 100;

    const int* p = &num;
    *p = 20 // ❌  Cannot modify
    p = &n; // βœ…  But the pointer variable itself can be modified

    printf("%d\n", num);

    return 0;
}

πŸ”‘ Yes, although p cannot change num, p can change the direction and modify the value of the p variable

❓ What about putting const on the * right?

int main()
{
    const int num = 10;

    int n = 100;
    int* const  p = &num;
// If it is placed to the right of *, it modifies the pointer variable p, indicating that the pointer variable cannot be changed
// But what the pointer points to can be changed
    
    p = 20; // βœ…  Can be modified
    p = &n; // ❌  Cannot modify

    printf("%d\n", num);

    return 0;
}

πŸ”‘ At this point, the content pointed to by the pointer can be modified, but the pointer variable

❓ If const is placed on both sides:

int main()
{
    const int num = 10;
    const int* const p = &num;
    int n = 100;

    *p = 20; // ❌  Cannot modify
    p = &n;  // ❌  Cannot modify
    
    printf("%d\n", num);

    return 0;
}

0x08 optimization - improve code robustness (add const)

πŸ’‘ In order to prevent the two variables from being written in reverse order, we can use const constant to "set rules" for ourselves. In this way, when we write in reverse, because it is a constant, it can not be dereferenced and modified, so as to report an error, and it is easy to find the problem!

char* my_strcpy (char* dest, const char* src)
{
	assert(dest != NULL);
	assert(src != NULL);

	// while(*src++ = *dest)  πŸ‘ˆ  To prevent reverse writing, add a const
	while (*dest++ = *src++)
		;
}

It can virtually prevent you from writing while(*src++ = *dest). Even if you write it wrong, the compiler will report an error (syntax error);

πŸ“Œ Note: add const logically. Don't add const casually (for example, add const before dest);

0x09 final optimization - make it support chained access

πŸ’¬ The implementation returns to the starting position of the target space

#include <stdio.h>
#include <assert.h>

char* my_strcpy (char* dest,const char* src)
{
	char* ret = dest; // Record dest at the beginning
	assert(dest != NULL);
	assert(src != NULL);

	while (*dest++ = *src++)
		;

	return ret; // Finally, dest is returned
}

int main()
{
	char arr1[20] = "xxxxxxxxxx";
	char arr2[] = "hello";
	
	printf("%s\n", my_strcpy(arr1, arr2)); // Chain access

	return 0;
}

0x0A library function writing method

/***
*char *strcpy(dst, src) - copy one string over another
*
*Purpose:
*       Copies the string src into the spot specified by
*       dest; assumes enough room.
*
*Entry:
*       char * dst - string over which "src" is to be copied
*       const char * src - string to be copied over "dst"
*
*Exit:
*       The address of "dst"
*
*Exceptions:
*******************************************************************************/

char * strcpy(char * dst, const char * src)
{
        char * cp = dst;
        assert(dst && src);

        while( *cp++ = *src++ )
               ;     /* Copy src over dst */
        return( dst );
}

6, Simulate the implementation of strlen function

0x00 counter implementation

#include <stdio.h>
#include <assert.h>

int my_strlen(const char* str)
{
	assert(str);
	int count = 0;
	while (*str) {
		count++;
		str++;
	}
	return count;
}

int main()
{
	char arr[] = "abcdef";
        int len = my_strlen(arr);
	printf("%d\n", len);

	return 0;
}

0x01 pointer minus pointer implementation

#include <stdio.h>
#include <assert.h>

size_t my_strlen(const char* str)
{
	assert(str);
	const char* eos = str;
	while (*eos++);
	return(eos - str - 1);
}

int main()
{
	char arr[] = "abcdef";
	printf("%d\n", my_strlen(arr));

	return 0;
}

0x02 library function writing method

/***
*strlen.c - contains strlen() routine
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*       strlen returns the length of a null-terminated string,
*       not including the null byte itself.
*
*******************************************************************************/

#include <cruntime.h>
#include <string.h>

#pragma function(strlen)

/***
*strlen - return the length of a null-terminated string
*
*Purpose:
*       Finds the length in bytes of the given string, not including
*       the final null character.
*
*Entry:
*       const char * str - string whose length is to be computed
*
*Exit:
*       length of the string "str", exclusive of the final null byte
*
*Exceptions:
*
*******************************************************************************/

size_t __cdecl strlen (
        const char * str
        )
{
        const char *eos = str;

        while( *eos++ ) ;

        return( eos - str - 1 );
}

size_t: unsigned int

__ cdecl: function calling convention

7, Common programming errors

0x00 compile type error

πŸ“š Directly look at the error message (double click) to solve the problem;

Or it can be done with experience, which is relatively simple;

0x01 linked error

πŸ“š Look at the error message, mainly find the identifier in the error message in the code, and then locate the problem.

Generally, the identifier name does not exist or is misspelled;

0x02 runtime error

πŸ“š The code ran clearly, but the result was wrong;

πŸ”‘ With the help of debugging, gradually locate the problem and solve it by using the practical debugging skills described in this chapter;

0x03 suggestion

πŸ“œ Be a conscientious person, summarize yourself every time you encounter mistakes and accumulate wrong experience!

Chapter 9 - data storage

preface:

This chapter will learn the storage of C language data and introduce the data types in detail. Formally explain the knowledge of original code, inverse code, complement, size end and so on. It will also discuss the storage of floating-point numbers and introduce the provisions of IEEE754.

1, Introduction to data types

0x00 built in type

​

0x01 meaning of type

πŸ“š significance:

β‘  type determines the size of memory space (size determines the scope of use)

β‘‘ type determines the perspective of memory space

2, Basic classification of types

0x00 integer family

​

❓ Why char is an integer:

Because the bottom layer of character type stores ASCII code value, and ASCII code value is also an integer, char type will be attributed to integer family when classifying. (ASCII code: American Standard Code for information interchange )

(signed and unsigned types will be explained in detail later in this article)

0x01 floating point family

​

0x02 construction type

​

πŸ“š Definition: construction type, also known as user-defined type, is a type created by ourselves;

πŸ“Œ Note: they are all integer arrays, but their types are completely different

int arr[10]; πŸ‘‰ The type is: int[10]
int arr2[5]; πŸ‘‰ Type: int[5] 

0x03 pointer type

​

0x04 null type

​

πŸ“š void represents null type (no type), which is usually applied to the return type, parameter and pointer type of a function

πŸ’¬ void defines the function return type and function parameters:

void test(void) // Functions that do not require a return value
{
    printf("hehe\n");
}

int main(void)
{
    test(); // No parameters are declared in the test(100) error function

    return 0;
}

πŸ’¬ void defines a null pointer:

void* p

3, Storage of integers in memory

0x00 import

​

πŸ“š Data is stored in memory in binary form:

β‘  for integers, there are three forms of binary stored in memory: original code, inverse code and complement code

β‘‘ positive integer: the original code, inverse code and complement code are the same

β‘’ negative integer: original code, inverse code and complement code shall be calculated

0x00 original code

πŸ“š Original code: the binary sequence written directly according to the value of the data is the original code

​

❗ Sign bit: the highest bit 1 represents a negative number, and the highest bit 0 represents a positive number

0x01 inverse code

πŸ“š Inverse code: the sign bit of the original code remains unchanged, and the other bits are reversed by bit, that is, the inverse code

​

0x02 complement

πŸ“š Complement: inverse code + 1, you get the complement

​

0x03 why is complement stored in memory

At this point, let's look at the introduction mentioned earlier:

πŸ“š Although there are three binary representations of integers, complements are stored in memory

​

πŸ”Ί Conclusion: integers are stored in memory as complements

❓ Why is a complement stored in memory

In the computer system, integer values are represented and stored by complement. The reason is that the symbol bit and digital field can be processed uniformly by using complement; At the same time, addition and subtraction can also be processed uniformly (CPU only has adder). In addition, the operation process of complement and complement conversion is the same, and no additional hardware circuit is required.

4, Size end

0x00 problem introduction

Let's take a closer look at the memory just now. This time, we'll add another b = 10

​

❓ We can see that the complement is stored for a and b respectively,

But we find that the order seems to be reversed. Why?

Large end and small end storage. When the size of a data stored in the memory space is greater than 1 byte, there will be a storage order problem. There are two kinds of storage order problems, one is the large end and the other is the small end.

​

0x01 big end mode

πŸ“š Function: save the low bit of data at the high address of memory, and store the contents of high byte order in the low address (positive storage)

πŸ”Ί Summary: in the big end mode, the low bit is placed at the high address and the high bit is placed at the low address

0x02 small end mode

πŸ“š Function: save the low order of data in the low address of memory, and store the contents of high byte order in the high address (reverse storage)

πŸ”Ί Summary: small end mode, low order is placed at low address and high order is placed at high address

0x03 causes

❓ Why are there big and small ends:

Why are there big and small end modes? This is because in the computer system, we take bytes as the unit, and each address unit corresponds to a byte, one byte is 8bit. However, in C language, in addition to the 8-bit char, there are also 16 bit short and 32-bit long (depending on the specific compiler). In addition, for processors with more than 8 bits, such as 16 bit or 32-bit processors, since the register width is greater than one byte, there must be a problem of arranging multiple bytes. Therefore, it leads to large end storage mode and small end storage mode.

For example, for a 16bit short x, the address in memory is 0x0010, and the value of X is 0x1122, then 0X11 is the high byte and 0x22 is the low byte. For the big end mode, put 0X11 in the low address, that is, 0x0010, and 0x22 in the high address, that is, 0x0011. Small end mode, just the opposite. Our commonly used X86 structure is the small end mode, while KEIL C51 is the large end mode. Many arm and DSP are in small end mode. Some ARM processors can also be selected by hardware whether it is large end mode or small end mode.

0x04 judge the current system size

πŸ“œ Baidu 2015 System Engineer written test questions:

Please briefly describe the concepts of large end byte order and small end byte order, and design a small program to judge the byte order of the current machine (10 points)

πŸ’‘ Implementation idea:

​

πŸ’¬ Code implementation:

int main()
{
    int a = 1;
    char* pa = (char*)&a; // You need to cast the type here
    
    if(*pa == 1)
        printf("Small end");
    else
        printf("Big end");
        
    return 0;
}

⚑ Optimize - encapsulate into function:

int check_sys() 
{
    int a = 1;
    char* pa = (char*)&a;
    return *pa; // A return of 1 indicates the small end, and a return of 0 indicates the large end
    // return *(char*)&a;
}

int main()
{
    int ret = check_sys();
    
    if(ret == 1)
        printf("Small end\n");
    else
        printf("Big end\n");

    return 0;
}

5, Signed and unsigned numbers

0x00 definition

πŸ“š unsigned, signed

0x01 range of two char s

πŸ“š The range of signed char is: - 128 ~ 127

πŸ“š The range of unsigned char is: 0 ~ 255

πŸ”‘ signed char parsing:

​

0x02 special exercise

πŸ’¬ Exercise 1:

What is the output of the following code?

int main()
{
    char a = -1;
    signed char b = -1;
    unsigned char c = -1;
    printf("a=%d, b=%d, c=%d", a, b, c);

    return 0;
}

🚩 a = -1, b = -1, c = 255

πŸ”‘ Resolution:

​

πŸ“Œ matters needing attention:

β‘  Int is signed int, short is signed short... This is stipulated in the C language standard.

β‘‘ However, char is special! Whether char is a signed char or an unsigned char is not specified in the C language standard. It depends on the compiler. Most compiler chars refer to signed char.

πŸ’¬ Exercise 2:

What is the output of the following program?

int main()
{
    char a = -128;
    printf("%u\n", a);
    
    return 0;
}

🚩 4294967168

πŸ”‘ Resolution:

​

❓ If you do not print in% u format, what is printed in% d format:

​

πŸ’¬ Exercise 3:

Similar to the above question, what is the result after running?

int main()
{
    char a = 128;
    printf("%u\n", a);

    return 0;
}

🚩 4294967168

πŸ”‘ Resolution:

​

❓ Can char a fit 128?

(only a part is put in, and truncation occurs)

​

πŸ’¬ Exercise 4:

What is the result of running the following code?

int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--) {
		printf("%u\n", i);
	}

	return 0;
}

🚩 The operation results are as follows:

​

πŸ”‘ Analysis: because i is an unsigned integer, the judgment condition is i > = 0. Because i cannot be less than 0 under any circumstances, this condition is constant, which leads to an endless loop.

πŸ’¬ Exercise 5:

int main()
{
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++) {
		a[i] = -1 - i;
	}
	printf("%d", strlen(a)); 

	return 0;
}

🚩  255

πŸ”‘ Resolution:

​

πŸ’¬ Exercise 6:

What is the result of running the following code?

unsigned char i = 0;
int main()
{
	for (i = 0; i <= 255; i++) {
		printf("hello world\n");
	}

	return 0;
}

🚩 The operation results are as follows:

​

πŸ”‘ Analysis: i is unsigned char. The value range of unsigned char is 0 ~ 255, and the judgment condition is i < = 255. This condition can never be met, because the maximum value that can be put in unsigned char is 255, so it is an endless loop.

6, Storage of floating point numbers in memory

0x00 common floating point numbers

πŸ“š Floating point number family includes: float, double, long double types

0x01 view defined value range

Integer value range: limit Defined in H

Value range of floating point number: float Defined in H

0x02 example of floating point storage

int main()
{
	int n = 9;

	float* pFloat = (float*)&n;
	printf("n The value of is: %d\n", n);
	printf("*pFloat The value of is %f\n", *pFloat);

	*pFloat = 9.0;
	printf("num The value of is: %d\n", n);
	printf("*pFloat The value of is: %f\n", *pFloat);

	return 0;
}

🚩 The operation results are as follows:

​

❓ num and * pFloat are obviously the same number in memory. Why are the interpretation results of floating-point numbers and positive numbers so different?

πŸ’‘ Resolution:

​

0x03 IEEE754 regulations

πŸ“š IEEE754: According to the international standard IEEE 754, any binary floating-point number V can be expressed in the following form: (- 1)^S * M * 2^E

β‘  (- 1)^s represents the sign bit. When s = 0, v is a positive number; When s = 1, v is negative

β‘‘ M represents significant number, greater than or equal to 1 and less than 2

β‘’ 2^E indicates the index bit

πŸ“œ example:

Floating point number: 5.5 - decimal

Binary: 101.1 → 1.011 * 2 ^ 2 → (- 1) ^ 0 * 1.011 * 2 ^ 2

Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β  s=0Β Β  M=1.011Β  E=2

πŸ”Ί IEEE 754 states:

For 32-bit floating-point numbers, the highest bit is sign bit S, then 8 bits are exponent E, and the remaining 23 bit significant digits M:

​

For 64 bit floating-point numbers, the highest bit is sign bit S, the next 11 bits are exponent E, and the remaining 52 significant digits M:

​

IEEE 754 has some special provisions for the significant number m and index E. As mentioned earlier, 1 ≤ m < 2, that is, M can be written as 1 The form of xxxxxx, where xxxxxx represents the decimal part.

IEEE 754 stipulates that when saving m in the computer, the first digit of this number is always 1 by default, so it can be rounded off and only the following xxxxxx part is saved. For example, when saving 1.01, only 01 is saved. When reading, add the first 1. The purpose of this is to save 1 significant digit. Take the 32-bit floating-point number as an example. There are only 23 bits left for M. after rounding off the 1 of the first bit, it is equivalent to saving 24 significant digits.

As for index E, the situation is more complicated.

Firstly, e is an unsigned int, which means that if e is 8 bits, its value range is 0 ~ 255; If e is 11 bits, its value range is 0 ~ 2047. However, we know that E in the scientific counting method can have negative numbers, so IEEE 754 stipulates that the real value of e must be added with an intermediate number when stored in memory. For 8-bit e, the intermediate number is 127; For an 11 bit e, the median is 1023. For example, e of 2 ^ 10 is 10, so when saving as a 32-bit floating-point number, it must be saved as 10 + 127 = 137, that is, 10001001.

​

Then, the index E can be extracted from the memory and can be divided into three cases:

E is not all 0 or all 1

At this time, the floating-point number is represented by the following rule, that is, subtract 127 (or 1023) from the calculated value of index E to obtain the real value, and then add the first 1 before the significant number M. For example, the binary form of 0.5 (1 / 2) is 0.1. Since it is specified that the positive part must be 1, that is, if the decimal point is shifted to the right by 1 digit, it will be 1.0 * 2 ^ (- 1). Its order code is - 1 + 127 = 126, which is expressed as 01111110, while the mantissa of 1.0 is 0 after removing the integer part and complementing 0 to 23 digits, then its binary representation is

0 01111110 00000000000000000000000

E is all 0

At this time, the exponent E of the floating point number is equal to 1-127 (or 1-1023), which is the real value, and the significant number M is no longer added with the first 1, but restored to 0 The decimal of XXXXXX. This is done to represent ± 0 and small numbers close to 0.

E is all 1

At this time, if the significant digits M are all 0, it means ± infinity (the positive and negative depend on the sign bit s);

0x04 explain the previous example

​

Chapter 10 - Advanced pointer (Part 1)

preface:

Pointer to the subject we are in the primary stage of [vitamin C language] Chapter 6 - pointer We have already touched this chapter. We know the concept of pointer:

1. A pointer is a variable used to store an address, which uniquely identifies a piece of memory space.

2. The size of the pointer is fixed 4 / 8 bytes (32-bit platform / 64 bit platform).

3. The pointer is typed. The pointer type determines the + - integer step size of the pointer and the permission during pointer dereference operation.

4. Pointer operation.

In this chapter, we will continue to explore the advanced topic of pointers.

πŸšͺ [vitamin C language] Chapter 10 - Advanced pointer (Part 2)

1, Character pointer

Definition of 0x00 character pointer

πŸ“š Definition: character pointer, constant string, only one copy is stored (to save memory)

0x01 character pointer usage

πŸ’¬ Usage:

int main()
{
    char ch = 'w';
    char *pc = &ch;
    *pc = 'w';
    
    return 0;
}

πŸ’¬ About pointing to Strings:

❓ Did you put a string in pstr?

int main()
{
    char* pstr = "hello world";
    printf("%s\n", pstr);
    
    return 0;
}

🚩  hello world

πŸ”‘ Analysis: the above code char * pstr = "Hello world" is very easy to make people think that hello world is put in the character pointer pstr, but essentially the address of the first character of the string Hello world is put in pstr;

0x02 character pointer exercise

πŸ’¬ What is the output of the following code?

int main()
{
    char str1[] = "abcdef";
    char str2[] = "abcdef";
    const char* str3 = "abcdef";
    const char* str4 = "abcdef";

    if (str1 == str2)
        printf("str1 == str2\n");
    else
        printf("str1 != str2\n");

    if (str3 == str4)
        printf("str3 == str4\n");
    else
        printf("str3 != str4\n");

    return 0;
}

🚩 The operation results are as follows:

πŸ”‘ Resolution:

β‘  There are two spaces in the memory, one for arr1 and one for arr2. When the two starting addresses are in different spaces, the two values are naturally different, so arr1 and ar2 are different.

β‘‘ Because abcdef is a constant string and cannot be modified, only one copy is stored in memory to save space, called abcdef. At this time, as like as two peas, p1 or p2 point to the beginning of the same space, that is, the address of the first character, and the p1 and p2 values are exactly the same, so arr3 and arr4 are the same.

0x03 Segmentfault problem

πŸ’¬ Store the address of the first character a of a constant string in the pointer variable pstr:

2, Pointer array

0x00 definition of pointer array

πŸ“š Pointer array is an array. Array: pointer (address) is stored in the array

[] has high priority. First, it is combined with P to form an array, and then int * indicates that it is an integer pointer array, which has n array elements of pointer type. When p+1 is executed here, P points to the next array element.

0x01 usage of pointer array

πŸ’¬ There are few scenes written in this way, which is only for understanding:

int main()
{
    int a = 10;
    int b = 20;
    int c = 30;
    int* parr[4] = {&a, &b, &c};

    int i = 0;
    for(i=0; i<4; i++) {
        printf("%d\n", *(parr[i]) );
    }
 
    return 0;
}

🚩  10  20  30

πŸ’¬ Usage of pointer array:

#include <stdio.h>

int main()
{
    int arr1[] = {1, 2, 3, 4, 5};
    int arr2[] = {2, 3, 4, 5, 6};
    int arr3[] = {3, 4, 5, 6, 7};

    int* p[] = { arr1, arr2, arr3 }; // First element address

    int i = 0;
    for(i=0; i<3; i++) {
        int j = 0;
        for(j=0; j<5; j++) {
            printf("%d ", *(p[i] + j)); // J - > first element + 0, first element + 1, + 2
            // == p[i][j] 
        }
        printf("\n");
    }
    
    return 0;
}

🚩 The operation results are as follows:

πŸ”‘ Resolution:

3, Array pointer

0x00 definition of array pointer

πŸ“š Array pointer is a pointer to an array. Array pointer, also known as row pointer, is used to store the address of the array

Integer pointer - is a pointer to an integer

Character pointer - is a pointer to a character

Array pointer - is a pointer to an array

int main()
{
    int a = 10;
    int* pa = &a;
    char ch = 'w';
    char* pc = &ch;

    int arr[10] = {1,2,3,4,5};
    int (*parr)[10] = &arr; // The address of the array is taken out
    // parr is an array pointer

    return 0;
}

πŸ’¬ Try to write out the array pointer of double* d [5]:

double* d[5];
double* (*pd)[5] = &d;

0x01 difference between array name and & array name

πŸ’¬ Observe the following codes:

int main()
{
    int arr[10] = {0};

    printf("%p\n", arr);
    printf("%p\n", &arr);

    return 0;
}

🚩 As like as two peas, we found their addresses were identical.

πŸ”‘ Resolution:

πŸ’¬ verification:

int main()
{
    int arr[10] = { 0 };

    int* p1 = arr;
    int(*p2)[10] = &arr;

    printf("%p\n", p1);
    printf("%p\n", p1 + 1);

    printf("%p\n", p2);
    printf("%p\n", p2 + 1);

    return 0;
}

🚩 The operation results are as follows:

πŸ”Ί The array name is the address of the first element of the array, but there are 2 exceptions:

β‘  sizeof (array name) - the array name represents the entire array. It calculates the size of the entire array in bytes.

β‘‘ & array name - the array name represents the entire array, and the address of the entire array is taken out.

0x02 array pointer usage

πŸ’¬ Array pointers are generally not used in one-dimensional arrays:

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int (*pa)[10] = &arr; // The pointer points to an array of 10 elements, each of which is of type int
    
    int i = 0;
    for(i=0; i<10; i++) {
        printf("%d ", *((*pa) + i));
    }

    return 0;
}

❓ Is the code above a little awkward? Array pointers are awkward to use here and are not a good way to write them.

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int *p = arr;
    int i = 0;

    for(i=0; i<10; i++) {
        printf("%d ", *(p + i));
    }

    return 0;
}

πŸ’¬ Use array pointer when two-dimensional array is above:

void print1 (
    int arr[3][5], 
    int row, 
    int col
    )
{
    int i = 0;
    int j = 0;
    for(i=0; i<row; i++) {
        for(j=0; j<col; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

void print2 (
    int(*p)[5], // πŸ‘ˆ  Array pointer to a row of a two-dimensional array
    int row, 
    int col
    )
{
    int i = 0;
    int j = 0;
    for(i=0; i<row; i++) {
        for(j=0; j<col; j++) {
            printf("%d ", *(*(p + i) + j));
            // printf("%d ", (*(p + i))[j]);
            // printf("%d ", *(p[i] + j));
            // printf("%d ", p[i][j]);
        }
        printf("\n");
    }
}

int main()
{
    int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7}};
    // print1(arr, 3, 5);
    print2(arr, 3, 5); // arr array name, indicating the address of the first element of the element

    return 0;
}

🚩 The operation results are as follows:

0x03 how to write array access elements

πŸ’‘ The following expressions are all equivalent:

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int i = 0;
    int* p = arr;
    for(i=0; i<10; i++)
    {
        //The following expressions are all equivalent
        printf("%d ", p[i]);
        printf("%d ", *(p+i));
        printf("%d ", *(arr+i));
        printf("%d ", arr[i]); //arr[i] == *(arr+i) == *(p+i) == p[i]
    }
}

0x04 exercise

πŸ’¬ Analyze the meaning of these codes:

int arr[5];                                                   
int* parr1[10];                                                             
int (*parr2)[10];                                                        
int (*parr3[10])[5];

πŸ’‘ Resolution:

4, Array and pointer parameters

When writing code, it is inevitable to pass arrays or pointers to functions. How to design function parameters?

0x00 one dimensional array parameter transfer

πŸ’¬ Judge whether the design of the following formal parameters is reasonable:

void test(int arr[]) //Is it reasonable?
{}
void test(int arr[10]) // Is it reasonable?
{}
void test(int *arr) // Is it reasonable?
{}
void test(int *arr[]) // Is it reasonable?
{}
void test2(int *arr[20]) // Is it reasonable?
{}
void test2(int **arr) // Is it reasonable?
{}

int main()
{
    int arr[10] = {0};
    int* arr2[20] = {0};
    test(arr);
    test2(arr2);
}

🚩 Answer: all the above are reasonable

πŸ”‘ Resolution:

0x01 two dimensional array parameter transfer

πŸ’¬ Judge whether the following two-dimensional array parameters are reasonable:

void test(int arr[3][5]) // Is it reasonable?
{}
void test(int arr[][5]) // Is it reasonable?
{}
void test(int arr[3][]) // Is it reasonable?
{}
void test(int arr[][]) // Is it reasonable?
{}

int main()
{
    int arr[3][5] = {0};
    test(arr); // Two dimensional array parameter transfer
    
    return 0;
} 

🚩 Answer: the first two are reasonable and the last two are unreasonable

πŸ”‘ Resolution:

πŸ”Ί Summary: for the design of two-dimensional array parameters and function parameters, only the first [] number can be omitted (rows can be omitted, but columns cannot be omitted)

Because for a two-dimensional array, you can't know how many rows there are, but you must determine how many elements there are in a row!

πŸ’¬ Judge whether the following two-dimensional array parameters are reasonable:

void test(int* arr) // Is it reasonable?
{}
void test(int* arr[5]) // Is it reasonable?
{}
void test(int(*arr)[5]) // Is it reasonable?
{}
void test(int** arr) // Is it reasonable?
{}

int main()
{
    int arr[3][5] = { 0 };
    test(arr);

    return 0;
}

🚩 Answer: only the third one is reasonable, and the others are unreasonable

πŸ”‘ Resolution:

0x02 first level pointer transfer parameter

πŸ’¬ Example of primary pointer parameter transfer:

void print(int* ptr, int sz) // The first level pointer transmits parameters and receives them with the first level pointer
{
    int i = 0;
    for(i=0; i<sz; i++) {
        printf("%d ", *(ptr + i));
    }
}

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int *p = arr;
    int sz = sizeof(arr) / sizeof(arr[0]);
    // p is the first level pointer, which is passed to the function
    print(p, sz);

    return 0;
}

🚩  1 2 3 4 5 6 7 8 9 10

❓ Think: what parameters can be received when the function parameter is a first-order pointer?

πŸ’¬ The primary pointer transmits parameters and the primary pointer receives:

void test1(int* p)
{}
void test2(char* p)
{}

int main()
{
    int a = 10;
    int* pa = &a;
    test1(&a);  // βœ…
    test1(pa);  // βœ…
    
    char ch = 'w';
    char* pc = &ch;
    test2(&ch); // βœ…
    test2(pc);  // βœ…

    return 0;
}

πŸ“Œ Need to master:

β‘  how do we design parameters when designing functions

β‘‘ how can I use other people's functions when the parameters of other people's functions have been designed

0x03 secondary pointer transfer parameter

πŸ’¬ Example of secondary pointer parameter transfer:

void test(int** ptr)
{
    printf("num = %d\n", **ptr);
}

int main()
{
    int n = 10;
    int* p = &n;
    int** pp = &p;
    
    // Both are secondary pointers
    test(pp);
    test(&p); // The address of the p pointer is still a secondary pointer

    return 0;
}

🚩  num = 10   num = 10

❓ Think: what parameters can a function receive when its parameters are secondary pointers?

πŸ’¬ When the function parameter is a secondary pointer:

void test(int **p) // If the parameter is a secondary pointer
{
    ;
}

int main()
{
    int *ptr;
    int** pp = &ptr;
    test(&ptr); // Pass the address of the first level pointer variable βœ…
    test(pp); // Pass secondary pointer variable βœ…
    
    //Pointer arrays can also be used
    int* arr[10]; 
    test(arr); // Pass the array to store the first level pointer, because arr is the address of the first element and the address of int * βœ…

    return 0;
}

Chapter 10 - Advanced pointer (Part 2)

preface:

  πŸšͺ Portal: [vitamin C language] Chapter 10 - Advanced pointer (Part 1)

This chapter will continue to explain the advanced part of pointer and summarize the pointer knowledge. It also introduces the usage of qsort function and simulates the implementation of qsort function. After learning this chapter, the topic of C language pointer is over. Corresponding exercises and explanations are provided. It is strongly recommended to do it. In addition, the pointer of C language can not be completely covered by this topic. There are more pointer usages that need to be learned through books and actual combat, and accumulated continuously in order to learn the most representative thing of C language - pointer.

Knowledge sorting:

int main()
{
    int a = 10;
    int* pa = &a;

    char ch = 'w';
    char* pc = &ch;

    int arr[10] = {0};
    int (*parr)[10] = &arr; // Fetch the address of the array

    return 0;
}

I. function pointer

Introduction to 0x00 function pointer

πŸ“š Pointer array: an array of pointers. Array pointer: pointer to an array,

Function pointer: a pointer to a function that stores the address of the function.

0x01 fetch function pointer address

πŸ“š Functions also have addresses. The function address can be obtained by & function name or function name.

πŸ“Œ matters needing attention:

Function name = = as like as two peas. (the two methods are only formal differences, meaning the same.

β‘‘ array name= & array name

πŸ’¬ Fetch function address:

int Add(int x, int y)
{
    return x + y;
}

int main()
{
    // Function pointer - pointer to the address of the function
    // &Function name - gets the address of the function
    printf("%p\n", &Add);
    printf("%p\n", Add);

    return 0;
}

🚩 The operation results are as follows:


Β 

0x02 definition of function pointer

πŸ“š Function return type (* pointer variable name) (function parameter type...) = & function name;

πŸ’¬ To create a function pointer variable:

int Add(int x, int y)
{
    return x + y;
}

int main()
{
    int (*pf)(int, int) = &Add;
//        πŸ‘†  pf is a function pointer variable

    return 0;
}

πŸ”‘ Resolution:

  πŸ’¬ Function pointer definition exercise:

Please complete the definition of the function pointer below.

void test(char* str)
{
    ;
}

int main()
{
    pt = &test;

    return 0;
}

🚩 Reference answer:

void (*pt)(char*) = &test

0x03 call of function pointer

πŸ“š (* pointer variable name) (passed parameters...);

πŸ’¬ Call of function pointer:

int Add(int x, int y)
{
    return x + y;
}

int main()
{
    int (*pf)(int, int) = &Add;
    
    int ret = (*pf)(3, 5);
//              πŸ‘†  Dereference pf, find the function it points to, and then pass parameters to it
    printf("%d\n", ret);

    return 0;
}

❓ Can you write (* pf) (3,5) as * pf (3,5)?

πŸ’‘ A: No, this will cause the asterisk to dereference the return value of the function. Is this reasonable? This is unreasonable! So if you want to add an asterisk, be sure to enclose it in parentheses. Of course, you can choose not to add it, because you can also:

int Add(int x, int y)
{
    return x + y;
}

int main()
{
    int (*pf)(int, int) = &Add;

    // int ret = Add(3, 5);
    int ret = pf(3, 5);

    printf("%d\n", ret);

    return 0;
}

πŸ”‘ Analysis: the result is the same. It shows that (* pf) is just a decoration and has no actual operational significance, so pf(3, 5) can also be used.

πŸ”Ί Summary:

Add(3, 5);   // βœ…
(*pf)(3, 5); // βœ…
pf(3, 5);    // βœ…

*pf(3, 5);   // ❌

πŸ’¬ Character pointer char * function pointer:

void Print(char*str)
{
    printf("%s\n", str);
}

int main()
{
    void (*p)(char*) = Print; // p combines with * first, which is the pointer
    (*p)("hello wrold"); // Call this function
    
    return 0;
}

🚩  hello world


0x04 analyze the following codes

πŸ“š These two codes are mentioned in C traps and defects.

πŸ’¬ Code 1:

(*(void (*)())0)();

πŸ”‘ Resolution: this code is actually used to call the function at address 0. The function has no parameters and the return type is void

πŸ’¬ Code 2:

void (*signal(int, void(*)(int)))(int);

πŸ”‘ Resolution:

⚑ Simplified code:

int main()
{
    void (* signal(int, void(*)(int)) )(int);

    // typedef void(*)(int) pfunc_t;  ❌  You can't write that. The compiler can't read it
    typedef void(*pfun_t)(int); // Rename the function pointer type of void(*)(int) to pfun_t

    pfun_t signal(int, pfun_t); // It is completely equivalent to the above writing

    return 0;
}

2, Function pointer array

Array is a storage space for storing data of the same type. We have learned pointer array, such as:

int *arr[10]; // Each element of the function is an array of * int pointers

Introduction to 0x00 function pointer array

πŸ“š If you want to store the address of the function in an array, this array is called the function pointer array

0x01 definition of function pointer array

πŸ’¬ Definition of function pointer array:

int Add(int x, int y) {
    return x + y;
}

int Sub(int x, int y) {
    return x - y;
}

int main()
{
    int (*pf)(int, int) = Add;
    int (*pf2)(int, int) = Sub;

    int (*pfArr[2])(int, int) = {Add, Sub};
//         πŸ‘†  pfArr is an array of function pointers

    return 0;
}

Application of 0x02 function pointer array

πŸ“š Implement a calculator that can perform simple addition, subtraction, multiplication and division operations.

πŸ’¬ Code 1:

#include <stdio.h>

void menu()
{
    printf("*****************************\n");
    printf("**    1. add     2. sub    **\n");
    printf("**    3. mul     4. div    **\n");
    printf("**         0. exit         **\n");
    printf("*****************************\n");
}

int Add(int x, int y) {
    return x + y;
}
int Sub(int x, int y) {
    return x - y;
}
int Mul(int x, int y) {
    return x * y;
}
int Div(int x, int y) {
    return x / y;
}

int main()
{
    // Calculator - calculates the addition, subtraction, multiplication, and division of integer variables
    int input = 0;
    do {
        menu();
        int x = 0;
        int y = 0;
        int ret = 0;
        printf("Please select:> ");
        scanf("%d", &input);
        printf("Please enter 2 operands:> ");
        scanf("%d %d", &x, &y);
        switch(input) {
            case 1:
                ret = Add(x, y);
                break;
            case 2:
                ret = Div(x, y);
                break;
            case 3:
                ret = Mul(x, y);
                break;
            case 4:
                ret = Div(x, y);
                break;
            case 0:
                printf("Exit program\n");
                break;
            default:
                printf("Reselect\n");
                break;
        }
        printf("ret = %d\n", ret);
    } while(input);
    
    return 0;
}

🚩 Let's test the code:

❗ At this point, we found the problem. Even if you choose 0 or choose wrong, the program still requires you to enter 2 operands. Is that reasonable? This is unreasonable! So we need to modify the code:

β‘  allow the user to enter two operands only after calculation

β‘‘ print the results after calculation

πŸ’¬ Modification:

#include <stdio.h>

void menu()
{
    printf("*****************************\n");
    printf("**    1. add     2. sub    **\n");
    printf("**    3. mul     4. div    **\n");
    printf("**         0. exit         **\n");
    printf("*****************************\n");
}

int Add(int x, int y) {
    return x + y;
}
int Sub(int x, int y) {
    return x - y;
}
int Mul(int x, int y) {
    return x * y;
}
int Div(int x, int y) {
    return x / y;
}

int main()
{
    int input = 0;
    do {
        menu();
        int x = 0;
        int y = 0;
        int ret = 0;
        printf("Please select:> ");
        scanf("%d", &input);
        switch(input) {
            case 1:
                printf("Please enter 2 operands:> ");
                scanf("%d %d", &x, &y);
                ret = Add(x, y);
                printf("ret = %d\n", ret);
                break;
            case 2:
                printf("Please enter 2 operands:> ");
                scanf("%d %d", &x, &y);
                ret = Div(x, y);
                printf("ret = %d\n", ret);
                break;
            case 3:
                printf("Please enter 2 operands:> ");
                scanf("%d %d", &x, &y);
                ret = Mul(x, y);
                printf("ret = %d\n", ret);
                break;
            case 4:
                printf("Please enter 2 operands:> ");
                scanf("%d %d", &x, &y);
                ret = Div(x, y);
                printf("ret = %d\n", ret);
                break;
            case 0:
                printf("Exit program\n");
                break;
            default:
                printf("Reselect\n");
                break;
        }
    } while(input);
    
    return 0;
}

🚩 Let's test the code:

❗ After the modification, the code is much more reasonable. Although the functions are realized, there are places that can be optimized:

β‘  the current code is redundant and there are a large number of repeated statements.

β‘‘ when adding calculator functions (such as a & B), a section of case should be written for each function added. Can it be added more conveniently?

⚑ Use function pointer array to improve code:

#include <stdio.h>

void menu()
{
    printf("*****************************\n");
    printf("**    1. add     2. sub    **\n");
    printf("**    3. mul     4. div    **\n");
    printf("**         0. exit         **\n");
    printf("*****************************\n");
}

int Add(int x, int y) {
    return x + y;
}
int Sub(int x, int y) {
    return x - y;
}
int Mul(int x, int y) {
    return x * y;
}
int Div(int x, int y) {
    return x / y;
}

int main()
{
    int input = 0;
    do {
        menu();

        // pfArr is an array of function pointers
        int (*pfArr[5])(int, int) = {NULL, Add, Sub, Mul, Div};
        int x = 0;
        int y = 0;
        int ret = 0;
        printf("Please select:> ");
        scanf("%d", &input);

        if(input >= 1 && input <= 4) {
            printf("Please enter 2 operands:> ");
            scanf("%d %d", &x, &y);
            ret = (pfArr[input])(x, y);
            printf("ret = %d\n", ret);  
        }
        else if(input == 0) {
            printf("Exit program\n");
            break;
        } else {
            printf("Selection error\n");
        }

    } while(input);
    
    return 0;
}

🚩 Let's test the code:

πŸ”‘ Analysis: This is the application of function pointer array. Receive a subscript, find an element in the array through the subscript. If this element happens to be the address of a function, then call that function. It acts as a "springboard", so we usually call this array a transfer table (the transfer table is mentioned in the book C and pointers).

3, Pointer to array of function pointers

0x00 pointer definition of function pointer array

πŸ“š Definition: the pointer to the array of function pointers is a pointer, the pointer points to an array, and the elements of the array are function pointers.

0x01 example of function pointer array

πŸ’¬ ppfArr is an array of function pointers:

int Add(int x, int y) {
    return x + y;
}

int main()
{
    int arr[10] = {0};
    int (*p)[10] = &arr; // Fetch the address of the array

    int (*pfArr[4])(int, int); // pfArr is an array - an array of function pointers
    // ppfArr is a pointer to [function pointer array]
    int (* (*ppfArr)[4])(int, int) = &pfArr;
    // ppfArr is an array pointer. The array pointed to by the pointer has four elements
    // The type of each element of the array pointed to is a function pointer int(*)(int, int)

    return 0;
}

0x02 summary of pointers

πŸ”Ί Pointer:

4, Callback function (call back)

0x00 concept of callback function

A callback function is a function called through a function pointer. If you pass the pointer (address) of a function as a parameter to another function, when the pointer is used to call the function it points to, we call it a callback function. The callback function is not called directly by the implementer of the function, but by another party when a specific time or condition occurs, which is used to respond to the event or condition.

0x01 example of callback function

πŸ’¬ Take the switch version calculator as an example:

#include <stdio.h>

void menu()
{
    printf("*****************************\n");
    printf("**    1. add     2. sub    **\n");
    printf("**    3. mul     4. div    **\n");
    printf("**         0. exit         **\n");
    printf("*****************************\n");
}

int Add(int x, int y) {
    return x + y;
}
int Sub(int x, int y) {
    return x - y;
}
int Mul(int x, int y) {
    return x * y;
}
int Div(int x, int y) {
    return x / y;
}

void Calc(int (*pf)(int, int))
{
    int x = 0;
    int y = 0;
    printf("Please enter 2 operands:>");
    scanf("%d %d", &x, &y);
    printf("%d\n", pf(x, y));
}

int main()
{
    int input = 0;

    do {    
        menu();
        printf("Please select:>");
        scanf("%d", &input);

        switch(input) {
            case 1:
                Calc(Add);
                break;
            case 2:
                Calc(Sub);
                break;
            case 3:
                Calc(Mul);
                break;
            case 4:
                Calc(Div);
                break;
            case 0:
                printf("sign out\n");
                break;
            default:
                printf("Selection error\n");
                break;
        }
    } while(input);

    return 0;
}

πŸ”‘ Analysis: the above code achieves the purpose of doing whatever calculation you want, which is what function pointers can do. A Calc function can do many functions. Passing different parameters to it can do different things.

0x02 no pointer type void*

void*

0x03 qsort function

πŸ“š Description: qsort function is the sorting function of the C language compiler function library (the header file stdlib.h needs to be introduced)

πŸ’¬ Review bubble sorting:

[vitamin C language] Chapter 4 - array ( 3 - 0x01 )

#include <stdio.h>

void bubble_sort (int arr[], int sz)
{
    int i = 0;
    // Number of confirmed trips
    for (i = 0; i < sz-1; i++) {
        // Bubble sort
        int j = 0;
        for (j = 0; j < sz-1-i; j++) {
            if(arr[j] > arr[j + 1]) {
                // exchange
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}

void print_arr(int arr[], int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main()
{
    int arr[10] = {9,8,7,6,5,4,3,2,1,0};
    int sz = sizeof(arr) / sizeof(arr[0]);

    print_arr(arr, sz);
    bubble_sort(arr, sz);
    print_arr(arr, sz);

    return 0;
}

❓ Problem point: the bubble sorting function we implemented can only sort integer order. If we want to sort strings or a structure, do we want to re implement this function separately? The qsort function can help us arrange any data type we want.

πŸ“š Four parameters of qsort function:

πŸ’¬ qsort integer data sorting (ascending):

#include <stdio.h>
#include <stdlib.h>

/*
void qsort (
    void* base,
    size_t num,
    size_t size,
    int (*cmp_int)(const void* e1, const void* e2)
    );
*/

int cmp_int(const void* e1, const void* e2)
{
    // Ascending: e1 - e2
    return *(int*)e1 - *(int*)e2;
}


void print_arr(int arr[], int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

void int_sort()
{
    int arr[] = {9,8,7,6,5,4,3,2,1,0};
    int sz = sizeof(arr) / sizeof(arr[0]);
    // Sorting (fill in four parameters respectively)
    qsort(arr, sz, sizeof(arr[0]), cmp_int);

    // Print
    print_arr(arr, sz);
}

int main()
{
    int_sort();

    return 0;
}

🚩  0 1 2 3 4 5 6 7 8 9

❓ What if I want to test a structure data?

πŸ’¬ Then we write the cmp function of the structure (in ascending order):

(requirement: the structure content is "name + age", and qsort is used to sort by age and by name)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Stu
{
    char name[20];
    int age;
};

/*
void qsort (
    void* base,
    size_t num,
    size_t size,
    int (*cmp_int)(const void* e1, const void* e2)
    );
*/

int cmp_struct_age(const void* e1, const void* e2)
{
    return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}

int cmp_struct_name(const void* e1, const void* e2)
{
    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

void struct_sort()
{
    // Sorting structure data using qsort function
    struct Stu s[3] = { 
        {"Ashe", 39},
        {"Hanzo", 38},
        {"Ana", 60}
    };
    int sz = sizeof(s) / sizeof(s[0]);
    // Sort by age
    qsort(s, sz, sizeof(s[0]), cmp_struct_age);
    // Sort by name
    qsort(s, sz, sizeof(s[0]), cmp_struct_name);
}

int main()
{
    struct_sort();

    return 0;
}

πŸ”‘ Analysis: sorting by age compares the size of age. Sorting by name essentially compares the size of Ascii code.

❓ Now it's ascending. What if I want to achieve descending?

πŸ’‘ Simply replace e1 - e2 with e2 - e1:

int cmp_int(const void* e1, const void* e2)
{
    // Descending order: e2 - e1
    return( *(int*)e2 - *(int*)e1 );
}

int cmp_struct_age(const void* e1, const void* e2)
{
    return( ((struct Stu*)e2)->age - ((struct Stu*)e1)->age );
}

int cmp_struct_name(const void* e1, const void* e2)
{
    return( strcmp(((struct Stu*)e2)->name, ((struct Stu*)e1)->name) );
}

0x04 analog implementation of qsort function

πŸ“š Imitate qsort to realize a general algorithm for bubble sorting

πŸ’¬ Full code (ascending):

#include <stdio.h>
#include <string.h>

struct Stu 
{
    char name[20];
    char age;
};

// Imitate qsort to realize a general algorithm for bubble sorting
void Swap(char*buf1, char*buf2, int width) {
    int i = 0;
    for(i=0; i<width; i++) {
        char tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}
void bubble_sort_q (
    void* base, // First element address
    int sz, // Total number of elements
    int width, // Size of each element
    int (*cmp)(const void*e1, const void*e2) // Function of two elements
    )
{
    // Number of confirmed trips
    int i = 0;
    for(i=0; i<sz-1; i++) {
        // One pass sorting
        int j = 0;
        for(j=0; j<sz-1-i; j++) {
            // Two element comparison arr[i] arr[j+i]
            if(cmp( (char*)base+j*width, (char*)base+(j+1)*width ) > 0) {
                //exchange
                Swap((char*)base+j*width, (char*)base+(j+1)*width, width);     
            }
        }
    }
}

int cmp_struct_age(const void* e1, const void* e2) {
    return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int cmp_struct_name(const void* e1, const void* e2) {
    return strcmp( ((struct Stu*)e1)->name, ((struct Stu*)e2)->name );
}
void struct_sort()
{
    // Sorting structure data using qsort
    struct Stu s[] = {"Ashe", 39, "Hanzo", 38, "Ana", 60};
    int sz = sizeof(s) / sizeof(s[0]);
    // Sort by age
    bubble_sort_q(s, sz, sizeof(s[0]), cmp_struct_age);
    // Sort by name
    bubble_sort_q(s, sz, sizeof(s[0]), cmp_struct_name);
}

void print_arr(int arr[], int sz) 
{
    int i = 0;
    for(i=0; i<sz; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int cmp_int(const void* e1, const void* e2) {
    // Ascending: e1 - e2
    return *(int*)e1 - *(int*)e2;
}
void int_sort()
{
    int arr[] = {9,8,7,6,5,4,3,2,1,0};
    int sz = sizeof(arr) / sizeof(arr[0]);
    // sort
    bubble_sort_q(arr, sz, sizeof(arr[0]), cmp_int);
    // Print
    print_arr(arr, sz);
}


int main()
{
    int_sort();
    // struct_sort();

    return 0;
}

🚩 0 1 2 3 4 5 6 7 8 9

Chapter 11 - string H and stype H introduction to common functions

preface:

πŸ“š Character and string processing is very common in C language, but C language itself has no string type. Strings are usually placed in constant strings or character arrays. String constants apply to string functions that do not modify it.

1, Find string length

0x00 strlen function

πŸ“œ Header file: string h

πŸ” Link: strlen - C++ Reference

πŸ“š Description: the string ends with \ 0. strlen returns the number of characters that appear before \ 0 in the string

πŸ“Œ matters needing attention:

β‘  the string pointed to by the parameter must end with \ 0 +

β‘‘ the return value of the function is size_t. unsigned

πŸ’¬ Code demonstration:

#include <stdio.h>
#include <string.h>

int main()
{
    int len = strlen("abcdef");
    printf("%d\n", len);

    return 0;
}

🚩  6

2, String function with unlimited length

0x00 strcpy function

πŸ“œ Header file: string h

πŸ” Link: strcpy - C++ Reference

πŸ“š Description: String copy, copy the string containing \ 0 to another address space, and the return value type is char*

πŸ“Œ matters needing attention:

β‘  the source string src must end with \ 0

β‘‘ the \ 0 in the source string src will be copied to the destination space dest together

β‘’ the target space must be large enough to store the source string dest (to be demonstrated when strncmp is described below)

β‘£ the target space must be variable, that is, the target space dest cannot be declared by const

πŸ’¬ Code demonstration:

#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[] = "abcdefghi";
    char arr2[] = "123";
    printf("Before copying:%s\n", arr1);

    strcpy(arr1, arr2); // String copy (destination space, source string)

    printf("After copying:%s\n", arr1);

    return 0;
}

🚩 The operation results are as follows:

0x02 strcat function

πŸ“œ Header file: string h

πŸ” Link: http://www.cplusplus.com/reference/cstring/strcat/

πŸ“š Note: copy the string pointed to by src to the string pointed to by DeST (delete \ 0 at the original end of * dest)

πŸ“Œ matters needing attention:

β‘  the source string src must end with \ 0

β‘‘ the \ 0 in the source string src will be copied to the destination space dest together, and the \ 0 at the original end of * dest will be deleted

β‘’ the target space must be large enough to store the source string dest

β‘£ the target space must be variable, that is, the target space dest cannot be declared by const

πŸ’¬ Code demonstration:

#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[30] = "hello";
    char arr2[] = "world";
    
    strcat(arr1, arr2);
    printf("%s\n", arr1);

    return 0;
}

 🚩 hello world

0x03 strcmp function

πŸ“œ Header file: string h

πŸ” Link: http://www.cplusplus.com/reference/cstring/strcmp/

πŸ“š Description: it is used to compare two strings and return integers according to the comparison results. The two strings are compared character by character from left to right, and compared according to the ASCII value. The comparison starts from the first pair of characters. If they are equal, they are compared to the next pair until different characters appear or encounter \ 0. The comparison rules are as follows:

  πŸ’¬ Code demonstration:

#include <stdio.h>
#include <string.h>

int main()
{   
    char *p1 = "abcdef";
    char *p2 = "aqwer";
    int ret = strcmp(p1, p2); // p1 and p2 ratio
    // a==a, compare the next pair, B < Q, so p2 is large
    printf("%d\n", ret);

    return 0;
}

🚩 - 1 (returns a negative number, so P1 < P2)

πŸ”‘ Resolution:

πŸ“Œ Note: the returned results vary depending on the compiler

In VS2013, greater than 1, equal to 0 and less than - 1 are returned. However, in Linux GCC, greater than returns a positive number, equal to 0, and less than returns a negative number. Therefore, we need to pay attention to the writing of the judgment part:

// Not recommended ❌
    if(strcmp(p1, p2) == 1) {
        printf("p1 > p2");
    } else if(strcmp(p1, p2 == 0)) {
        printf("p1 == p2");
    } else if(strcmp(p1, p2) == -1) {
        printf("p1 < p2");
    }
    
// recommend βœ…
    if(strcmp(p1, p2) > 0) {
        printf("p1 > p2");
    } else if(strcmp(p1, p2 == 0)) {
        printf("p1 == p2");
    } else if(strcmp(p1, p2) < -1) {
        printf("p1 < p2");
    }

3, Function string with limited length

0x00 strncpy function

πŸ“œ Header file: string h

πŸ” Link: http://www.cplusplus.com/reference/cstring/strncpy/

πŸ“š Description: copy n characters from the source string to the target space

πŸ“Œ matters needing attention:

β‘  if the length of the source string is less than N, after copying the source string, append 0 to the destination and fill it to n

β‘‘ dest and src should not overlap (they can be replaced by safer memmove when overlapping)

β‘’ the target space must be large enough to store the source string dest

β‘£ the target space must be variable, that is, the target space dest cannot be declared by const

πŸ’¬ Code demonstration:

#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[5] = "abc";
    char arr2[] = "hello world";
    strncpy(arr1, arr2, 4); // Copy 4 from arr2 to arr1
    printf("%s\n", arr1);

    return 0;
}

🚩 hell

❌ Insufficient target space will result in an error:

#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[5] = "abc"; // Array of size 5
    char arr2[] = "hello world";
    strncpy(arr1, arr2, 6); // 6 bytes are required to be copied
    printf("%s\n", arr1);

    return 0;
}

🚩 The operation results are as follows:

0x01 strncat function

πŸ“œ Header file: string h

πŸ” Link: strncat - C++ Reference

πŸ“š Description: append n characters to the target space

πŸ“Œ Note: if the length of the source string is less than n, only the contents before \ 0 will be copied.

πŸ’¬ Code demonstration:

#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[30] = "hello";
    char arr2[] = "world";
    strncat(arr1, arr2, 3); // Add 3 from arr2 to arr1
    printf("%s\n", arr1);

    return 0;
}

🚩 hellowor

0x02 strncmp function

πŸ” Link: strncmp - C++ Reference

πŸ“š Note: it is found that another character is different, or a string ends, or all n characters are compared.

(except for the addition of an n, the others are the same as strcmp)

πŸ’¬ Code demonstration:

#include <stdio.h>
#include <string.h>

int main()
{
    const char* p1 = "abczdef";
    const char* p2 = "abcqwer";
    // int ret = strcmp(p1, p2);
    int ret = strncmp(p1, p2, 1);
    int ret2 = strncmp(p1, p2, 4);
    printf("%d %d\n", ret, ret1);

    return 0;
}

🚩  0  1

4, String lookup

0x00 STR function

πŸ“œ Header file: string h

πŸ” Link: strstr - C++ Reference

πŸ“š Description: returns the address of the first substring in the string. If str2 is a substring of str1, the address where str2 first appears in str1 is returned. Returns NULL if str2 is not a substring of str1.

πŸ’¬ Code demonstration: it is a substring and returns the address of the first occurrence

#include <stdio.h>
#include <string.h>

int main()
{
    char* p1 = "abcdef";
    char* p2 = "def";
    char* ret = strstr(p1, p2); // Determine whether p2 is a substring of p1
    printf("%s\n", ret);

    return 0;
}

🚩 def (p2 is a substring of p1, so def is returned)

πŸ’¬ Code demonstration: not a substring, return NULL

#include <stdio.h>
#include <string.h>

int main()
{
    char* p1 = "abcdef";
    char* p2 = "zzz";
    char* ret = strstr(p1, p2);
    printf("%s\n", ret);

    return 0;
}

🚩  (null)

πŸ’¬ We use if judgment to add description to better present:

#include <stdio.h>
#include <string.h>

int main()
{
    char* p1 = "abcdef";
    char* p2 = "def";
    char* ret = strstr(p1, p2);
    if ( ret == NULL ) {
        printf("Substring does not exist\n");
    } else {
        printf("%s\n", ret);
    }
    return 0;
}

0x01 strtok function

πŸ“œ Header file: string h

πŸ” Link: strtok - C++ Reference

πŸ“š explain:

πŸ“Œ Note: strtok will destroy the original string. After segmentation, the original string will retain the characters before the first delimiter

πŸ’¬ Code demonstration: split ip

#include <stdio.h>
#include <string.h>

int main()
{
    //192.168.0.1
    //192 168 0 1 - strtok

    char ip[] = "192.168.0.1";
    // const char* sep = ".";
    // char* ret = strtok(ip, sep);
    char* ret = strtok(ip, ".");
    printf("%s\n", ret);

    ret = strtok(NULL, ".");
    printf("%s\n", ret);

    ret = strtok(NULL, ".");
    printf("%s\n", ret);

    ret = strtok(NULL, ".");
    printf("%s\n", ret);

    return 0;
}

🚩 The operation results are as follows:

πŸ’¬ Code demonstration: split mailbox

#include <stdio.h>
#include <string.h>

int main()
{
    //1300300100@qq.com
    //1300300100 qq com
    char arr[] = "1300300100@qq.com";
    printf("Original string: %s\n", arr);
    
    const char* sep = "@."; // Create sep
    char arr1[30];
    char* ret = NULL;
    strcpy(arr1, arr); // Copy the data and keep the contents of the arr array

    // Branch print cutting content
    for (ret = strtok(arr, sep); ret != NULL; ret = strtok(NULL, sep)) {
        printf("%s\n", ret);
    }

    printf("Retained original content:%s\n", arr1); // Contents of the saved arr array
    printf("The original string is destroyed after segmentation: %s\n", arr); // After splitting, the original string retains the characters before the first delimiter

    return 0;
}

🚩 The operation results are as follows:

0x02 strerror function

πŸ“œ Header file: string h

πŸ” Link: strerror - C++ Reference

πŸ“š Description: returns the error code and the error information corresponding to the error code

πŸ’¬ Code demonstration:

#include <string.h>
#include <stdio.h>
#include <errno.h>

int main()
{
     // Error code error message
     // 0 -          No error
     // 1 -          Operation not permitted
     // 2 -          No such file or directory
     // ...

    //errno is a global error code variable
    //When an error occurs during the execution of the library function of c language,
    //The corresponding error code will be assigned to errno
    char* str = strerror(errno);
    printf("%s\n", str);

    return 0;
}

🚩  No error

πŸ“š About errno: See the detailed introduction of errno [Baidu Encyclopedia]

Errno is the last error code recorded by the system. The code is an int value in errno Defined in H

πŸ’¬ It can be used during file operation (file operation will be described later)

FILE* pf = fopen("test.txt", "r");
if ( pf == NULL ) {
    printf("%s\n", strerror(errno));
} else {
    printf("*****open file success*****\n")
}

5, Character operation

0x00 character classification

πŸ“œ Header file: stype h

  πŸ’¬ Code demonstration: islower

#include <stdio.h>
#include <ctype.h>

int main()
{
    char ch1 = 'a';
    int ret = islower(ch1); // Judge whether ch1 is lowercase
    printf("%d\n", ret);

    char ch2 = 'B';
    int res = islower(ch2); // Determine whether ch2 is lowercase
    printf("%d\n", res);

    return 0;
}

  🚩 The operation results are as follows:

0x01 character conversion

πŸ“œ You need to import the header file stype h

πŸ’¬ Code demonstration: Tower

int main()
{
    char ch = tolower('Q'); // Capital to lowercase
    putchar(ch);

    return 0;
}

🚩  q

πŸ’¬ Code demonstration: toupper

int main()
{
    char ch = toupper('q'); // Lowercase to uppercase
    putchar(ch);
    
    return 0;
}

🚩  Q

πŸ’¬ Code demonstration: convert all string contents from uppercase to lowercase (using the while loop)

#include <stdio.h>
#include <ctype.h>

int main()
{
    char arr[] = "I Am A Student";
    int i = 0;
    while(arr[i]) {
        if ( isupper(arr[i]) ) {
            arr[i] = tolower(arr[i]);
        } 
        i++;
    }
    printf("%s\n", arr);

    return 0;
}

🚩  i am a student

πŸ’¬ Simulate and implement the swapcase function in Python (string case exchange)

#include <stdio.h>
#include <ctype.h>
#include <assert.h>

void swapcase(char arr[]) 
{
    assert(arr != NULL); // Assertion prevention
    int i = 0;
    while (arr[i] != '\0') {
        if (islower(arr[i])) { //Is it lowercase?
            arr[i] = toupper(arr[i]); //If so, make it uppercase
        } else {  //Not lowercase
            arr[i] = tolower(arr[i]); //Make it lowercase
        }
        i++;
    }
}

int main()
{
    char arr[] = "AaBbCcDdEeFf";
    swapcase(arr);
    printf("%s\n", arr);

    return 0;
}

🚩   aAbBcCdDeEfF

6, Character manipulation function

0x00 memcpy function

πŸ“œ Header file: string h

πŸ” Link: memcpy - C++ Reference

πŸ“š Note: copy n bytes from the starting position of the source memory address src to the destination memory address dest

πŸ“Œ matters needing attention:

β‘  memcpy does not brake. This function will not stop when it encounters \ 0

β‘‘ if there is any overlap between src and dest, the copy result is undefined

πŸ’¬ Code demonstration:

#include <stdio.h>
#include <string.h>

int main()
{
    int arr1[] = {1, 2, 3, 4, 5};
    int arr2[5] = {0};
    memcpy(arr2, arr1, sizeof(arr1));

    // Print the contents of arr2
    int i = 0;
    for(i=0; i<5; i++) {
        printf("%d ", arr2[i]);
    }

    return 0;
}

🚩   1 2 3 4 5

πŸ’¬ Code demonstration: copy structure

#include <stdio.h>
#include <string.h>

struct S
{
    char name[20];
    int age;
};

int main()
{
    struct S arr3[] = { {"Zhang San", 20}, {"Li Si", 30} };
    struct S arr4[3] = { 0 };

    memcpy(arr4, arr3, sizeof(arr3));

    return 0;
}

πŸ”‘ Debug to see if the copy is successful:

0x02 memmove function

πŸ“œ Header file: string h

πŸ” Link: memcpy - C++ Reference

πŸ“š Note: it is used to copy bytes. If the target area and the source area overlap, memmove can ensure that the source string copies the bytes of the overlapping area to the target area before being overwritten, but the source content will be changed after copying.

πŸ“Œ matters needing attention:

β‘  the difference between memcpy and memcpy is that the source memory block and target memory block processed by memmove function can overlap

β‘‘ if the original space and the target space overlap, the memmove function should be used

C language standard requirements:

memcpy is used to handle non overlapping memory copies, while memmove is used to handle overlapping memory copies.

πŸ’¬ Code demonstration:

#include <stdio.h>
#include <string.h>

int main()
{
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    int i = 0;

    memmove(arr+2, arr, 20);

    for(i=0; i<10; i++) {
        printf("%d ", arr[i]);
    }

    return 0;
}

🚩 The operation results are as follows:

0x03 memcmp function

πŸ“œ Header file: string h

πŸ” Link: memcmp - C++ Reference

πŸ“š Note: compare the n bytes at the beginning of ptr1 and ptr2 pointers by byte

πŸ“Œ Note: unlike strcmp, memcmp will not stop comparing when it encounters \ 0

πŸ’¬ Code demonstration:

#include <stdio.h>
#include <string.h>

int main()
{
    float arr1[] = {1.0, 2.0, 3.0, 4.0};
    float arr2[] = {1.0, 3.0};
    int ret = memcmp(arr1, arr2, 8); // Whether arr1 is larger than arr2, compare 8 bytes
    printf("%d\n", ret);
    
    return 0;
}

🚩 - 1 (indicating that arr1 is less than arr2)

0x04 memset function

πŸ“œ Header file: string h

πŸ” Link: http://www.cplusplus.com/reference/cstring/memset/

πŸ“š Note: set all the contents of a block of memory to the specified value, and usually initialize the newly applied memory.

πŸ“Œ Note: memset sets the memory in bytes

πŸ’¬ Code demonstration: set the first 20 bytes of the integer array to 1

#include <stdio.h>
#include <string.h>

int main()
{
    // 40
    int arr[10] = {0};
    memset(arr, 1, 20); // Set all the first 20 bytes to 1

    return 0;
}

Chapter 12 - explanation of custom types in C language (consortium, enumeration, consortium)

preface:

This chapter will explain the custom types of C language. We talked about structures earlier, and this chapter will continue to supplement the knowledge that has not been finished before.

1, Structure (struct)

We have talked about the structure in Chapter 7. In this chapter, we will review briefly and then make some supplements.

0x00 basic knowledge of structure

πŸ“š A structure is a collection of values called member variables. Each member of the structure is a different type of variable.

0x01 declaration of structure

πŸ’¬ Code demonstration: describe a student

struct Stu
{
 char name[20]; // name
 int age; // Age
 char sex[5]; // Gender
 char id[20]; // Student number
}; // Semicolons cannot be lost 

0x02 anonymous structure

πŸ“š Definition: when declaring a structure, it can not be fully declared. When declaring an anonymous structure, the tag of the structure is omitted. Because the type cannot be formed without the tag of the structure, the anonymous structure can only be used once.

πŸ’¬ Code demonstration: anonymous structure

struct
{
    int a;
    char b;
    float c;
    double d;
} s;

struct
{
    int a;
    char b;
    float c;
    double d;
} *ps;

πŸ“Œ matters needing attention:

For the above code, it is illegal to perform the following operations

int main()
{
    ps = &s; // error

    return 0;
}

❌ The compiler will give the following warning:

Although as like as two peas in the compiler, the compiler still considers them to be two completely different types. Because of the difference, *ps cannot store the address of variable s.

0x03 self reference of structure

πŸ“š Introduction: a structure contains a member whose type is the structure itself, and contains structure pointers of the same type (not structure variables of the same type)

πŸ’¬ Code demonstration: structure self reference

struct A
{
    int i;
    char c;
};

struct B
{
    char c;
    struct A sa;
    double d;
};

πŸ“Œ Note 1: a structure cannot contain itself, nor can it contain structural variables of the same type

  ❌ Error presentation:

struct N
{
    int d;
    struct N n; // ❌  Members of the structure's own type cannot exist in the structure
};

πŸ“š To deepen our understanding, let's first introduce some knowledge of data structure:

πŸ“Œ Note 2: do not use anonymous structures when self referencing structures:

❌ Error presentation:

struct  // If the structure name is omitted
{
    int data;
    struct Node* next; // Where does the struct Node * come from?
};

Even if you use typedef to rename it Node, it won't work. To generate a Node, you must have a structure type before renaming the Node, that is, Node* next defines members before typedef can rename this type to Node. Therefore, this method still does not work:

typedef struct
{
    int data;
    Node* next; // Chicken or egg first???
} Node;

πŸ”‘ Solution:

typedef struct Node
{
    int data;
    struct Node* next;
} Node;

0x04 definition and initialization of structure variables

πŸ’¬ Create variables directly while declaring types:

struct S
{
    char c;
    int i;
} s1, s2; // Create variables while declaring types

int main()
{
    struct S s3, s4;

    return 0;
}

πŸ’¬ Assignment (initialization) while creating variables

struct S
{
    char c;
    int i;
} s1, s2;

int main()
{
    struct S s3 = {'x', 20};
//                  c    i

    return 0;
}

πŸ’¬ The structure contains the initialization method of the structure:

struct S
{
    char c;
    int i;
} s1, s2;

struct B
{
    double d;
    struct S s;
    char c;
};

int main()
{
    struct B sb = {3.14, {'w', 100}, 'q'};
    printf("%lf %c %d %c\n", sb.d, sb.s.c, sb.s.i, sb.c);
    
    return 0;
}

🚩  3.140000 w 100 q

0x05 structure memory alignment

πŸ“š In this section, we will discuss how much memory space the structure occupies and learn how to calculate the size of the structure

πŸ’¬ Let's look at the following code first:

#include <stdio.h>

struct S
{
    char c1; // 1
    int i; // 4
    char c2; // 1
};

int main()
{
    struct S s = { 0 };
    printf("%d\n", sizeof(s));

    return 0;
}

🚩  12

❓ Why 12? This involves the problem of structure memory alignment.

πŸ“š Alignment rules for structures:

β‘  the first member of the structure starts at the 0 offset of the storage position of the structure variable in memory.

β‘‘ all members from the second member to the next shall be placed at the address of an integer multiple of the integer of the alignment number (the smaller value of the member size and the default alignment number). The default alignment number in VS is 8!

β‘’ the total size of the structure is an integer multiple of the largest alignment number of all members of the structure.

β‘£ if a structure is nested, the nested structure is aligned to an integer multiple of its maximum alignment number, and the overall size of the structure is an integer multiple of all the maximum alignment numbers (including the alignment number of nested structures).

πŸ“Œ Note: the default alignment number in VS is 8, and there is no default alignment number in Linux!

πŸ’¬ Exercise 1:

#include <stdio.h>

struct S2
{
    char c;
    int i;
    double d;
};

int main()
{
    struct S2 s2 = {0};
    printf("%d\n", sizeof(s2));

    return 0;
}

πŸ’¬ Exercise 2:

#include <stdio.h>

struct S3
{
    char c1;
    char c2;
    int i;
};

int main()
{
    struct S3 s3 = { 0 };
    printf("%d\n", sizeof(s3));

    return 0;
}

  πŸ’¬ Exercise 3:

#include <stdio.h>

struct S4
{
    double d;
    char c;
    int i;
};

int main()
{
    struct S4 s4 = { 0 };
    printf("%d\n", sizeof(s4));

    return 0;
}

πŸ’¬ Structure nesting problem:

If a structure is nested, the nested structure is aligned to an integer multiple of its maximum alignment number, and the overall size of the structure is an integer multiple of all the maximum alignment numbers (including the alignment number of nested structures).

#include <stdio.h>

struct S4
{
    double d;
    char c;
    int i;
};
struct S5
{
    char c1;
    struct S4 s4;
    double d;
};

int main()
{
    struct S5 s5 = {0};
    printf("%d\n", sizeof(s5));

    return 0;
}

❓ Why is there memory alignment?

 1. Platform reason (migration reason): not all hardware platforms can access any data at any address; Some hardware platforms can only get certain types of data at certain addresses, otherwise hardware exceptions will be thrown.

2. Performance reason: data structures (especially stacks) should be aligned on natural boundaries as much as possible. The reason is that in order to access misaligned memory, the processor needs to make two memory accesses; Aligned memory access requires only one access.

Memory alignment of structures is a method of trading space for time.

⚑ When designing the structure, how to meet the alignment and save space?

Let the members with small space gather together as much as possible.

πŸ’¬ Although S1 and S2 as like as two peas, it is possible to make the space smaller by making the best possible combination of the members.

struct S1
{
    char c1;
    int i;
    char c2;
};

struct S2
{
    char c1;
    char c2;
    int i;
};

0x06 modify default alignment number

πŸ“š The preprocessing instruction #pragma can change our default alignment number, #pragma pack(2)

#include <stdio.h>

// The default number of alignments is 8
#pragma pack(2) / / change the default alignment number to 2
struct S
{
    char c1; //1
    int i; // 4
    char c2; // 1
};

#pragma pack() / / cancel
int main()
{
    printf("%d\n", sizeof(struct S)); //12

    return 0;    
}

πŸ”Ί Conclusion: when the alignment of the structure is inappropriate, we can modify the default alignment number by using #pragma

0x07 offsetof

πŸ“š Function: this macro is used to calculate the offset of a member in the structure.

πŸ“œ Header file: stddef h

πŸ’¬ Usage demonstration:

#include <stdio.h>
#include <stddef.h>

struct S
{
    char c1; //1
    int i; // 4
    char c2; // 1
};

int main()
{
    printf("%d\n", offsetof(struct S, c1));
    printf("%d\n", offsetof(struct S, i));
    printf("%d\n", offsetof(struct S, c2));

    return 0;    
}

🚩  0 4 8

πŸ’­ Baidu written test question: write a macro to calculate the offset of a variable in the structure relative to the first address, and give a description.

0x08 structure parameter transfer

πŸ’¬ Observe the following codes:

#include <stdio.h>

struct S
{
 int data[1000];
 int num;
};

struct S s = {{1, 2, 3, 4}, 1000};

//Structural transmission parameters
void print1(struct S s)
{
    printf("%d\n", s.num);
}

//Structure address transfer parameter
void print2(struct S* ps)
{
    printf("%d\n", ps->num);
}

int main()
{
    print1(s);  //Transmission structure
    print2(&s); //Transmission address

    return 0;
}

❓ Which of the print1 (pass through structure) and print2 (pass through address) functions is better?

πŸ’‘ Answer: print2 (address transfer) function is preferred

πŸ”‘ Analysis: it is necessary to press the stack when passing function parameters, which will cause system overhead in time and space. If the structure is too large when passing a structure object, the system overhead of parameter stack will be large, resulting in performance degradation.

  πŸ”Ί Conclusion: when the structure passes parameters, the address of the structure should be passed.

2, bit field

0x00 what is a bit segment

πŸ“š Definition: bit segment. C language allows to specify the memory length occupied by its members in bits in a structure. This member in bits is called "bit segment" or "bit field". Using bit segments, data can be stored in fewer bits.

πŸ“š The declaration and structure of bit segments are similar, but there are two differences:

β‘  the members of the bit field can only be: int, unsigned int, signed int

β‘‘ there is a colon and a number after the member name of the bit field: member_name : number

πŸ’¬ Code demonstration:

struct A
{
    int _a:2;
    int _b:5;
    int _c:10;
    int _d:30;
}

// A is a bit segment type

❓ So the question is, what is the size of bit segment A?

#include <stdio.h>

struct A
{
    int _a:2;  // _ a members occupy 2 bits
    int _b:5;  // _ b members occupy 5 bits
    int _c:10; // _ c members occupy 10 bits
    int _d:30; // _ d members occupy 30 bits
};

int main()
{
    printf("%d\n", sizeof(struct A));

    return 0;
}

🚩  8

πŸ’‘ The running result is actually 8. Four members account for 47 bits, while 8 bytes are 64 bits. Why is this?

Memory allocation of 0x01 bit segment

πŸ“š Meaning of bit segment: bit segment helps us save space to a certain extent.

πŸ“Œ matters needing attention:

β‘  the members of the bit segment can be int, unsigned int, signed int or char (belonging to the shaping family).

β‘‘ the space of bit segment is opened up in the way of 4 bytes (int) or 1 byte (char) as required.

β‘’ bit segments involve many uncertain factors. Bit segments are not cross platform. Pay attention to portable programs and avoid using bit segments.

❓ How is space opened up?

struct S
{
    char a : 3;
    char b : 4;
    char c : 5;
    char d : 4;
};

int main()
{
    struct S s = { 0 };
    s.a = 10;
    s.b = 12;
    s.c = 3;
    s.d = 4;
}

Cross platform problem of 0x02 bit segment

1. It is uncertain whether the int bit field is regarded as a signed number or an unsigned number.

2. The number of the largest bits in the bit segment cannot be determined. For example, the 16 bit machine is 16 at most, and the 32-bit machine is 32 at most, which is written as 27. There will be problems on the 16 bit machine.

3. Whether the members in the bit segment are allocated from left to right or from right to left in memory has not been defined.

4. When a structure contains two bit segments, and the member of the second bit segment is too large to accommodate the remaining bits of the first bit segment, it is uncertain whether to discard the remaining bits or use them.

πŸ”Ί Summary:

Compared with the structure, bit segment can achieve the same effect, but bit segment can save space better. The defect is that there is a cross platform problem.

3, enumerate

0x00 what is enumeration

In mathematical and computer science theory, the enumeration of a set is to list some finite elements sequence Assembly, or a count of objects of a particular type. The two types often (but not always) overlap.



It is a collection of named integer constants. Enumeration is very common in daily life. For example, SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY and SATURDAY are enumerations. [Baidu Encyclopedia]

πŸ“š Enumeration, as the name suggests, is one by one enumeration, listing the possible values one by one.

eg. gender includes male, female and confidential, we can list them, or there are 12 months a year, we can list them every month.

0x01 definition of enumeration

  πŸ’¬ Code demonstration:

enum Day //week
{
    // enumeration constant 
    Mon,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun
};

enum Sex //Gender
{
    // enumeration constant 
    MALE,
    FEMALE,
    SECRET
};

enum Color //colour
{
    // enumeration constant 
    RED,
    GREEN,
    BLUE
};

πŸ“š enum Day, enum Sex and enum Color defined in the above code are enumeration types. The contents in {} are possible values of enumeration types, that is, enumeration constants. These possible values have values, starting from 0 by default and increasing by 1 in turn. Of course, it can also be given an initial value when defining, for example:

enum Color //colour
{
    // enumeration constant 
    RED = 1, // Initial value assignment
    GREEN = 2,
    BLUE = 4
};

0x02 advantages of enumeration

❓ We can use #define to define constants. Why do we have to use enumeration?

πŸ“š Advantages of enumeration:

β‘  increase the readability and maintainability of the code.

β‘‘ compared with #define defined identifiers, enumeration has type checking, which is more rigorous.

β‘’ effectively prevent naming pollution (packaging).

β‘£ convenient for debugging.

β‘€ easy to use, multiple constants can be defined at a time.

0x03 use of enumeration

πŸ’¬ Code demonstration:

#include <stdio.h>

enum Color //colour
{
    RED = 1,
    GREEN = 2,
    BLUE = 4
};

int main()
{
   enum Color c = GREEN;
   c = 5;
   printf("%d\n", c);

   return 0;
}

πŸ“Œ matters needing attention:

β‘  by default, it starts from 0 and increases by 1. (the initial value can be assigned, and the value assigned above will be + 1 if not assigned below)

β‘‘ enumeration constants cannot be changed. (MALE = 3  error!)

#include <stdio.h>

enum Sex
{
    // enumeration constant 
    MALE = 3, // The initial value is 3
    FEMALE, // No initial value is assigned. By default, it is accompanied by the previous enumeration constant, + 1 is 4
    SECRET // +1 is 5
};

int main(void)
{
    enum Sex s = MALE;
    printf("%d\n", MALE);
    // MALE = 3 error  ❌  Cannot be modified
    printf("%d\n", FEMALE);
    printf("%d\n", SECRET);

    return 0;
}

🚩  3  4  5

Β Β Β Β Β 

β‘’ although enumeration constants cannot be changed, variables created by enumeration constants can be changed!

enum Color 
{
    // enumeration constant 
    RED,
    YEELOW,
    BULE
};

int main(void)
{
    enum Color c = BULE; // We create a variable c and assign bull to it
    c = YEELOW; // At this time, there is no problem assigning YEELOW to it βœ…
    BULE = 6; // error! Enumeration constants cannot be changed ❌

    return 0;
}

0x04 practical application demonstration

πŸ’¬ This is how we wrote the code when we implemented the calculator: (only part of the code is demonstrated)

#include <stdio.h>

void menu()
{
    printf("*****************************\n");
    printf("**    1. add     2. sub    **\n");
    printf("**    3. mul     4. div    **\n");
    printf("**         0. exit         **\n");
    printf("*****************************\n");
}

int main()
{
    int input = 0;
    do {
        menu();
        printf("Please select:> ");
        scanf("%d", &input);
        switch {
            case 1:
                break;
            case 2:
                break;
            case 3:
                break;
            case 4:
                break;
            case 0:
                break;
            default:
                break;
    } while (input);
    
    return 0;
}

❓ Thinking: if you don't look at the menu above when reading the code, it's difficult to know what 12340 in the case is. 1 why add? 2 why is it minus? When you see a number, you can't think of what it does.

⚑ In order to improve the readability of the code, we can use enumeration to solve the following problems:

#include <stdio.h>

void menu() {...}

enum Option
{
    EXIT, // 0
    ADD,  // 1
    SUB,  // 2
    MUL,  // 3
    DIV,  // 4
};

int main()
{
    int input = 0;
    do {
        menu();
        printf("Please select:> ");
        scanf("%d", &input);
        switch {
            case ADD: // It is much better after replacement, and the readability of the code is greatly increased
                break;
            case SUB:
                break;
            case MUL:
                break;
            case DIV:
                break;
            case EXIT:
                break;
            default:
                break;
    } while (input);
    
    return 0;
}

4, Consortium

0x00 what is a consortium

πŸ“š Definition: consortium, also known as community, is a special user-defined type. Different data types can be stored in the same memory location. You can define a union with multiple members, but only one member can have a value at any time.

0x01 definition of consortium

πŸ’¬ Code demonstration:

#include <stdio.h>

union Un
{
    char c; // 1
    int i; // 4
};

int main()
{
    union Un u; // Create a consortium variable
    printf("%d\n", sizeof(u)); // Calculate the size of the consortium variable

    return 0;
}

 🚩  4

❓ Why 4 bytes? Let's try to observe its memory:

#include <stdio.h>

union Un
{
    char c; // 1
    int i; // 4
};

int main()
{
    union Un u;
    printf("%p\n", &u);
    printf("%p\n", &(u.c));
    printf("%p\n", &(u.i));

    return 0;
}

🚩 The operation results are as follows:

πŸ”Ί Conclusion: the members of the consortium share the same memory space. Because the union must at least have the ability to save the largest member, the size of a union variable is at least the size of the largest member.

0x02 consortium initialization

πŸ’¬ Code demonstration:

#include <stdio.h>

union Un
{
    char c; // 1
    int i; // 4
};

int main()
{
    union Un u = {10};

    return 0;
}

🐞 Debugging: after monitoring is turned on, we can see that i and c share the same 10:

❓ What if you want to put independent values in each member?

#include <stdio.h>

union Un
{
    char c; // 1
    int i; // 4
};

int main()
{
    union Un u = {10}; 
    u.i = 1000;   
    u.c = 100;
    
    return 0;
}

🐞 Observe the commissioning process:

  πŸ”Ί Conclusion: you can only use one member of the Consortium at the same time.

0x03 calculation of consortium size

πŸ’¬ Look at the code:

#include <stdio.h>

union Un
{
    char a[5]; // 5
    int i; // 4
};

int main()
{
    union Un u;
    printf("%d\n", sizeof(u));

    return 0;
}

🚩  8

❓ Why 8 bytes again?

πŸ”‘ In fact, consortia are aligned. Let's explore the size rules of lower consortia more systematically and in detail:

πŸ“š Calculation of consortium size:

β‘  the size of the union is at least the size of the largest member.

β‘‘ when the size of the largest member is not an integer multiple of the maximum number of alignments, align to the integer multiple of the maximum number of alignments.

union Un
{
    char a[5]; // The number of alignments is 1
    int i; // The number of alignments is 4
};

// Therefore, 8 bytes are finally taken as the size of the consortium

0x04 practical application demonstration

Judge the current machine size through the consortium

πŸ”— Review link: [vitamin C language] Chapter 9 - data storage (IV. large and small ends)

πŸ’‘ Implementation idea:

  πŸ’¬ Previously learned methods:

#include <stdio.h>

int main()
{
    int a = 1;
    if ( ( *(char*)&a ) == 1 )
        printf("Small end\n");
    else
        printf("Big end\n");
    
    return 0;
}

  ⚑ Encapsulate it as a function:

#include <stdio.h>

int check_sys() 
{
    int a = 1;
    return *(char*)&a;
}
int main()
{
    int ret = check_sys();
    if(ret == 1)
        printf("Small end\n");
    else
        printf("Big end\n");
    
    return 0;
}

πŸ’¬ Judgment by Consortium: (code written by deeply understanding the characteristics of the consortium)

#include <stdio.h>

int check_sys() 
{
    union U {
        char c;
        int i;
    } u;

    u.i = 1;
    return u.c;
    // Return 1 is the small end
    // Return 0 is the big end
}
int main()
{
    int ret = check_sys();
    if(ret == 1)
        printf("Small end\n");
    else
        printf("Big end\n");
    
    return 0;
}

Chapter 13 - dynamic memory management

preface:

This chapter will explain the dynamic memory management of C language from shallow to deep. After learning this chapter, you can do some exercises on dynamic memory allocation to deepen the consolidation and reduce the probability of stepping on the dynamic memory allocation pit:

πŸšͺ Portal: Dynamic memory allocation (written test question + answer + detailed explanation)

1, Dynamic memory allocation

0x00 import

πŸ“š At present, we have mastered the following two ways to open up memory:

// Open up four bytes on the stack
int val = 20;

// Open up 10 bytes of continuous space on the stack space
char arr[10] = {0};

πŸ“š The above way of opening up space has two characteristics:

β‘  the size of the space is fixed.

β‘‘ the length of the array must be specified when declaring the array, and the required memory space will be opened up and allocated during compilation.

0x01 definition

πŸ” [Baidu Encyclopedia] dynamic memory allocation

Dynamic memory allocation refers to the method of dynamically allocating or reclaiming storage space during program execution. Unlike static memory allocation methods such as arrays, dynamic memory allocation does not require pre allocation of storage space, but is allocated by the system immediately according to the needs of the program, and the allocated size is the size required by the program.

0x02 reason for existence

❓ Why is there dynamic memory development?

πŸ’‘ Sometimes the size of space we need can only be known when the program is running. At this time, the way of opening up space during array compilation can not be satisfied. At this time, we need dynamic memory development to solve the problem.

2, Introduction to dynamic memory functions

0x00 malloc function

πŸ“œ Header file: stdlib h

  πŸ“š Introduction: malloc is a dynamic memory development function provided by C language. This function applies for a continuously available space from memory and returns a pointer to this space. The details are as follows:

β‘  if the development is successful, a pointer to the developed space is returned.

β‘‘ if the development fails, a NULL pointer is returned.

β‘’ the type of the return value is void *, and the malloc function does not know the type of open space, which is determined by the user.

β‘£ if the size is 0 (0 bytes), the behavior of malloc is not defined in the standard, and the result will depend on the compiler.

πŸ” Official introduction: malloc - C++ Reference

0x01 free function

πŸ“œ Header file: stdlib h

  πŸ“š Introduction: the free function is used to release the dynamically opened memory space. The details are as follows:

β‘  if the space pointed to by the parameter ptr is not dynamically opened up, the behavior of the free function is undefined.

β‘‘ if the parameter ptr is a NULL pointer, free will not perform any action.

πŸ“Œ matters needing attention:

β‘  after use, you must remember to use the free function to release the opened memory space.

β‘‘ use the pointer to point to the dynamically opened memory. Remember to set it as a null pointer after using it and free.

πŸ” Official introduction: http://www.cplusplus.com/reference/cstdlib/malloc/?kw=free

πŸ’¬ Code demonstration: dynamic memory opens up 10 integer spaces (complete steps)

#include <stdio.h>
#include <stdlib.h>

int main(void) 
{
    // Suppose you open up 10 integer spaces
    int arr[10]; // Open up on stack area

    // Dynamic memory development
    int* p = (int*)malloc(10*sizeof(int)); // Open up 10 spaces of size int

    // When using these spaces
    if (p == NULL) {
        perror("main"); // main: error message
        return 0;
    }
    
    // use
    int i = 0;
    for (i = 0; i < 10; i++) {
        *(p + i) = i;
    }
    for (i = 0; i < 10; i++) {
        printf("%d ", p[i]);
    }

    // Recycle space
    free(p);
    p = NULL; // Need to manually set null pointer

    return 0;
}

🚩  0 1 2 3 4 5 6 7 8 9

  ❗ Failure of dynamic memory development: (perror function)

#include <stdio.h>
#include <stdlib.h>

int main(void) 
{
    ...
    int* p = (int*)malloc(9999999999*sizeof(int)); // open one's mouth wide. Here you go
    ...

}

🚩   main: Not enough space

❓ Why do you have to set p as a null pointer after free?

πŸ”‘ Analysis: because the memory space opened after free is no longer available, its function is to reclaim the opened space, but p still points to the starting position of the memory space. Is this reasonable? This is unreasonable. So we need to use p = NULL to set it as a null pointer. To deepen the impression, give an image example:

❓ Why should malloc be cast before?

int* p = (int*)malloc(10*sizeof(int));

πŸ”‘ Resolution: in order to correspond with int* p type, cast is required. You can try to delete the strong conversion. In fact, there won't be any problem. However, because some compilers require forced conversion, it is best to perform forced conversion to avoid unnecessary trouble.

0x02 calloc function

πŸ“œ Header file: stdlib h

πŸ“š Introduction: the function of calloc function actually opens up a space for num size elements, initializes each byte of the space to 0, and returns a pointer to it.

β­• contrast:

β‘  malloc has only one parameter, while calloc has two parameters: the number of elements and the size of elements.

β‘‘ the difference between calloc and malloc is that calloc initializes each byte of the requested space to 0 before returning the address.

πŸ” Official introduction: http://www.cplusplus.com/reference/cstdlib/malloc/?kw=calloc

πŸ’¬ Verification: calloc initializes the memory

#include <stdio.h>
#include <stdlib.h>

int main()
{
    // malloc
    int* p = (int*)malloc(40); // Open up 40 spaces
    if (p == NULL)
        return 1;
    int i = 0;
    for (i = 0; i < 10; i++)
        printf("%d ", *(p + i));
    free(p);
    p = NULL;

    return 0;
}

  🚩 (the running result is 10 random values)

#include <stdio.h>
#include <stdlib.h>

int main()
{
    // calloc
    int* p = (int*)calloc(10, sizeof(int)); // Open up 10 spaces of size int, 40
    if (p == NULL)
        return 1;
    int i = 0;
    for (i = 0; i < 10; i++)
        printf("%d ", *(p + i));
    free(p);
    p = NULL;

    return 0;
}

 🚩  0 0 0 0 0 0 0 0 0 0

πŸ”Ί Summary: description calloc initializes the memory and initializes each byte of the space to 0. If we require the initialization of the requested memory space, we can easily implement it by using the calloc function.

0x03 realloc function

πŸ“œ Header file: stdlib h

πŸ“š Introduction: realloc function makes dynamic memory management more flexible. It is used to readjust the size of the memory blocks referred to by the ptr allocated before malloc or calloc, and adjust the size of the dynamically opened memory. Details are as follows:

β‘  ptr is the memory address to be adjusted by the pointer.

β‘‘ size is the new size after adjustment.

β‘’ the return value is the adjusted memory starting position. If the request fails, a null pointer is returned.

β‘£ on the basis of adjusting the size of the original memory space, the realloc function will also move the data in the original memory to a new space.

πŸ“Œ There are three situations when realloc function adjusts memory space:

Case 1: there is enough space after the original space.

Case 2: there is not enough space after the original space.

Case 3: realloc may not find the right space to resize.

Case 1: when there is not enough space after the original space, the space is directly added after the original memory, and the array of the original space does not change.

Case 2: when there is not enough space behind the original space, another continuous space of appropriate size will be found on the heap space for use. The return value of the function will be a new memory address.

Case 3: if no suitable space is found, a null pointer is returned.

πŸ” Official introduction: http://www.cplusplus.com/reference/cstdlib/malloc/?kw=realloc

πŸ’¬ Code demonstration: realloc resizing memory

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)calloc(10, sizeof(int));
    if (p == NULL) {
        perror("main");
        return 1;
    }
    // use
    int i = 0;
    for (i = 0; i < 10; i++) {
        *(p + i)  = 5;
    }
    // At this time, the space that p points to is larger, and the space of 20 ints is required
    // realloc adjustment space
    p = (int*)realloc(p, 20*sizeof(int)); // Space resized to 20 int s

    // release
    free(p);
    p = NULL;
}

  ❗ In the third case just mentioned, if realloc cannot find a suitable space, it will return a null pointer. We want to increase its capacity, but it is in danger of returning null pointers. How can this work?

πŸ’‘ Solution: don't take the pointer to directly receive realloc. You can use the temporary pointer to judge.

#include <stdio.h>
#include <stdlib.h>

int main() 
{
    int* p = (int*)calloc(10, sizeof(int));
    if (p == NULL) {
        perror("main");
        return 1;
    }
    // use
    int i = 0;
    for (i = 0; i < 10; i++) {
        *(p + i)  = 5;
    }
    // At this time, the space that p points to is larger, and the space of 20 ints is required
    // realloc adjustment space
    int* ptmp = (int*)realloc(p, 20*sizeof(int));
    // If ptmp is not equal to the null pointer, deliver p to it
    if (ptmp != NULL) {
        p = ptmp;
    }

    // release
    free(p);
    p = NULL;
}

  πŸ“š Interestingly, you can actually use realloc as malloc:

// In the memory address section to adjust, pass in NULL:
int* p = (int*)realloc(NULL, 40); // The function here is similar to malloc, which is to open up 40 bytes directly in the heap

3, Common dynamic memory errors

0x00 dereference of null pointer

❌ Code demonstration:

#include <stdlib.h>
#include <stdio.h>

int main()
{
    int* p = (int*)malloc(9999999999);
    int i = 0;
    for (i = 0; i < 10; i++) {
        *(p + i) = i; // Dereference of null pointer, illegal access to memory
    }

    return 0;
}

πŸ’‘ Solution: empty the return value of malloc function

#include <stdlib.h>
#include <stdio.h>

int main()
{
    int* p = (int*)malloc(9999999999);
    // Empty the return value of malloc function
    if (p == NULL) {
        perror("main")
        return 1;
    }
    int i = 0;
    for (i = 0; i < 10; i++) {
        *(p + i) = i; // Dereference of null pointer, illegal access to memory
    }

    return 0;
}

0x01 cross border access to dynamic open space

❌ Code demonstration:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)malloc(10*sizeof(int)); // Apply for 10 integer spaces
    if (p == NULL) {
        perror("main");
        return 1;
    }
    int i = 0;
    // Out of bounds access - pointer p only manages 10 integer spaces and cannot access 40 at all
    for (i = 0; i < 40; i++) {
        *(p + i) = i;
    }

    free(p);
    p = NULL;

    return 0;
}

πŸ’‘ Reminder: in order to prevent cross-border access, you must pay attention to the size of the open space when using the space.

0x02 use free release for non dynamic memory

❌ Code demonstration:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int arr[10] = {0}; // Open up on stack area
    int* p = arr;
    // Use slightly

    free(p); // Use free to free up non dynamic space
    p = NULL;

    return 0;   
}

Β 

  πŸ’‘ Reminder: do not use free for non dynamically opened memory, otherwise unexpected errors will occur.

0x03 use free to release a part of dynamic memory

❌ Code demonstration:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = malloc(10*sizeof(int));
    if (p == NULL) {
        return 1;
    }
    int i = 0;
    for (i = 0; i < 5; i++) {
        *p++ = i; // The space pointed to by p has been changed
    }

    free(p);
    p = NULL;
  
    return 0;
}

πŸ“Œ Note: writing code like this will cause p to release only the space behind it. No one remembers the starting position of this space, and no one can find it anymore. This is a terrible thing, and there is a risk of memory leakage.

  πŸ’‘ Reminder: when releasing memory space, be sure to release it from the beginning.

0x04 multiple releases of the same dynamic memory

❌ Code demonstration:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = malloc(10*sizeof(int));
    if (p == NULL) {
        return 1;
    }
    int i = 0;
    for (i = 0; i < 10; i++) {
        p[i] = i;
    }

    // release
    free(p);
    // A brain fever, released again
    free(p);
  
    return 0;
}

πŸ’‘ Solution: set p as a null pointer immediately after the first release

// release
free(p);
p = NULL;

free(p); // At this point p is empty and free does nothing

0x05 memory leakage caused by dynamic memory forgetting to release

❌ Code demonstration:

#include <stdio.h>
#include <stdlib.h>

void test()
{
    int* p = (int*)malloc(100);
    if (p == NULL) {
        return;
    }
    // Use slightly
    
    // I forgot to release at this time
}

int main()
{
    test();
    
    free(p); // At this time, it can't be released. No one knows where the starting position of this space is
    p = NULL;
}

There are two ways to reclaim dynamic memory space: 1 Active release (free) 2 Program end

If this program runs on the server for 7x24 hours, if you don't actively release or you can't find this space, it will eventually lead to memory leakage. Memory Leak refers to the heap memory that has been dynamically allocated in the program. For some reason, the program does not release or cannot release, resulting in a waste of system memory, slowing down the running speed of the program and even system crash.

πŸ’‘ Reminder: malloc and free must be used in pairs. Remember to release them in time. If you don't intend to use the space you apply for, you can release it yourself. If you want to pass the space you applied for to others for use, be sure to remind others to release it when you pass it to others.

Chapter 14 - Analysis of memory development of C/C + + program

Preface:

This blog aims to deepen the understanding of dynamic memory development, and briefly analyzes the memory development of C/C + + programs.

Memory allocation area of C/C + + program:

1. stack area

When executing a function, the storage units of local variables inside the function can be created on the stack. After the function is executed, these storage units will be automatically released. Stack memory allocation is built into the instruction set of the processor, which has high efficiency, but the allocated memory capacity is limited. The stack area mainly stores the local variables, function parameters, return data, return address, etc. allocated for running the function.

2. heap

Generally, it is distributed and released by the programmer independently. If the programmer does not actively release it, it may be recycled by the operating system at the end of the program. Its allocation method is similar to linked list.

3. data segment

Static storage area, which stores global variables and static data, and is released by the system after the program is completed.

4. code segment

Store the binary code of the function body (class member function and global function).

πŸ’¬ Memory area division diagram:

πŸ“š We talked about static keyword modifying local variables in previous chapters. With this figure, we can better understand:

πŸ”‘ In fact, ordinary local variables allocate space in the stack area. Variables created in the stack area will be destroyed out of the scope. However, if a variable is modified by static, it will be stored in the data segment (static area), and the variables created on the data segment will not be destroyed until the end of the program, so the life cycle becomes longer.

Characteristics of stack area: the variables created above are destroyed when they are out of scope.

Characteristics of data segment: the variables created above are not destroyed until the end of the program

Chapter 15 - flexible array (variable length array)

preface:

This article will explain the new feature introduced in C99 standard - flexible array. It also discusses the advantages of flexible array, briefly introduces the related concepts of memory pool, and realizes the advantages of flexible array.

1, Introduction to flexible array

πŸ“š Definition: Flexible Array, also known as variable length array. The length of a general array is determined at compile time, while the length of a Flexible Array object is determined at run time. When defining a structure, you are allowed to create an empty array (for example, arr [0]). The size of the array can be changed according to your needs during the running of the program.

πŸ” Source: Flexible Array is a new feature introduced in the C99 standard of C language. The size of the last element in the structure is allowed to be an unknown array, that is, a Flexible Array.

[Baidu Encyclopedia] after the establishment of ANSI standard, the specification of C language has not changed greatly for a period of time. However, C + + continues to grow in the process of its own standardization. Standard Amendment 1 created a new standard for C language in 1994, but only revised some details in C89 standard and added more and wider international character set support. However, this standard led to the publication of ISO 9899:1999 in 1999. Known as C99, C99 was adopted by ANSI in March 2000.

πŸ’¬ demonstration:

struct S {
    int n;
    int arr[]; // πŸ‘ˆ  Flexible array member
};

  ❗ Some compilers may report errors. Try changing a [0] to a []:

struct S {
    int n;
    int arr[]; // πŸ‘ˆ  Flexible array member
};

2, Characteristics of flexible array

πŸ’¬ A flexible array member in a structure must be preceded by at least one other member:

typedef struct st_type {
    int i;    // πŸ‘ˆ  There must be at least one other member
    int a[0]; // πŸ‘ˆ  Flexible array member
} type_a;

πŸ’¬ sizeof calculates the size of this structure without flexible array members:

#include <stdio.h>

struct S {
    int n; // 4
    int arr[]; // The size is unknown
};

int main() {
    struct S s = {0};
    printf("%d\n", sizeof(s));

    return 0;
}

🚩  4

πŸ’¬ For structures containing flexible array members, malloc function is used for memory allocation, and the allocated memory should be larger than the size of the structure to adapt to the expected size of the flexible array:

#include <stdio.h>
#include <stdlib.h>

struct S {
    int n;
    int arr[0];
};

int main() {
    // The expected size of arr is 10 integers

    //         For n, for arr[]
    //           πŸ‘‡                πŸ‘‡
    struct S* ps = (struct S*)malloc(sizeof(struct S) + sizeof(int)); 
    // The size of the following + is prepared for flexible arrays

    return 0;
}

πŸ’‘ analysis:

3, Use of flexible arrays

πŸ’¬ Code demonstration:

#include <stdio.h>
#include <stdlib.h>

struct S {
    int n;
    int arr[0];
};

int main() {
    // The expected size of arr is 10 integers
    struct S* ps = (struct S*)malloc(sizeof(struct S) + sizeof(int));
    ps->n = 10;

    // use
    int i = 0;
    for (i = 0; i < 10; i++) {
        ps->arr[i];
    }

    // increase capacity
    struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 20*sizeof(int));
    if (ptr != NULL) {
        ps = ptr;
    }

    // Reuse (omitted)

    // release
    free(ps);
    ps = NULL;

    return 0;
}

🐞 View address:

4, Advantages of flexible arrays

πŸ’¬ Code 1: using flexible arrays

/* Code 1 */
#include <stdio.h>
#include <stdlib.h>

struct S {
    int n;
    int arr[0];
};

int main() {
    // The expected size of arr is 10 integers
    struct S* ps = (struct S*)malloc(sizeof(struct S) + sizeof(int));
    ps->n = 10;

    // use
    int i = 0;
    for (i = 0; i < 10; i++) {
        ps->arr[i];
    }

    // increase capacity
    struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 20*sizeof(int));
    if (ptr != NULL) {
        ps = ptr;
    }

    // Reuse (omitted)

    // release
    free(ps);
    ps = NULL;

    return 0;
}

πŸ’¬ Code 2: direct use of pointers

If you want n to have its own space, you can do it without using a flexible array.

/* Code 2 */

#include <stdio.h>
#include <stdlib.h>

struct S {
    int n;
    int* arr;
};

int main() {
    struct S* ps = (struct S*)malloc(sizeof(struct S));
    if (ps == NULL)
        return 1;
    ps->n = 10;
    ps->arr = (int*)malloc(10 * sizeof(int));
    if (ps->arr == NULL)
        return 1;

    // use
    int i = 0;
    for (i = 0; i < 10; i++) {
        ps->arr[i];
    }

    // increase capacity
    int* ptr = (struct S*)realloc(ps->arr, 20 * sizeof(int));
    if (ptr != NULL) {
        ps->arr = ptr;
    }

    // Reuse (omitted)

    // release
    free(ps->arr); // free the second space first
    ps->arr = NULL;
    free(ps);
    ps = NULL;

    return 0;
}

❓ The above code 1 and code 2 can achieve the same energy, which is better?

πŸ’‘ Obviously, code 1 is better:

β‘  first benefit: it is conducive to memory release

Although code 2 implements the corresponding functions, there are still many deficiencies compared with code 1. Code 2 is completed with pointers and malloc twice, and malloc twice corresponds to free twice, which is more prone to error than code 1. If our code is in a function for others, you allocate memory twice and return the whole structure to the user. Although the user can release the structure by calling free, the user does not know that the members in the structure also need free, so you can't expect the user to find it. Therefore, if we allocate the memory of the structure and the memory required by its members at one time (rather than multiple times), and return a structure pointer to the user, the user can release all memory by using free only once, which can indirectly reduce the possibility of memory leakage.

β‘‘ the second advantage: it is conducive to access speed

Continuous memory is more or less beneficial to improve access speed and reduce memory fragmentation. The more malloc times, the more memory fragments will be generated. These memory fragments are not big or small, and the possibility of being used again is very low. The more memory fragments, the lower the memory utilization. Frequent space development will reduce efficiency and increase debris.

  πŸ”Ί Summary: therefore, using flexible arrays is more or less beneficial.

Chapter 16 - document operation (I)

preface:

This chapter is the first part of document operation teaching, which introduces the problems from simple to deep, and then introduces the knowledge one by one. It will explain in detail the opening and closing of files, the sequential reading and writing of files, elaborate on the function part, and preliminarily learn the concept of "flow"!

  πŸšͺ Portal: File operation (Part 2)

1, Problem introduction

0x00 what is a file?

[Baidu Encyclopedia] computer file, also known as computer file, is a data stream stored in a long-term storage device or temporary storage device, and belongs to the management of computer file system. The so-called "long-term storage equipment" generally refers to magnetic disk, optical disk, magnetic tape, etc. And "short term storage device" generally refers to Computer memory . It should be noted that it is stored for a long time storage device The files may not be stored for a long time, and some may also be temporary data generated during the operation of the program or system and deleted after the program or system exits.

πŸ“š In short, it is the file on the disk.

0x01 why use files?

πŸ“š For example, when we want to implement an "address book" program, a series of operations such as creating and deleting contacts in the address book. At this time, the data is stored in memory, and all data will disappear after the program exits. In order to save the information in the address book, that is, to make the data persistent, we need to adopt the method of data persistence. Our general methods of data persistence are: storing data in disk files, or in databases.

0x02 what are program files and data files?

πŸ“š However, in programming, we generally refer to files as program files and data files.


πŸ“‚ Program files: program files include source program files (suffix. c), target files (suffix. obj in Windows Environment), and executable programs (suffix. exe in Windows Environment).

πŸ“‚ Data file: the content of the data file is not necessarily the program, but the data read and written by the program during operation, such as the file from which the program needs to read data or the file that outputs the content.

πŸ’¬ Let's write a random piece of code to demonstrate:

#include <stdio.h>

int main(void) {
    printf("Hello,World!\n");
    
    return 0;
}

🚩 Then run the code (for file generation):

❓ So, what is a program file?

Find the code path, open the folder and view the executable program:

Go back to the upper directory and find the "target file":

❓ What is the data file?

Create a file under the code path:

0x03 what is a file name?

[Baidu Encyclopedia] the file name is the identification of the existence of the file, and the operating system controls and manages it according to the file name. Different operating systems have slightly different rules for file naming, that is, the format and length of file names vary from system to system. In order to facilitate people to distinguish different files in the computer, each file is given a specified name. It consists of the main name and extension of the file.

πŸ“š Meaning of existence: a file should have a unique file ID to facilitate user identification and reference.

  ❗ The file name consists of three parts: file path + file name trunk + file suffix

(for example: C:\code2021\TestDemo.txt)

πŸ”‘ For convenience, the file ID is often referred to as the file name.

πŸ”Ί In this chapter, we will discuss data files!

2, Opening and closing of files

The file should be opened before reading and writing, and closed after use.

When writing a program, when opening a FILE again, a FILE * pointer variable will be returned to point to the FILE, which is equivalent to establishing the relationship between the pointer and the FILE.

ANSIC specifies that fopen function is used to open the file and fclose function is used to close the file.

0x00 file pointer

❓ What is a file pointer:

[Baidu Encyclopedia] in C language, a pointer variable is used to point to a file, which is called a file pointer. Through the file pointer, you can perform various operations on the file it refers to.

πŸ”‘ In the buffered file system, a key concept is "file type pointer", referred to as "file pointer".

πŸ“š Each used FILE will open up a corresponding FILE information area in memory. This information area is used to store FILE related information (such as FILE name, FILE status, current FILE location, etc.). This information is stored in a structure variable. The structure type is declared by the system and is called FILE (note the type).

  πŸ’¬ For example, stdio provided by VS2013 compilation environment The H header file contains the following file type declarations:

struct _iobuf {
    char *_ptr;
    int   _cnt;
    char *_base;
    int   _flag;
    int   _file;
    int   _charbuf;
    int   _bufsiz;
    char *_tmpfname;
};
typedef struct _iobuf FILE;

πŸ“Œ matters needing attention:

β‘  The structure of FILE is not exactly the same in different C editors, but it is quite similar.

β‘‘ Whenever a FILE is opened, the system will automatically create a variable of FILE structure according to the status of the FILE and fill in the information in it. As long as the FILE is read and written, the FILE information area will also change. As for how the FILE information area changes and modifies when the FILE changes, we don't need to care about these details, because the C language has helped you.

β‘’ We usually maintain the variables of the FILE structure through a FILE pointer. It will not be used directly, but take a structure pointer to this structure, and use this pointer to access and maintain relevant data, which will be more convenient to use.

πŸ’¬ Let's create a pointer variable of FILE *:

Definition PF is a pointer variable of type FILE. You can make pf point to the FILE information area (a structure variable) of a FILE. The FILE can be accessed through the information in the FILE information area.

FILE* pf; // File pointer variable

πŸ’‘ That is, the file associated with it can be found through the file pointer variable.

0x01 fopen function and fclose function

πŸ“œ Header file: stdlib h

πŸ“š ANSIC specifies that fopen function is used to open the file and fclose function is used to close the file.

βœ… The filename parameter refers to the file name, and the mode parameter is the opening method. The opening method is as follows:

File usage

meaning

If the specified file does not exist
"r" (read only)To enter data, open an existing text fileerror
"w" (write only)To output data, open a text fileCreate a new file
"a" (added)Add data like the end of a text fileCreate a new file
"rb" (read only)To enter data, open a binary fileerror
"wb" (write only)To output data, open a binary fileCreate a new file
"ab" (additional)Add data like the end of a binary fileerror
"r +" (read / write)To read and write, open a text fileerror
"w +" (read / write)Create a new file for reading and writingCreate a new file
"a +" (read / write)Open a file and read and write at the end of the fileCreate a new file
"rb +" (read / write)To read and write, open a binary fileerror
"wb +" (read / write)Create a new binary file for reading and writingCreate a new file
"ab +" (read / write)Open a binary file and read and write at the end of the fileCreate a new file

πŸ’¬ Code demonstration: open the test. XML file we just created manually Dat file

#include <stdio.h>

int main(void) {
    FILE* pf = fopen("test.dat", "w");
    // Check for null pointers
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    /* Write file */

    // Close file
    fclose(pf);
    pf = NULL; // Remember to set pf as a null pointer

    return 0;
}

🚩 (code works normally)

❓ We created test The path of DAT is Under the path, can I read it if it is placed under other paths?

πŸ”‘ Yes, but the file must be in the path of the project.

πŸ’¬ We put test Delete the dat file, and then change the opening method to r try:

#include <stdio.h>

int main(void) {
    FILE* pf = fopen("test.dat", "r");
    // Check for null pointers
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    /* Write file */

    // Close file
    fclose(pf);
    pf = NULL; // Remember to set pf as a null pointer

    return 0;
}

🚩 The running results are as follows: (it can be seen from the table just now that an error will be reported if r the specified file is not found)

πŸ’¬ If relative path is not applicable, read the file using absolute path:

You can use absolute paths, but be careful to escape slashes in absolute paths!

#include <stdio.h>

int main(void) {
    // FILE* pf = fopen("D:\code2021\0817\0817\test2.dat", "w"); // error
    FILE* pf = fopen("D:\\code2021\\0817\\0817\\test2.dat", "w"); // Transfer character\

    // Check for null pointers
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    /* Write file */

    // Close file
    fclose(pf);
    pf = NULL; // Remember to set pf as a null pointer

    return 0;
}

πŸ“Œ Note: consequences of not closing files: the files that a program can open are limited, and files belong to a resource. If you only open it without releasing it, the file will be occupied. Some operations may be buffered in memory. If they cannot be closed normally, the data buffered in memory cannot be written to the file normally, resulting in data loss.

3, Sequential reading and writing of files

0x00 what is sequential read / write

First of all, we should understand what reading and writing is: the program we write is in memory, the data is to be put in the file, and the file is on the hard disk. When we read the data in the file into memory, this action is called input / read. Conversely, if you put something in the program on the hard disk, this action is called output / write.

πŸ“š Sequential reading and writing, as the name suggests, is to read and write in the file in order.

0x01 list of sequential read / write functions

0x02 character output function fputc

πŸ“š Description: write the character specified in the parameter char to the specified stream, and move the location identifier forward (the character must be an unsigned character). Applicable to all output streams.

πŸ’¬ Code demonstration: create a test Txt, and then use the fputc function to write "abc" to the file respectively

#include <stdio.h>

int main(void) {
    FILE* pf = fopen("test.txt", "w");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    // Write file
    fputc('a', pf);
    fputc('b', pf);
    fputc('c', pf);

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

🚩 (code works normally)

πŸ“‚ At this point, open the project folder and you can successfully see test Txt is created (the size is 1kb, which shows that the writing is successful):

πŸ’¬ We just tested the coverage effect of w, and we commented out the content:

#include <stdio.h>

int main(void) {
    FILE* pf = fopen("test.txt", "w");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    // Write file
    //fputc('a', pf);
    //fputc('b', pf);
    //fputc('c', pf);

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

🚩 When running again, we find that the contents of that file are missing (the size also changes to 0kb):

  ❗ It is worth noting that the writing of files is sequential. abc, first a, then b, and finally c:

fputc('a', pf);
fputc('b', pf);
fputc('c', pf);

0x03 character input function fgetc

πŸ“š Description: get the next character from the specified stream and move the location identifier forward (the character must be an unsigned character). If the reading is successful, it will return the corresponding ASCII code value. If the reading fails, it will return an EOF. Applicable to all input streams.

πŸ’¬ Code demonstration: open test in the project folder Txt, write some data casually, and then use the fgetc function to read:

#include <stdio.h>
// Use fgetc to read from the file
int main(void) {
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    // read file
    int ret = fgetc(pf);
    printf("%c\n", ret);
    ret = fgetc(pf);
    printf("%c\n", ret);
    ret = fgetc(pf);
    printf("%c\n", ret);

    // Close file
    fclose(pf);
    pf = NULL;
    
    return 0;
}

🚩 The operation results are as follows:

❓ What happens if you finish reading?

🐞 Let's debug:

0x04 text line output function fputs

πŸ“š Description: writes a string to the specified stream (excluding null characters). Applicable to all output streams.

πŸ’¬ Code demonstration: using fputs in test2 Txt:

#include <stdio.h>

int main(void) {
    FILE* pf = fopen("test2.txt", "w");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    // Write file - write by line
    fputs("abcdef", pf);
    fputs("123456", pf);

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

🚩 (code runs successfully)

❓ What if you want to change your line?

πŸ”‘ Line feed needs to be added in the code \ n

fputs("abcdef\n", pf);
fputs("123456", pf);

(if you open the file at this time, it will wrap the line.)

0x05 text line input function fgets

πŸ“š Introduction: read a line from the specified stream and store it in the string pointed to by the string. When reading (n-1) characters, or when reading the newline character and reaching the end of the file, it will stop, depending on the situation. Applicable to all input streams.

πŸ“Œ Note: if n is 100, 99 characters (n-1) will be read, because a character will be left for the slash 0.

πŸ’¬ Code demonstration: use fgets to read test2 Txt:

#include <stdio.h>

int main(void) {
    char arr[10] = "xxxxxx"; // Storage

    FILE* pf = fopen("test2.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    // Read file - read by line
    fgets(arr, 4, pf);
    printf("%s\n", arr);

    fgets(arr, 4, pf);
    printf("%s\n", arr);

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

🚩 The running results of the code are as follows:

  🐞 Debug:

0x06 format output function fprintf

πŸ“š Introduction: fprintf is used to write files to formatted data and send formatted output to stream. Applicable to all output streams.

πŸ’¬ Code demonstration: use fprintf to write the three data of the structure to test3 Txt:

#include <stdio.h>

struct Player {
    char name[10];
    int dpi;
    float sens;
};

int main(void) {
    struct Player p1 = { "carpe", 900, 3.12f };

    // Write the formatted data to a file
    FILE* pf = fopen("test3.txt", "w");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    // Write file
    fprintf(pf, "%s %d %f", p1.name, p1.dpi, p1.sens);

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

  🚩 (code runs successfully)

Β 

0x07 format input function fscanf

πŸ“š Introduction: fscanf is used to read formatted data and read formatted input from stream. Applicable to all input streams.

πŸ’¬ Code demonstration: use fscanf to read test4 Txt and print:

#include <stdio.h>

struct Player {
    char name[10];
    int dpi;
    float sens;
};

int main(void) {
    struct Player p1 = { 0 }; // Storage

    // Write the formatted data to a file
    FILE* pf = fopen("test3.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    // read file
    fscanf(
        pf, "%s %d %f",
        p1.name, &(p1.dpi), &(p1.sens) 
    ); // πŸ‘†  p1.name itself is the address (no &)

    // Print the read data
    printf("%s %d %f\n", p1.name, p1.dpi, p1.sens);

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

  🚩 The operation results are as follows:

0x08 binary output function fwrite

πŸ“š Introduction: write a data to the stream, and write the data in the array pointed to by the buffer to the given stream.

πŸ’¬ Create a test5 Txt, use fwrite to write a data to text5 Txt:

#include <stdio.h>
// Written in binary form

struct S {
    char arr[10];
    int num;
    float score;
};

int main(void) {
    struct S s = { "abcde", 10, 5.5f };

    FILE* pf = fopen("test5.txt", "w");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    // Write file
    fwrite(&s, sizeof(struct S), 1, pf);

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

🚩 (code runs successfully)

Β 

We found him burning 2333 (crossed out)

πŸ’‘ After opening the file, we found that only abcde could understand it, but we couldn't understand what was behind it.

We try to open it with nodepad + +

❓ Why is it still garbled? Why is abcde not garbled?

πŸ”‘ answer:

β‘  We just used the text compiler. The text compiler opens binary files in two states.

β‘‘ Because writing strings in text form is the same as writing strings in binary form, but it is different for integers and floating-point numbers. Writing strings in text form and binary form are completely two concepts.

(so how to read it, let's look at the fread function below)

0x08 binary input function fread

πŸ“š Introduction: read from the stream. Read the data from the given stream to the array pointed to by the buffer.

πŸ’¬ Read text5 with fread Binary data in txt:

#include <stdio.h>
// Binary form reading

struct S {
    char arr[10];
    int num;
    float score;
};

int main(void) {
    struct S s = { 0 }; // Storage

    FILE* pf = fopen("test5.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    // read file
    fread(&s, sizeof(struct S), 1, pf);

    // Print the read data
    printf("%s %d %f", s.arr, s.num, s.score);

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

🚩 (code works normally)

Β 

πŸ”Ί Summary: fwrite and fread are a pair. Fwrite is written in and read with fread.

0x09 concept of stream (stream)

Here, we add the concept of obscenity.

πŸ“š Looking at the table just now, we can find that some functions are applicable to all xx streams (such as fputc function). Fputc is applicable to all output streams, that is, it can not only write to files. Let's read the introduction of MSDN:

We found that it can also be written to stdout.

❓ So what is stdout?

πŸ’‘ stdout is the standard output stream. Here, let's talk about the concept of stream.

πŸ“š Three streams are opened by default in C language:

β‘  stdin - standard input stream - Keyboard
β‘‘ stdout - standard output stream - screen
β‘’ stderr - standard output stream - screen

πŸ’¬ We use the flow direction to output information on the screen - stdout:

#include <stdio.h>

int main(void) {
    fputc('a', stdout);
    fputc('b', stdout);
    fputc('c', stdout);

    return 0;
}

🚩  a b c

  πŸ’¬ fgetc reads - stdin from the standard input stream

#include <stdio.h>
// Use fgetc to read from the standard input stream
int main(void) {
    int ret = fgetc(stdin);
    printf("%c\n", ret);
    ret = fgetc(stdin);
    printf("%c\n", ret);
    ret = fgetc(stdin);
    printf("%c\n", ret);
    
    return 0;
}

🚩 function:

Chapter 16 - document operation (Part 2)

preface:

In the file operation (Part 1), we talked about the opening and closing of files, the sequential reading and writing of files, and a simple explanation of stream. This chapter will explain the random reading and writing of files, text files and binary files, the determination of the end of file reading, and file buffer.

πŸšͺ Portal: File operation (Part 1)

1, Random reading and writing of files

0x00 file pointer positioning function fseek

πŸ“š Introduction: locate the pointer according to the position and offset of the file pointer.

one ️⃣ Parameter: offset is the offset.

two ️⃣ Parameter: origin is the starting position. There are three options:

      β‘  SEEK_CUR = offset from the position of the current file pointer.

      β‘‘ SEEK_ The end} file is offset from the end position.

      β‘’ SEEK_ The start position of the set} file is offset.

πŸ’¬ Code demonstration: manually create a file, open the file and write some content

#include <stdio.h>

int main(void) {
    FILE* pf = fopen("test6.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    // read file
    int ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

❓ What should I do if I want a and B?

πŸ’‘ We can try to use the fseek function:

#include <stdio.h>

int main(void) {
    FILE* pf = fopen("test6.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    // read file
    int ch = fgetc(pf);
    printf("%c\n", ch);

    // Adjust file pointer
    fseek(pf, -1, SEEK_CUR); // SEEK_CUR is the current file pointer position, the offset is - 1, and moves forward 1 unit
    ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

πŸ’¬ Use SEEK_SET, print a d e:

#include <stdio.h>

int main(void) {
    FILE* pf = fopen("test6.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    // read file
    int ch = fgetc(pf);
    printf("%c\n", ch);

    // Adjust file pointer
    fseek(pf, 3, SEEK_CUR); // SEEK_SET is the starting position of the file, the offset is 3, and moves backward by 3 units
    ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

πŸ’¬ Use SEEK_END, print a e f:

#include <stdio.h>

int main(void) {
    FILE* pf = fopen("test6.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    // read file
    int ch = fgetc(pf);
    printf("%c\n", ch);

    // Adjust file pointer
    fseek(pf, -2, SEEK_END); // SEEK_END is the end position of the current file, with an offset of - 2, moving forward by 2 units
    ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

0x01 return offset function ftell

πŸ“š Description: returns the offset of the file pointer relative to the starting position.

πŸ’¬ Code demonstration: how to use ftell

#include <stdio.h>

int main(void) {
    FILE* pf = fopen("test6.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    // Adjust file pointer
    fseek(pf, 5, SEEK_CUR); // SEEK_CUR is the pointer position of the current file, with an offset of 5 and a backward movement of 5 units

    // read file
    int ch = fgetc(pf);
    printf("%c\n", ch); // f

    // Return offset
    int ret = ftell(pf);
    printf("%d\n", ret); // 6

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

🚩 The operation results are as follows:

0x02 file pointer returns to starting position function rewind

  πŸ“š Description: rewind (meaning rewind, tape rewind), set the file position to the beginning of the file of the given stream, and return the file pointer to the starting position.

πŸ’¬ Code demonstration: use the rewind function to return the file pointer to the starting position

#include <stdio.h>

int main(void) {
    FILE* pf = fopen("test6.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    // Adjust file pointer
    fseek(pf, 5, SEEK_CUR); // SEEK_CUR is the pointer position of the current file, with an offset of 5 and a backward movement of 5 units

    // Return offset
    int loc = ftell(pf);
    printf("fseek After adjusting the file pointer:%d\n", loc); // 6

    // Returns the file pointer to the starting position
    rewind(pf);

    // Return the offset again to see if it is back to the starting position
    loc = ftell(pf);
    printf("use rewind After:%d\n", loc); // 6

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

  🚩 The operation results are as follows:

2, Text and binary files

0x00 import

In the last section, we have started with text files and binary files. Here we will discuss them in detail!

  πŸ“š According to the organization form of data, data files are called text files or binary files.

0x01 text file

❓ What is a text file?

πŸ“š If it is required to store in ASCII code on external memory, it needs to be converted before storage. A file stored in the form of ASCII characters is a text file.

0x02 binary file

❓ What are binaries?

πŸ“š Data is stored in binary form in memory. If it is output to external memory without conversion, it is a binary file.

0x03 storage mode of data in file

❓ How is a data stored in a file?

πŸ“š The storage method is as follows:

β‘  all characters are stored in ASCII.

β‘‘ numerical data can be stored in ASCII or binary form.

🌰 Take a simple example: for example, if the integer 10000 is output to the disk in the form of ASCII code, the disk will occupy 5 bytes (1 byte for each character). If it is output in binary form, it takes only 4 bytes on the disk.

πŸ’¬ Test code:

#include <stdio.h>

int main(void) {
    int a = 10000;
    FILE* pf = fopen("test6.txt", "wb");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    // Write file
    fwrite(&a, sizeof(int), 1, pf); // Write to file in binary form

    // Close file
    fclose(pf);
    pf = NULL;

    return 0;
}

🚩 (code runs successfully)

πŸ’‘ Let's use the very powerful [universe first editor] Visual Studio (2013 version) to open our binary file TEST6 Txt, the detailed steps are as follows:

πŸ”‘ Finally, let's check whether 10000 is 10 27 00:

πŸ”Ί Summary: how text files and binary files are stored

β‘  text file: convert the data in memory into ASCII code value and store it in the file.

β‘‘ binary file: store the binary data in the memory directly into the binary file without any conversion.

3, Determination of end of file reading

When the reading of documents is over is a problem worthy of our discussion.

0x00 frequently misused "feof function"

πŸ“š Introduction: at the end of a file, it is a function to judge what causes the file to end, and whether it ends because of reading failure or because of encountering the end of the file. If the file ends, a non-zero value is returned, otherwise 0 is returned.

❌ Wrong use: feof function is a function that is often used incorrectly. In the process of file reading, you cannot directly judge whether the file ends with the return value of feof function! Feof function is definitely not a function to judge whether the file ends! Feof is not used to determine whether the file has ended, or to determine what causes the file to end when the file has ended.

βœ… Correct usage: when the file reading ends, judge whether it ends because of the reading failure or because of the end of the file.

πŸ’¬ Code demonstration: feof usage:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int ch = 0; // Note: it is int type instead of char, and EOF processing is required
    FILE* pf = fopen("test.txt", "r");
    if (!pf) {  // pf == NULL
        perror("fopen");
        return EXIT_FAILURE; // Symbolic Constant EXIT_FAILURE indicates that a program was not successfully executed
    }
    // fgetc - EOF will be returned when reading fails or when the file ends
    while ( (ch = fgetc(pf)) != EOF ) {
        putchar(ch);
    } printf("\n");

    // Determine the reason for the end of the file
    if (ferror(pf)) {  // ferror - check for errors.
        puts("Read failure error( I/O error when reading)");
    } else if (feof(pf)) {
        puts("End of file encountered( End of file reached successfully) ");
    }

    // File close
    fclose(pf);
    pf = NULL;
}

🚩 The operation results are as follows:

0x01 method to correctly determine whether the file reading is over

πŸ“š Whether the reading of the text file ends, and judge whether the return value is EOF (fgetc) or NULL (fgets), for example:

β‘  The fgetc function will return EOF at the end of reading. During normal reading, it will return the ASCII code value of the read character.

β‘‘ The fgets function returns NULL at the end of reading. In normal reading, it returns the starting address of the space where the string is stored.

β‘’ At the end of reading, the fread function will return the number of complete elements actually read. If it is found that the number of complete elements read is less than the specified number of elements, it is the last reading.

πŸ’¬ Code demonstration: manually create a file under the project path Txt file, just write a few lines in it. Then, the file Txt file to generate File2 txt :

#include <stdio.h>

int main(void) {
    FILE* pfRead = fopen("file.txt", "r");
    if (pfRead == NULL) {
        return 1;
    }

    FILE* pfWrite = fopen("file2.txt", "w");
    if (pfWrite == NULL) {
        fclose(pfRead);
        pfRead = NULL;
        return 1;
    }

    // The file is opened successfully. Read and write the file
    int ch = 0;
    // read file
    ch = fgetc(pfRead);
    while ( (ch = fgetc(pfRead)) != EOF ) {
        // Write file
        fputc(ch, pfWrite);
    }

    // Close file
    fclose(pfRead);
    pfRead = NULL;
    fclose(pfWrite);
    pfWrite = NULL;

    return 0;
}

  🚩 (code runs successfully)

πŸ’¬ Code demonstration: example of Binary Reading

#inlucde <stdio.h>

enum {
    SIZE = 5
};

int main(void)
{
    double a[SIZE] = {1.,2.,3.,4.,5.};

    FILE *fp = fopen("test.bin", "wb"); // Binary mode must be used
    fwrite(a, sizeof *a, SIZE, fp); // Write an array of double
    fclose(fp);

    double b[SIZE];
    fp = fopen("test.bin","rb");
    size_t ret_code = fread(b, sizeof *b, SIZE, fp); // Read array of double
    if (ret_code == SIZE) {
        puts("The array was read successfully. The contents of the array are as follows:");
        for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
        putchar('\n');
    } else { // exception handling
        if (feof(fp)) {  
            printf("test.bin Read error! unexpected end of file\n");
        } else if (ferror(fp)) {
           perror("test.bin Read error!");
       }
    }
    fclose(fp);
}

  🚩 The operation results are as follows:

4, File Buffer

0x00 what is a file buffer

[Baidu Encyclopedia] a file refers to a set of relevant information stored on an external storage medium and identified by the file name. Due to speed mismatch between CPU and I/O device. In order to alleviate the speed mismatch between CPU and I/O device. File buffer is a certain space reserved in memory area for temporarily storing file data during reading and writing. Using a file buffer reduces the number of reads to the hard disk.

πŸ“š Introduction: ANSIC standard uses buffer file system to process data files. The so-called buffer file system refers to that the system automatically opens up a file buffer for each file in use in the program in memory. The rules are as follows:

β‘  If data is output from memory to disk, it will be sent to the buffer in memory first. After the buffer is full, it will be sent to disk together.

β‘‘ If you read data from the disk to the computer, read the data from the disk file, input it into the memory buffer (fill the buffer), and then send the data from the buffer to the program data area (program variables, etc.) one by one.

πŸ“Œ Note: the size of the buffer is determined by the C compilation system.

0x01 flush buffer function fflush

  πŸ“š Description: force the data in the buffer to be written back to the file specified by the parameter stream. 0 is returned if the refresh is successful, EOF is returned if an error occurs, and the error identifier, feof, is set.

πŸ“Œ Note: fflush is not applicable to higher version VS

0x02 feel the existence of file buffer

  πŸ’¬ Observation Code: verify the existence of buffer concept (VS2013 - Win10)

#include <stdio.h>
#include <windows.h>

int main(void) {
	FILE* pf = fopen("test7.txt", "w");
	fputs("abcdef", pf);//Put the code in the output buffer first
	printf("Sleep for 10 seconds-The data has been written. Open it test.txt File, no content found in the file\n");
	Sleep(10000);
	printf("refresh buffer \n");
	fflush(pf);//When the buffer is refreshed, the data of the output buffer is written to the file (disk)
	//Note: fflush cannot be used on higher version VS
	printf("Sleep for another 10 seconds-At this point, open again test.txt File, the file has content\n");
	Sleep(10000);
	fclose(pf);
	//Note: fclose also flushes the buffer when closing the file
	pf = NULL;

	return 0;
}

  🚩 The operation results are as follows:

πŸ”Ί Conclusion: because of the existence of buffer, C language needs to refresh the buffer or close the file at the end of file operation. If not, it may cause problems in reading and writing files.

Chapter 17 - C language preprocessing (I)

preface:

This chapter will explain the pretreatment knowledge. First, the predefined symbols are introduced, and then the preprocessing instructions are emphasized. The preprocessing operator is introduced, and finally the macro and function are compared.

1, Pretreatment

0x00 what is preprocessing

[Baidu Encyclopedia] in the field of programming, preprocessing generally refers to the process before generating binary code in the process of translating program source code into object code. Typically, the program source code text is processed by the preprocessor, and the results are further compiled by the compiler core. This process does not parse the source code of the program, but it divides or processes the source code into specific units - preprocessing tokens (in C/C + + terms) are used to support language features (such as macro calls in C/C + +).

0x01 predefined symbols

πŸ“š Introduction: the defined symbols processed in the preprocessing stage are predefined symbols. These symbols can be used directly and have been built in C language.

πŸ“Œ Note: it is worth noting that__ Underline for two!

πŸ’¬ Code demonstration:

#include <stdio.h>

int main(void) {
	printf("%s\n", __FILE__);     // Returns the name of the source file where the line of code is used, including the path
	printf("%d\n", __LINE__);     // Return line number
	printf("%s\n", __DATE__);     // Returns the date the program was compiled
	printf("%s\n", __TIME__);     // Returns the time the program was compiled
	printf("%s\n", __FUNCTION__); // Returns the function name of the function

	return 0;
}

🚩 The operation results are as follows:

❓ What is the use of these predefined symbols?

πŸ’‘ If a project is particularly complex, there may be no way to start debugging at this time. Therefore, the code needs to record some log information during operation. If there is a problem with the log information analysis program, then troubleshooting is like catching a turtle in a jar.

πŸ’¬ for instance:

#include <stdio.h>

int main(void) {
    int i = 0;
    FILE* pf = fopen("log.txt", "a+"); //The form of append, which is appended every time it runs
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    for (i = 0; i < 5; i++) {
        printf("* Error log ");
        printf("%d *\n", i+1);
        printf("Time of occurrence:%s  %s\n", __DATE__, __TIME__);
        printf("Specific location:%s,The function name is%s,The first%d that 's ok.\n", __FILE__, __FUNCTION__, __LINE__);
        printf("\n");
    }
    fclose(pf); 
    pf = NULL;

    return 0;
}

🚩 (test, run the code three times)

❗ About__ STDC__ , Returns 1 if the compiler fully complies with the ANSI C standard, otherwise undefined.

2, #define

0x00 #define identifier

πŸ’¬ Code demonstration: #define method for defining identifier

#include <stdio.h>

#define TIMES 100

int main(void) {
    int t = TIMES;
    printf("%d\n", t);

    return 0;
}

🚩 Operation result: 100

πŸ”‘ Parsing: in the preprocessing phase, TIMES will be replaced with 100. After preprocessing, int t = TIMES, there will be no TIMES here, and it will become int t = 1000.

// Before pretreatment
int t = TIMES;
// After pretreatment
int t = 1000;

πŸ“Œ Of course, #define defines symbols that can be used not only for numbers, but also for many things, such as:

#define REG register / / give the keyword register a short name
#define DEAD_LOOP for(;;)   // Replace an implementation with a more visual symbol

β‘  #define REG register, create a short name for the keyword register:

#define REG register

int main(void) {
    register int num = 0;
    REG int num = 0; // Here REG is equal to register

    return 0;
}

 β‘‘ #define DEAD_LOOP for(;;) , replace an implementation with a more vivid symbol:

#define DEAD_LOOP for(;;)

int main(void) {
    DEAD_LOOP // After preprocessing, replace with for(;); 
        ; // The body of the loop loops through an empty statement

    DEAD_LOOP; // Well, you can write it this way. The semicolon is the loop body, and the loop is an empty statement

    return 0;
}

β‘’ suppose that break should be added after the switch statement in a program, but someone didn't write C language before. He didn't need to add break after the language case he used to use, because he doesn't adapt to adding break after every case, so he always forgets. You can use #define to solve this problem:

#define CASE break;case / / automatically break the word when writing a case statement

int main(void) {
    int n = 0;
	//switch (n) {
	//	case 1:
	//		break;
	//	case 2:
	//		break;
	//	case 3:
	//		break;
	//}

	switch (n) {
		case 1: // The first case cannot be replaced
		CASE 2: // Equivalent to break; case 2:
		CASE 3: // Equivalent to break; case 3:
	}

    return 0;
}

Β 

β‘£ If the defined stuff is too long, it can be written in separate lines. Except for the last line, each line can be followed by a continuation character \:

#include <stdio.h>

#define DEBUG_PRINT printf("file:%s\nline:%d\n \
                        date:%s\ntime:%s\n" , \
                        __FILE__,__LINE__ , \
                        __DATE__,__TIME__ )

int main(void) {
    DEBUG_PRINT;

    return 0;
}

❓ #define when defining an identifier, why is there no semicolon at the end?

#define TIMES 100;
#define TIMES 100

πŸ’¬ For example, after adding a semicolon, the content to be replaced by preprocessing will also have a semicolon of 100;

#include <stdio.h>

#define TIMES 100;
int main(void) {
    int t = TIMES; // int t = 100;;

    // Equal to two statements
    // int t = 100;
    // ;

    return 0;
}

❌ For example, if you add a semicolon, the code will make an error

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

#define TIMES 100;

int main(void) {
    int a, b;
    if (a > 10)
        b = TIMES; // b = 100;;
    else // else doesn't know how to match
        b = -TIMES; // b = 100;;

    return 0;
}

πŸ”Ί Conclusion: when #define an identifier, try not to add a semicolon at the end! (except where necessary)

0x01 #define define macro

πŸ“š Introduction: #define mechanism allows parameters to be replaced into text. This implementation is usually called macro or define macro. Parameter list is a symbol table separated by commas. They may appear in stuff.

πŸ“Œ matters needing attention:

β‘  the left parenthesis of the parameter list must be immediately adjacent to name.

β‘‘ if there is any blank space between them, the parameter list will interpret it as part of the stuff.

πŸ’¬ Code demonstration: 3 × 3=9

#include <stdio.h>

#define SQUARE(X) X*X

int main(void) {
    printf("%d\n", SQUARE(3)); // printf("%d\n", 3 * 3);

    return 0;
}

❓ What is the result of SQUARE (3+1)?

#include <stdio.h>

#define SQUARE(X) X*X

int main(void) {
    printf("%d\n", SQUARE(3+1));

    return 0;
}

πŸ’‘ Answer: 7. Here, replace 3 + 1 with X, then x is 3 + 1, 3 + 1 * 3 + 1, and the result is {7 according to the priority. It depends on the complete replacement as a whole. Macro parameters are replaced. Instead of completing the calculation in advance, they are replaced before calculation. Replacement is to replace in the preprocessing stage, and the real calculation result of the expression is calculated at run time.

πŸ’¬ If you want to multiply 3 + 1 (that is, get 4) × 4 = 16), we need to add parentheses to them:

#include <stdio.h>

// The whole is enclosed in a bracket, which is rigorous
#define SQUARE(X) ((X)*(X))

int main(void) {
    printf("%d\n", SQUARE(3+1));

    return 0;
}

πŸ’¬ In addition, set another bracket as a whole! Make the code more rigorous and prevent unnecessary errors. For example, DOUBLE can add two numbers. I want to get 10* DOUBLE, that is, the case of "10 * expression addition":

#include <stdio.h>

#define DOUBLE(X) (X)+(X)

int main(void) {
    printf("%d\n", 10 * DOUBLE(3+1));
    // printf("%d\n", 10 * (4) + (4)); 
    // We meant to get 80, but the result was 44 because there were no parentheses in the whole

    return 0;
}

🚩 Operation result: 44 (not the expected result)

πŸ”‘ Solution: add a bracket to the whole!

#define DOUBLE(X) ((X)+(X))

int main(void) {
    printf("%d\n", 10 * DOUBLE(3+1));

    return 0;
}

🚩 Operation result: 80 (achieve the expected result)

πŸ”Ί Conclusion: therefore, macro definitions used to evaluate numerical expressions should be bracketed in this way, which can effectively avoid the unpredictable interaction between operators in parameters or adjacent operators when using macros.

0x02 #define replacement rule

πŸ“š When extending #define definition symbols or macros in the program, the steps involved are as follows:

one ️⃣ Check: when calling a macro, first check the parameters to see if they contain any symbols defined by #define. If so, they are replaced first.

two ️⃣ Replace: the replacement text is then inserted into the original text position in the program. For macros, function names are replaced by their values.

three ️⃣ Scan again: finally, scan the result file again to see if it contains any symbols defined by #define. If yes, repeat the above process.

πŸ’¬ for instance:

#include <stdio.h>

#define M 100
#define MAX(X, Y) ((X)>(Y) ? (X):(Y));

int main(void) {
    int max = MAX(101, M);

    return 0;
}

  πŸ“Œ matters needing attention:

β‘  variables defined by #define can appear in macro parameters and #define definitions. But there must be no recursion for macros!

β‘‘ when the preprocessor searches #define defined symbols, the contents of string constants are not searched.

0x03 # and##

❓ As we know, macros replace parameters with text. So how do I insert parameters into a string?

❌ For example, in this case, it is impossible to use functions:

void print(int x) {
    printf("Variables? The value of is%d\n", ?) Function can't do it at all
}

int main(void) {
    int a = 10;
    // Print content: the value of variable a is 10
    print(a);

    int b = 20;
    // Print content: the value of variable b is 20
    print(b);

    int c = 30;
    // Print content: the value of variable c is 30
    print(c);

    return 0;
}

πŸ’‘ In this case, you can use macros.

πŸ“š Introduction: # change a macro parameter into a corresponding string.

πŸ’¬ Use # to solve the above problems:

#include <stdio.h>
#define PRINT(X) printf("variable"#The value of X "is% d\n", X);
// #X becomes the string defined by the X content

int main(void) {
    // Print content: the value of variable a is 10
    int a = 10;
    PRINT(a); // printf("the value of variable" "a" "is% d\n", a);

    // Print content: the value of variable b is 20
    int b = 20;
    PRINT(b); // printf("the value of variable" B "is% d\n", b);

    // Print content: the value of variable c is 30
    int c = 30;
    PRINT(c); // printf("the value of variable" "C" "is% d\n", c);

    return 0;
}

  🚩 The operation results are as follows:

  ⚑ Improvement: let the program not only support printing integers, but also print other types of numbers (such as floating-point numbers):

#include <stdio.h>
#define PRINT(X, FORMAT) printf("variable"#The value of "X" is "FORMAT"\n", X);

int main(void) {
    // Print content: the value of variable a is 10
    int a = 10;
    PRINT(a, "%d");

    // Print content: the value of variable f is 5.5
    float f = 5.5f;
    PRINT(f, "%.1f"); //printf("the value of variable" "F" "is" "%. 1f""\n", f);

    return 0;
}

Β 

✨ Isn't this operation wonderful? What's more, this is a heavyweight:

  πŸ“š Introduction: ## you can fuse the symbols on both sides of it into one symbol. It allows macro definitions to create identifiers from separate text fragments.

πŸ’¬ Use ## to stitch the symbols on both sides into one symbol:

#include <stdio.h>

#define CAT(X,Y) X##Y

int main(void) {
    int vs2003 = 100;

    printf("%d\n", CAT(vs, 2003)); // printf("%d\n", vs2003);

    return 0;
}

🚩 The operation results are as follows:

  πŸ“Œ Note: ## you can also combine multiple symbols into one symbol, such as X##Y##Z

0x04 #undef

πŸ“š Used to remove a macro definition.

πŸ’¬ Code demonstration: remove the definition after using M

#include <stdio.h>

#define M 100

int main(void) {
    int a = M;
    printf("%d\n", M);
#undef M / / remove macro definition

    return 0;
}

0x05 macro parameter with side effect

❓ What are side effects?

πŸ’‘ The sequelae is the permanent effect that occurs when the expression is evaluated, for example:

// No side effects
x + 1;
// With side effects
x++;  

int a = 1;
// No side effects
int b = a + 1; // b=2, a=1
// With side effects
int b = ++a; // b=2, a=2

πŸ“š Introduction: when the macro parameter appears more than once in the macro definition, if the parameter has side effects (sequelae), you may be in danger when using this macro, resulting in unpredictable consequences. If this macro parameter with side effects is transferred to the macro body, this side effect will continue to the macro body.

πŸ’¬ for instance:

#include <stdio.h>

#define MAX(X,Y) ((X)>(Y)?(X):(Y))

int main(void) {
    int a = 5;
    int b = 8;
    int m = MAX(a++, b++);

    printf("m = %d\n", m);
    printf("a=%d, b=%d\n", a, b);

    return 0;
}

🚩 The operation results are as follows:

πŸ”Ί Conclusion: try to avoid using this parameter with side effects when writing macros.

0x06 macro and function comparison

πŸ’¬ For example: find the larger value in two numbers

β‘  Macro:

#include <stdio.h>

#define MAX(X,Y) ((X)>(Y)?(X):(Y))

int main(void) {
    int a = 10;
    int b = 20;
    int m = MAX(a, b); // int m = ((a)>(b) ? (a):(b))
    printf("%d\n", m);
    
    return 0;
}

β‘‘ Use function:

#include <stdio.h>

int Max(int x, int y) {
    return x > y ? x : y;
}

int main(void) {
    int a = 10;
    int b = 20;
    int m = Max(a, b);
    printf("%d\n", m);

    return 0;
}

❓ So the question is, which is better, macros and functions?

πŸ’‘ The code used to call and return from functions may take more time than actually performing this small calculation, so macros are better than functions in terms of program size and speed. More importantly, the parameters of a function must be declared as a specific type. Therefore, functions can only be used on expressions of the appropriate type. Conversely, macros can be applied to integer, long integer, floating point and other types that can be used for comparison. Because macros are type independent.

πŸ“š Of course, macros also have disadvantages:

β‘  Each time you use a macro, a copy of the macro definition code will be inserted into the program. Unless the macro is short, it may greatly increase the length of the program.

β‘‘ Macros cannot be debugged.

β‘’ Macros are not rigorous enough because they are type independent and there is no type check.

β‘£ Macros may cause operator priority problems, resulting in error prone procedures.

πŸ’¬ Macros can sometimes do things that functions can't. For example, macro parameters can have types, but the function cannot:

#include <stdio.h>
#include <stdlib.h>

#define MALLOC(num, type) (type*)malloc(num*sizeof(type))

int main(void) {
    // Original writing: malloc(10*sizeof(int));
    // But I want to write this: malloc(10, int);

    int* p = MALLOC(10, int); // (int*)malloc(10*sizeof(int))

    ...
    
    return 0;    
}

πŸ“Œ Comparison table of macros and functions:

attribute#define macrofunction
Code lengthMacro code is inserted into the program every time it is used. Except for very small macros, the length of the program will increase significantlyFunction code only appears in one place; Every time you use this function, you call the same code in that place

Execution speed

FasterThere is extra overhead for function calls and returns, so it is relatively slow
Operator precedence The evaluation of macro parameters is in the context of all surrounding expressions. Unless parentheses are added, the priority of adjacent operators may have unpredictable consequences. Therefore, it is recommended that macros be written with more parenthesesThe function parameter is evaluated only once when the function is called, and its result value is passed to the function. The evaluation result of the expression is easier to predict.
Parameters with side effectsParameters can be replaced at multiple locations in the macro body, so parameter evaluation with side effects can produce unpredictable resultsFunction parameters are evaluated only once when passing parameters, and the results are easier to control.
Parameter typeThe parameters of a macro are independent of the type. As long as the operation on the parameters is legal, it can be used for any parameter typeThe parameters of a function are type dependent. If the types of parameters are different, different functions are required, even if they perform different tasks
recursionMacros cannot be recursiveFunctions are recursive

πŸ”Ί Conclusion: if the logic of an operation is simple enough, it is recommended to use macros. Conversely, if the logic of an operation is complex enough, it is recommended to use functions.

πŸ’­ Brief introduction to inline function (C99):

0x07 naming convention

πŸ“š Generally speaking, the syntax of function macros is very similar, so the language itself can't help us distinguish the two. It is a common practice that macro names should be capitalized and function names should not be capitalized.

However, this is not absolute. For example, sometimes I just want to disguise a macro as a function, so I write a name for the macro. It is not mandatory, but this Convention is a "Convention" for every C/C + + programmer.

Chapter 17 - C language preprocessing (Part 2)

preface:

This article is the second part of C language preprocessing. This article will further explain the basic knowledge of preprocessing and the definition of command line. Explain the conditional compilation statements one by one, and understand the two file inclusion methods.

πŸšͺ Portal: It's easy to call downstairs after reading it! C language preprocessing (I)

1, Command line compilation

❓ What is command line compilation?

πŸ’‘ When compiling, it is defined through the command line, which is called command line compilation.

πŸ“š Introduction: many C compilers provide a capability that allows symbols to be defined on the command line. Used to start the compilation process. This feature can be used to increase flexibility when we need to compile different versions of a program according to the same source file.

πŸ’¬ Example: if a program declares an array of a certain length, if the memory of machine a is limited, we need a small data, but the memory of machine C is large, we need a large array.

#include <stdio.h>

int main() {
    int arr[ARR_SIZE];
    int i = 0;
    for (i = 0; i < ARR_SIZE; i++) {
        arr[i] = i;
    }
    for (i = 0; i < ARR_SIZE; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}

🚩 Testing in gcc environment: (it's not easy to demonstrate in VS)

gcc test.c -D ARR_SIZE=5

ls

a.outΒ  test.c

./a.out

0 1 2 3 4 5

gcc test.c -D ARR_SIZE=20

./a.out

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

2, Conditional compilation

0x00 introduction

πŸ“š When compiling a program, it is convenient to compile or discard a statement (a group of statements) through conditional compilation instructions.

πŸ’¬ The code used for debugging was deleted. Unfortunately, it was retained and hindered. We can use conditional compilation to selectively compile:

#include <stdio.h>

#define __DEBUG__ //  It's like a switch

int main(void)
{
    int arr[10] = {0};
    int i = 0;
    for (i = 0; i < 10; i++) {
        arr[i] = i;
        #ifdef __DEBUG__ //  Because__ DEBUG__ It's defined, so it's true
        printf("%d ", arr[i]); // Just print the array    
        #endif / / end of package
    }

    return 0;
}

🚩 Operation results: 1 2 3 4 5 6 7 8 9 10

❗ If you don't want to use it, put #define__ DEBUG__ Note out:

#include <stdio.h>

// #define __DEBUG__ //  shut

int main(void)
{
    int arr[10] = {0};
    int i = 0;
    for (i = 0; i < 10; i++) {
        arr[i] = i;
        #ifdef __DEBUG__ //  Ifdef is false at this time
        printf("%d ", arr[i]);      
        #endif
    }

    return 0;
}

🚩 (code runs successfully)

0x01 constant expression for conditional compilation

πŸ“š Description: if the constant expression is true, participate in the compilation. On the contrary, if it is false, it will not participate in the compilation.

πŸ’¬ Code demonstration: constant expression is true

#include <stdio.h>

int main(void) {
#if 1
    printf("Hello,World!\n");
#endif

    return 0;
}

  🚩 Run result: Hello,World!

πŸ’¬ Code demonstration: constant expression is false

#include <stdio.h>

int main(void) {
#if 0
    printf("Hello,World!\n");
#endif

    return 0;
}

🚩 (code runs successfully)

πŸ’¬ Of course, it can also be replaced with macros, which can be expressed more clearly:

#include <stdio.h>

#define PRINT 1
#define DONT_PINRT 0

int main(void) {
#if PRINT
    printf("Hello,World!\n");
#endif

    return 0;
}

0x02 conditional compilation of multiple branches

πŸ“š Introduction: multi branch conditional compilation is not executed until the constant expression is true.

πŸ’¬ Code demonstration:

#include <stdio.h>

int main(void) {
#if 1 == 2 / / false
    printf("rose\n");
#elif 2 == 2 / / true
    printf("you jump\n");
#else 
    printf("i jump\n")
#endif

    return 0;
}

🚩 Code run result: you jump

0x03 conditional compilation judge whether it is defined

πŸ“š Definition: ifdef and if defined(), ifndef and if! The defined () effect is the same to determine whether it is defined.

πŸ’¬ Code demonstration:

#include <stdio.h>

#define TEST 0
// #define TEST2 / / undefined

int main(void) {
/* If TEST is defined, the following steps are taken to compile */
// 1
#ifdef TEST
    printf("1\n");
#endif

// 2
#if defined(TEST)
    printf("2\n");
#endif


/* If TEST2 is not defined, the following steps will be taken to compile */
// 1
#ifndef TEST2
    printf("3\n");
#endif

// 2
#if !defined(TEST2)
    printf("4\n");
#endif

    return 0;
}

0x04 nesting of conditional compilation

πŸ“š Like if statements, they can be nested:

#if defined(OS_UNIX)
    #ifdef OPTION1
        unix_version_option1();
    #endif
    #ifdef OPTION2
        unix_version_option2();
    #endif
#elif defined(OS_MSDOS)
    #ifdef OPTION2
        msdos_version_option2();
    #endif
#endif

3, File contains

We already know that the #include directive enables another file to be compiled. Just as it actually appears in the #include directive. The replacement method is that the preprocessor first deletes this instruction and replaces it with the contents of the included file. If such a source file is included 10 times, it will actually be compiled 10 times.

0x00 how header files are included

πŸ“š < > and "" contain the essential difference between header files: the difference between search strategies

β‘  "Search strategy for" "" ": first search in the directory where the source file is located.". If the header file is not found, find it in the header file directory of the library function. (if it is still not found, a compilation error will be prompted)

Path to the Linux environment standard header file:

/usr/include

Path of VS environment standard header file:

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include

β‘‘ < > search strategy: go directly to the standard path. (if it is still not found, a compilation error will be prompted)

❓ In that case, can I also use "" to include library files?

πŸ’‘ Certainly. However, the efficiency of searching is lower. Of course, it is not easy to distinguish whether it is a library file or a local file. This is not recommended for efficiency.

πŸ’¬ Code demonstration:

β‘ Β  add.h

int Add(int x, int y);

β‘‘Β  add.c

int Add(int x, int y) {
	return x + y;
}

β‘’Β  test.c

#include <stdio.h>
#include "add.h"

int main(void) {
	int a = 10;
	int b = 20;
	int ret = Add(a, b);
	printf("%d\n", ret);

	return 0;
}

🚩 Operation result: 30

0x01 inclusion of nested files

❗ Repeated import of header files:

comm.h and comm.c are common modules.
test1.h and test1 C uses a common module.
test2.h and test2 C uses a common module.
test.h and test C uses test1 module and test2 module.
In this way, two copies of comm.h will appear in the final program, resulting in the duplication of file contents.

❓ So how to avoid the repeated introduction of header files?

πŸ’‘ Using the conditional compilation instruction, write at the beginning of each header file:

#ifndef __TEST_H__
#define __TEST_H__
// Contents of header file
#endif

⚑ If it's too much trouble, there's a very simple way:

#pragma once / / make the header file contain only one even if it is contained multiple times

πŸ’­ Written test questions: selected from high quality C/C + + Programming Guide

β‘  What is the ifnde / define / endif in the header file for?

A: prevent header files from being included repeatedly.

β‘‘ #include <filename. h> And #include "filename.h"?

A: angle brackets contain header files in the library, and double quotes contain custom header files. They are different in search strategies. Angle brackets are directly searched in the library directory. The alarm double quotation mark is to search under the customized code path. If the header file cannot be found, search under the header file directory of the library function.

Chapter 18 - C language programming environment

I have some good health here! Jackie, no ~ [analysis of C language program environment]

preface

What is the application environment? We have all "experienced", but we have never felt the existence of "he". In fact, we have already come into contact with the program environment unconsciously... When we created a file (test.c) for the first time, knocked down the sentence "hello world", then saved it and clicked run to compile the executable file (test.exe), we actually came into contact with "him".

We just pressed run, and then everything came into being as if by magic. All this seems to be taken for granted. But have you ever thought about how it became an "executable program"? In this chapter, we will briefly discuss how a "source program" becomes an "executable program" for a general understanding.

1, Translation environment and execution environment

0x00 ANSI C standard

ANSI C is a standard on C language launched by American National Standards Institute (ANSI) and international standards organization (ISO). ANSI C mainly standardizes the existing implementation, adds some content from C + + (mainly function prototypes) and supports multi-national character sets (including controversial three character sequences).

πŸ“š ANSI C is supported by almost all widely used compilers, and most C code is written on the basis of ANSI C.

πŸ” [Baidu Encyclopedia] ANCI C standard

0x01 program translation environment and execution environment

πŸ“š In any implementation of ANSI C, there are two different environments:

β‘  translation environment: in this environment, the source code is converted into executable machine instructions.

β‘‘ execution environment: used to actually execute code.

2, Detailed compilation and links

0x00 translation environment

  πŸ“š Each source file constituting a program is converted into object code through the compilation process

Each object file is bound together by a linker to form a single and complete executable program.

At the same time, the connector will also introduce any function used by the program in the standard C library function, and you can search the programmer's personal library and link the required functions to the program.

πŸ’¬ For example: test c,add.c,minu.c

0x01 several stages of compilation itself

πŸ’¬ for instance:

β‘  sum.c

int global_val = 2021;
void print(const char* string) {
    printf("%s\n", string);
}

β‘‘ test.c

#include <stdio.h>

int main(void) {
    extern void print(char* string);
    extern int global_val;
    printf("%d\n", global_val);
    printf("Hello,World!\n");

    return 0;
}

test.c

sum.c

Precompiled truncation (*. i)

preprocessor directives

......

Compile (*. s)

Syntax analysis

lexical analysis

semantic analysis

Symbol summary

Assembly (generate relocatable object files *. O)

Form symbol table

Assembly instruction → binary instruction ---- → test o

Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β  Β Β  Β Β  Β  ----β†’ sum.o

link

1. Consolidated segment table

2. Symbol table merging and symbol table relocation

Isolate compilation and link together.

πŸ“š main.c

extern int sum(int, int);

int main(void) {
    sum(1, 2);
    return 0;
}

πŸ“š sum.c

int sum(int num1, int num2) {
    return( num1 + num2);
}

πŸ”‘ Analysis diagram (VS2019):

0x02 operating environment

πŸ“š Program execution process:

β‘  The program must be loaded into memory. In the environment with operating system: the loading of programs is generally completed by the operating system. In an independent environment: the loading of the program must be arranged manually, or it may be completed by putting the executable code into the read-only memory.

β‘‘ The execution of the program begins. The main function is then called.

β‘’ Start executing the program code. At this time, the program will use a run-time stack, local variables and return addresses of memory functions. Programs can also use static memory to store variables in static memory and keep their values throughout the execution process.

β‘£ Terminate the procedure. Terminate the main function normally (or unexpectedly).

πŸ’¬ For example: the execution process of this code

int Add(int x, int y) {
    return( x + y);
}
int main(void) {
    int a = 10;
    int b = 20;
    int ret = Add(a, b);

    return 0;
}

πŸ“š Here's another concept: function stack frame (for the moment, I'll write an explanation of function stack frame)

[Baidu Encyclopedia] in C language, each stack frame corresponds to an unfinished function. The return address and local variables of the function are saved in the stack frame.

Array written test questions (with answers + detailed explanations)

preface:

This chapter is the practice chapter of the array part, with a total of eight questions. Equipped with answers + detailed drawing analysis.

If you haven't learned array, or want to review it before writing:

πŸšͺ Portal: [vitamin C language] Chapter 4 - array

πŸ“ Example:

πŸ” Topic explanation display:

Array written test questions (answer + detailed explanation)

8 big questions (63 small questions in total), 1 point for each small question, the full score is 63 points

πŸ“š explain:

β‘  It is suggested to take out paper and pen to write the results you think;

β‘‘ It is suggested not to look at the answers first, and then look at the answers for verification after writing;

β‘’ There are hyperlinks in front of some topics corresponding to knowledge points, you can choose to review by yourself;

β‘£ For the wrong question, you can see the analysis part below the answer to the question for in-depth understanding;

β‘€ You can reply to several questions you have done correctly in the comment area;

First question:

1 point for each sub question, full score 7 points

πŸ“Œ review: [vitamin C language] Chapter 5 - operators (0x05 type length of operand sizeof)

πŸ’¬ Predict the running result of the following code (sizeof)

int main()
{
    int a[] = {1, 2, 3, 4}; // One dimensional array

    /* 1 */  printf("%d\n", sizeof(a));
    /* 2 */  printf("%d\n", sizeof(a + 0));
    /* 3 */  printf("%d\n", sizeof(*a));
    /* 4 */  printf("%d\n", sizeof(a + 1));
    /* 5 */  printf("%d\n", sizeof(a[1]));
    /* 6 */  printf("%d\n", sizeof(&a));
    /* 7 */  printf("%d\n", sizeof(*&a));
    /* 8 */  printf("%d\n", sizeof(&a + 1));
    /* 9 */  printf("%d\n", sizeof(&a[0]));
    /* 10 */ printf("%d\n", sizeof(&a[0] + 1));

    return 0;
}

πŸ’‘ answer:

/* 1 */  printf("%d\n", sizeof(a)); // 16
/* 2 */  printf("%d\n", sizeof(a + 0)); // 4/8
/* 3 */  printf("%d\n", sizeof(*a)); // 4
/* 4 */  printf("%d\n", sizeof(a + 1)); // 4/8
/* 5 */  printf("%d\n", sizeof(a[1])); // 4
/* 6 */  printf("%d\n", sizeof(&a)); // 4/8
/* 7 */  printf("%d\n", sizeof(*&a)); // 16
/* 8 */  printf("%d\n", sizeof(&a + 1)); // 4/8
/* 9 */  printf("%d\n", sizeof(&a[0])); // 4/8
/* 10 */ printf("%d\n", sizeof(&a[0] + 1)); // 4/8

πŸ”‘ Resolution:

1️⃣

2️⃣

3️⃣

4️⃣

5️⃣

6️⃣

7️⃣

8️⃣

9️⃣

πŸ”Ÿ

Second question:

πŸ“Œ review: [vitamin C language] Chapter 4 - array (0x02 initialization of one-dimensional array)

1 point for each sub question, full score 7 points

πŸ’¬ Predict the running result of the following code (sizeof)

int main()
{
    
    char arr[] = {'a','b','c','d','e','f'}; // Character array

    /* 1 */  printf("%d\n", sizeof(arr));
    /* 2 */  printf("%d\n", sizeof(arr+0));
    /* 3 */  printf("%d\n", sizeof(*arr));
    /* 4 */  printf("%d\n", sizeof(arr[1]));
    /* 5 */  printf("%d\n", sizeof(&arr));
    /* 6 */  printf("%d\n", sizeof(&arr+1));
    /* 7 */  printf("%d\n", sizeof(&arr[0]+1));

    return 0;
}

πŸ’‘ answer:

/* 1 */  printf("%d\n", sizeof(arr));  // 6
/* 2 */  printf("%d\n", sizeof(arr+0)); // 4/8
/* 3 */  printf("%d\n", sizeof(*arr)); // 1
/* 4 */  printf("%d\n", sizeof(arr[1])); // 1
/* 5 */  printf("%d\n", sizeof(&arr)); // 4/8
/* 6 */  printf("%d\n", sizeof(&arr+1)); // 4/8
/* 7 */  printf("%d\n", sizeof(&arr[0]+1)); // 4/8 

πŸ”‘ Resolution:

1️⃣

2️⃣

3️⃣

4️⃣

5️⃣

6️⃣

7️⃣

Third question:

1 point for each sub question, full score 7 points

πŸ’¬ Predict the running results of the following code (strlen)

int main()
{

    char arr[] = { 'a','b','c','d','e','f' }; // Character array

    /* 1 */  printf("%d\n", strlen(arr));
    /* 2 */  printf("%d\n", strlen(arr + 0));
    /* 3 */  printf("%d\n", strlen(*arr));
    /* 4 */  printf("%d\n", strlen(arr[1]));
    /* 5 */  printf("%d\n", strlen(&arr));
    /* 6 */  printf("%d\n", strlen(&arr + 1));
    /* 7 */  printf("%d\n", strlen(&arr[0] + 1));

    return 0;
}

πŸ’‘ answer:

/* 1 */  printf("%d\n", strlen(arr)); // Random value
/* 2 */  printf("%d\n", strlen(arr + 0)); // Random value
/* 3 */  printf("%d\n", strlen(*arr)); // error
/* 4 */  printf("%d\n", strlen(arr[1])); // error
/* 5 */  printf("%d\n", strlen(&arr)); // Random value
/* 6 */  printf("%d\n", strlen(&arr + 1)); // Random value - 6
/* 7 */  printf("%d\n", strlen(&arr[0] + 1)); // Random value - 1

πŸ”‘ Resolution:

1️⃣

2️⃣

3️⃣

4️⃣

5️⃣

6️⃣

7️⃣

Fourth question:

1 point for each sub question, full score 7 points

πŸ’¬ Predict the running result of the following code (sizeof)

int main()
{
    char arr[] = "abcdef";

    /* 1 */  printf("%d\n", sizeof(arr));
    /* 2 */  printf("%d\n", sizeof(arr+0));
    /* 3 */  printf("%d\n", sizeof(*arr));
    /* 4 */  printf("%d\n", sizeof(arr[1]));
    /* 5 */  printf("%d\n", sizeof(&arr));
    /* 6 */  printf("%d\n", sizeof(&arr+1));
    /* 7 */  printf("%d\n", sizeof(&arr[0]+1));

    return 0;
}

πŸ’‘ answer:

/* 1 */  printf("%d\n", sizeof(arr)); // 7
/* 2 */  printf("%d\n", sizeof(arr+0)); // 4/8
/* 3 */  printf("%d\n", sizeof(*arr)); // 1
/* 4 */  printf("%d\n", sizeof(arr[1]));// 1
/* 5 */  printf("%d\n", sizeof(&arr)); // 4/8
/* 6 */  printf("%d\n", sizeof(&arr+1)); // 4/8
/* 7 */  printf("%d\n", sizeof(&arr[0]+1)); // 4/8

πŸ”‘ Resolution:

1️⃣

2️⃣

3️⃣

4️⃣

5️⃣

6️⃣

7️⃣

Fifth question:

1 point for each sub question, full score 7 points

πŸ’¬ Predict the running results of the following code (strlen)

int main()
{
    char arr[] = "abcdef";

    /* 1 */  printf("%d\n", strlen(arr));
    /* 2 */  printf("%d\n", strlen(arr + 0));
    /* 3 */  printf("%d\n", strlen(*arr));
    /* 4 */  printf("%d\n", strlen(arr[1]));
    /* 5 */  printf("%d\n", strlen(&arr));
    /* 6 */  printf("%d\n", strlen(&arr + 1));
    /* 7 */  printf("%d\n", strlen(&arr[0] + 1));

    return 0;
}

πŸ’‘ answer:

/* 1 */  printf("%d\n", strlen(arr)); // 6
/* 2 */  printf("%d\n", strlen(arr+0)); // 6
/* 3 */  printf("%d\n", strlen(*arr)); // error
/* 4 */  printf("%d\n", strlen(arr[1])); // error
/* 5 */  printf("%d\n", strlen(&arr)); // 6
/* 6 */  printf("%d\n", strlen(&arr+1)); // Random value
/* 7 */  printf("%d\n", strlen(&arr[0]+1)); // 5

πŸ”‘ Resolution:

1️⃣

2️⃣

3️⃣

4️⃣

5️⃣

6️⃣

7️⃣

Sixth question:

1 point for each sub question, full score 7 points

πŸ“Œ review: [vitamin C language] Chapter 10 - Advanced pointer (Part 1) (definition of 0x00 character pointer)

πŸ’¬ Predict the running result of the following code (sizeof)

int main()
{
    char* p = "abcdef";

    /* 1 */  printf("%d\n", sizeof(p));
    /* 2 */  printf("%d\n", sizeof(p + 1));
    /* 3 */  printf("%d\n", sizeof(*p));
    /* 4 */  printf("%d\n", sizeof(p[0]));
    /* 5 */  printf("%d\n", sizeof(&p));
    /* 6 */  printf("%d\n", sizeof(&p + 1));
    /* 7 */  printf("%d\n", sizeof(&p[0] + 1));

    return 0;
}

πŸ’‘ answer:

/* 1 */  printf("%d\n", sizeof(p)); // 4/8
/* 2 */  printf("%d\n", sizeof(p+1)); // 4/8
/* 3 */  printf("%d\n", sizeof(*p)); // 1
/* 4 */  printf("%d\n", sizeof(p[0])); // 1
/* 5 */  printf("%d\n", sizeof(&p));// 4/8
/* 6 */  printf("%d\n", sizeof(&p+1)); // 4/8
/* 7 */  printf("%d\n", sizeof(&p[0]+1)); // 4/8

πŸ”‘ Resolution:

1️⃣

2️⃣

3️⃣

4️⃣

5️⃣

6️⃣

7️⃣

Question 7:

1 point for each sub question, full score 7 points

πŸ’¬ Predict the running results of the following code (strlen)

int main()
{
    char *p = "abcdef";

    /* 1 */  printf("%d\n", strlen(p));
    /* 2 */  printf("%d\n", strlen(p+1));
    /* 3 */  printf("%d\n", strlen(*p));
    /* 4 */  printf("%d\n", strlen(p[0]));
    /* 5 */  printf("%d\n", strlen(&p));
    /* 6 */  printf("%d\n", strlen(&p+1));
    /* 7 */  printf("%d\n", strlen(&p[0]+1));

    return 0;
}

πŸ’‘ answer:

/* 1 */  printf("%d\n", strlen(p)); // 6
/* 2 */  printf("%d\n", strlen(p+1)); // 5
/* 3 */  printf("%d\n", strlen(*p)); // error
/* 4 */  printf("%d\n", strlen(p[0])); // error
/* 5 */  printf("%d\n", strlen(&p)); // Random value
/* 6 */  printf("%d\n", strlen(&p+1)); // Random value
/* 7 */  printf("%d\n", strlen(&p[0]+1)); // 5

πŸ”‘ Resolution:

1️⃣

2️⃣

3️⃣

4️⃣

5️⃣

6️⃣

7️⃣

Question 8:

1 point for each sub question, with a full score of 11 points

πŸ’¬ Predict the running result of the following code (sizeof)

int main()
{
    int a[3][4] = {0}; // Two dimensional array

    /* 1 */  printf("%d\n",sizeof(a));
    /* 2 */  printf("%d\n",sizeof(a[0][0]));
    /* 3 */  printf("%d\n",sizeof(a[0]));
    /* 4 */  printf("%d\n",sizeof(a[0]+1));
    /* 5 */  printf("%d\n",sizeof(*(a[0]+1)));
    /* 6 */  printf("%d\n",sizeof(a+1));
    /* 7 */  printf("%d\n",sizeof(*(a+1)));
    /* 8 */  printf("%d\n",sizeof(&a[0]+1));
    /* 9 */  printf("%d\n",sizeof(*(&a[0]+1)));
    /* 10 */ printf("%d\n",sizeof(*a));
    /* 11 */ printf("%d\n",sizeof(a[3]));

    return 0;
}

πŸ’‘ answer:

/* 1 */  printf("%d\n",sizeof(a)); // 48
/* 2 */  printf("%d\n",sizeof(a[0][0])); // 4
/* 3 */  printf("%d\n",sizeof(a[0])); // 16
/* 4 */  printf("%d\n",sizeof(a[0]+1)); // 4/8
/* 5 */  printf("%d\n",sizeof(*(a[0]+1)));// 4
/* 6 */  printf("%d\n",sizeof(a+1)); // 4
/* 7 */  printf("%d\n",sizeof(*(a+1))); // 16
/* 8 */  printf("%d\n",sizeof(&a[0]+1)); // 4/8
/* 9 */  printf("%d\n",sizeof(*(&a[0]+1))); // 16
/* 10 */ printf("%d\n",sizeof(*a)); // 16
/* 11 */ printf("%d\n",sizeof(a[3])); // 16

πŸ”‘ Resolution:

1️⃣

2️⃣

3️⃣

4️⃣

5️⃣

6️⃣

7️⃣

8️⃣

9️⃣

πŸ”Ÿ

1️⃣1️⃣

Summary:

πŸ“š Meaning of array name:

β‘  sizeof (array name) - the array name represents the entire array and calculates the size of the entire array.

β‘‘ & array name, where the array name represents the whole array, and the address of the whole array is taken out.

β‘’ In addition, all array names represent the address of the first element.

Drawing and analyzing C language pointer written test questions

preface:

C language pointer written test questions, it is recommended to look at the answers after completion. This blog has a detailed analysis part, which makes an in-depth drawing analysis of each problem. If you want to review the pointer before doing it, you can enter the following portal:

[vitamin C language] Chapter 6 - pointer

[vitamin C language] Chapter 10 - Advanced pointer (Part 1)

[vitamin C language] Chapter 10 - Advanced pointer (Part 2)

Pointer written test question (answer + detailed explanation)

πŸ“š explain:

β‘  It is suggested to take out paper and pen to write the results you think;

β‘‘ It is suggested to cover the answer first (it's OK to see it accidentally) and see what you think wrong. The important thing is not the result;

β‘’ For the wrong question, you can see the analysis part below the answer to the question for in-depth understanding;

Question 1:

πŸ’¬ What are the results of the following programs?

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int* ptr = (int*)(&a + 1);
    printf("%d,%d", *(a + 1), *(ptr - 1));

    return 0;
}

πŸ’‘ Answer to this question:

2,5

πŸ”‘ Detailed analysis: (wrong picture, int a[5] = {1,2,3,4,5})

Question 2:

  πŸ’¬ Suppose the value of p is 0x100000, what are the values of the following expressions?

(it is known that the variable size of the structure Test type is 20 bytes)

// Since the structure has not been learned, it is told that the size of the structure is 20 bytes
struct Test
{
    int Num;
    char* pcName;
    short sDate;
    char cha[2];
    short sBa[4];
}*p;

int main()
{
    printf("%p\n", p + 0x1);
    printf("%p\n", (unsigned long)p + 0x1);
    printf("%p\n", (unsigned int*)p + 0x1);

    return 0;
}

πŸ’‘ Answer to this question:

00000014
00000001
00000004

πŸ”‘ Detailed analysis:

Question 3:

πŸ’¬ What are the results of the following programs?

int main()
{
	int a[4] = { 1,2,3,4 };
	int* ptr1 = (int*) (&a + 1);
	int* ptr2 = (int*) ((int)a + 1);

	printf("%x, %x", ptr1[-1], *ptr2);

	return 0;
}

πŸ’‘ Answer to this question:

4, 2000000

πŸ”‘ Detailed analysis:

Question 4:

πŸ’¬ What are the results of the following programs?

int main()
{
	int a[3][2] = { (0, 1), (2, 3),(4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);

	return 0;
}

πŸ’‘ Answer to this question:

1

πŸ”‘ Detailed analysis:

Question 5:

πŸ’¬ What are the results of the following programs?

int main()
{
	int a[5][5];
	int(*p)[4];
	p = a;
	printf("%p, %d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);

	return 0;
}

  πŸ’‘ Answer to this question:

FFFFFFFC, -4

πŸ”‘ Detailed analysis:

Question 6:

πŸ’¬ What are the results of the following programs?

int main()
{
    int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int* ptr1 = (int*)(&aa + 1);
    int* ptr2 = (int*)(*(aa + 1));
    printf("%d, %d", *(ptr1 - 1), *(ptr2 - 1));
    return 0;
}

  πŸ’‘ Answer to this question:

10, 5

πŸ”‘ Detailed analysis:

Question 7:

πŸ’¬ What are the results of the following programs?

int main()
{
    char* a[] = { "work", "at", "alibaba" };
    char** pa = a;
    pa++;
    printf("%s\n", *pa);

    return 0;
}

  πŸ’‘ Answer to this question:

at

πŸ”‘ Detailed analysis:

Question 8:

πŸ’¬ What are the results of the following programs?

int main()
{
    char *c[] = {"ENTER", "NEW", "POINT", "FIRST"};
    char**cp[] = {c+3, c+2, c+1, c};
    char***cpp = cp;

    printf("%s\n", **++cpp);
    printf("%s\n", *--*++cpp+3);
    printf("%s\n", *cpp[-2] + 3);
    printf("%s\n", cpp[-1][-1] + 1);

    return 0;
}

  πŸ’‘ Answer to this question:

POINT
ER
ST
EW

πŸ”‘ Problem solving train of thought:

Classic dynamic memory allocation written test questions (question + answer + detailed explanation)

preface:

The topics are selected from high-quality C++/C programming guide and Nice2016 school recruitment test questions.

(4 major questions in total, 25 points for each question. The full score is 100 points)

πŸ“š Selected from high-quality C++/C programming guide and Nice2016 test questions

πŸšͺ Portal: [vitamin C language] dynamic memory management (review of relevant knowledge points)

Question 1:

πŸ’¬ What is the problem with the following code? Please point out the problem and modify it accordingly.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void GetMemory(char *p) {
    p = (char*)malloc(100);
}

void Test() {
    char *str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");
    printf(str);
}

int main() {
    Test();
    
    return 0;
}

πŸ’‘ Reference answer: STR is passed to the GetMemory function as a value, so the formal parameter p of the GetMemory function is a temporary copy of str. The address of the dynamically opened memory space inside the GetMemory function is stored in P and will not affect str. Therefore, after the GetMemory function returns, STR is still NULL, resulting in strcpy copy failure. Secondly, with the return of the GetMemory function, the formal parameter p is destroyed immediately and is not released in time using free, resulting in a memory leak in the 100 bytes dynamically opened. According to experience, the program will get stuck.

πŸ”‘ Detailed analysis:

⚑ Code modification:

β‘  Return p and let str receive:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// ↓ change the return type to char*
char* GetMemory(char *p) {
    p = (char*)malloc(100);
    return p; // Bring p back
}

void Test() {
    char *str = NULL;
    str = GetMemory(str); // Receive with str, and str points to the space just opened
    strcpy(str, "hello world"); // At this point, copy is no problem
    printf(str);
    // Remember free after use to solve the memory leak problem
    free(str);
    str = NULL; // Also set str to a null pointer
}

int main() {
    Test();

    return 0;
}

🚩  hello world

β‘‘ Change value passing to address passing:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//              ↓ receive with char * *
void GetMemory(char** p) {
    *p = (char*)malloc(100);
}

void Test() {
    char *str = NULL;
    GetMemory(&str); // Address transfer, you can get the address
    strcpy(str, "hello world");
    printf(str);
    // Remember free, you can solve the memory leak problem
    free(str);
    str = NULL; // Also set str to a null pointer
}

int main() {
    Test();

    return 0;
}

🚩   hello world

Question 2:

πŸ’¬ What is the problem with the following code?

#include <stdio.h>
#include <stdlib.h>

char* GetMemory(void) {
    char p[] = "hello world";
    return p;
}

void Test(void) {
    char *str = NULL;
    str = GetMemory();
    printf(str);
}

int main() {
    Test();

    return 0;
}

πŸ’‘ Reference answer: the array created inside the GetMemory function is actually created on the stack area. The space of the function p array is returned to the operating system. The returned address has no practical significance. If the memory is accessed through the returned address, it will lead to the problem of illegal memory access.

πŸ”‘ Detailed analysis:

Question 3:

πŸ’¬ What is the problem with the following code? Please point out the problem and modify it accordingly.

#include <stdio.h>
#include <stdlib.h>

void GetMemory(char **p, int num) {
    *p = (char *)malloc(num);
}

void Test(void) {
    char *str = NULL;
    GetMemory(&str, 100);
    strcpy(str, "hello");
    printf(str);
}

int main() {
    Test();

    return 0;
}

πŸ’‘ Reference answer: no free, resulting in memory leakage.

πŸ”‘ Detailed analysis:

⚑ Code modification:

#include <stdio.h>
#include <stdlib.h>

void GetMemory(char **p, int num) {
    *p = (char *)malloc(num);
}

void Test(void) {
    char *str = NULL;
    GetMemory(&str, 100);
    strcpy(str, "hello");
    printf(str);
    
    // Release and empty
    free(str);
    str = NULL;
}

int main() {
    Test();

    return 0;
}

Question 4:

πŸ’¬ What is the problem with the following code? Please point out the problem and modify it accordingly.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void Test(void) {
    char *str = (char *) malloc(100);
    strcpy(str, "hello");
    free(str);
    
    if(str != NULL) {
        strcpy(str, "world");
        printf(str);
    }
}

int main() {
    Test();

    return 0;
}

πŸ’‘ Answer to this question: after free, str is not set as a null pointer, resulting in if being true. The memory that has been released is accessed, causing the problem of illegal access.

πŸ”‘ Detailed analysis:

⚑ Code modification:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void Test(void) {
    char *str = (char *) malloc(100);
    strcpy(str, "hello");
    free(str);
    str = NULL; // Empty
    
    if(str != NULL) {
        strcpy(str, "world");
        printf(str);
    }
}

int main() {
    Test();

    return 0;
}

The end of this series.

Topics: C