12 detailed explanation of advanced pointer in C language

Advanced pointer

  1. Character pointer
  2. Array pointer
  3. Pointer array
  4. Array parameter passing and pointer parameter passing
  5. Function pointer
  6. Function pointer array
  7. Pointer to array of function pointers
  8. Callback function
  9. Pointer and array analysis of interview questions

Statement: This article is a note when learning the advanced C language. Learning content: Station B pengge C language , p34-p51. You can watch the video if you don't understand anything in the text.

Basic concept of pointer

  1. A pointer is a variable used to store an address, which is the unique identification of memory space.
  2. The size of the pointer is fixed at 4 or 8 bytes (on 32-bit or 64 bit platforms).
  3. There is a type of pointer. The pointer type determines the step size of address access, that is, the permission during pointer dereference operation.
  4. Pointer operation.

Character pointer

Form of use:

int main(){
    char ch = 'w';
    char* pc = &ch;
    *pc = 's';//You need to dereference to access the address, and then modify the address.
    return 0;
}

When a character array is used to store strings:

int main(){
    char arr[]="abcdef";
    char* pc = arr;//At this point, the pointer uses the address of the first element of the array.
    return 0;
}

Other situations: (pay attention to the string and character output format and the call mode of the pointer).

int main(){
    const char* p = "abcdef";//In this case, "abcdef" is a constant string.
    printf("%c\n",*p);
    printf("%s",p);
    return 0;
}

Parsing: if the * in the first printf is removed, the format specifications type 'int' but the argument has type 'char *' will be warned.
If p in the second printf is changed to * p, format specifications type 'char *' but the argument has type 'char' will be warned.
p represents the address content* p means what the address content points to.

Question:

int main() {
    char arr1[] = "abcdef";
    char arr2[] = "abcdef";
    if (arr1 == arr2) {
        printf("hehe");
    } else {
        printf("haaa");
    }
    return 0;
}

Analysis: the answer is haaa; Because arr1 and arr2 store the address of the first element, while the first abcdef and the second abcdef are strings stored in different locations.

int main() {
    char* p1 = "abcdef";
    char *p2 = "abcdef";
    if (p1 == p2) {
        printf("hehe");
    } else {
        printf("haaa");
    }
    return 0;
}

Analysis: the answer this time is hehe; Because this time "abcdef" is a constant, only one copy is stored in memory. So p1 and p2 actually point to the same place. biao

The standard way of writing is

const char* p1 = "abcdef";
const char *p2 = "abcdef";

Pointer array

It's actually an array

int* parr[] = {arr1,arr2,arr3};

Basic use

int main() {
    int arr1[] = {1, 2, 3, 4, 5};
    int arr2[] = {2, 3, 4, 5, 6};
    int arr3[] = {3, 4, 5, 6, 7};
    int* parr[] = {arr1,arr2,arr3};
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 5; ++j) {
            printf("%d ", *(parr[i])+j);//Access the address of the int unit several after the initial address.
        }
        printf("\n");
    }
    return 0;
}

int* arr1[10];//Integer pointer array
char* arr2[4];//Character pointer array
char** arr3[8];//Secondary character pointer array

Array pointer

What are array pointers? It's a pointer.

An integer pointer is a pointer that can point to an integer. A floating point pointer is a pointer that can point to a floating point.

Then the array pointer is the pointer that can point to the array.

Definition mode

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int (*p)[10] = &arr ;//The address of the array should be saved.

First, P is combined with * to indicate that it is a pointer, and then it points to an integer array of size 10, so p is a pointer to the array. Is an array pointer.

Little practice

//Completion code
int main(){
	char* arr[5];
    pa = &arr;
}
int main(){
    char* arr[5];
    char* (*pa)[5] = &arr;
    return 0;
}

Array pointer element: 1 Is a pointer 2 Pointer name 3 Pointer to the size of the array 4 Pointer to the type of the element.

Array name and & array name

For int arr [10]; What are arr and & arr respectively. The array name table shows the address of the first element of the array& The array represents the address of the entire array.

So you can store the & array name in the array pointer.

Array pointer use understanding code (not used in practice)

int mian(){
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int (*pa)[10] = &arr;
    //Traversal of array elements
    for(int i = 0;i<10;i++){
        printf("%d ",*(*pa+i));//It needs to be understood that * pa is actually arr
    }
    //Traversal mode 2
    for(int i = 0;i<10;i++){
        printf("%d ",(*pa)[i]);
    }
    return 0;
}

In practical use, it is generally above two-dimensional array.

//Parameters are pointers
void print(int (*p)[5],int x,int y){
    int i = 0;
    for (i = 0; i < x; ++i) {
        int j = 0;
        for (j = 0; j < y; ++j) {
            printf("%d ",*(*(p+i)+j));//Address representation
            //The following is also possible.
            // printf("%d ",(*(p+i))[j]);// Array access method
            // 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}};
    print(arr,3,5);
    return 0;
}

Four access forms of arrays

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) {
        printf("%d", p[i]);
        printf("%d", *(p + i));
        printf("%d", *(arr + i));
        printf("%d", arr[i]);
    }
    return 0;
}
//p[i]==*(p + i)==*(arr + i)==arr[i]

Little practice

Explain the meaning of the following code.

int arr[5];
int *parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];

Parsing: Line 1: arr is an integer array with 5 elements.
The second line: parr1 is an array with 10 elements. When the type of each element is int *, overview parr1 is a pointer array.
The third line: parr2 is a pointer. The pointer points to an array. The array has 10 elements. The type of each element is int. overview, parr2 is an array pointer.
Line 4: parr3 is an array. The array has 10 elements, and each element is an array pointer. The array pointed to by the array pointer has 5 elements, and each element is of type int. (illustration)

Array parameter, pointer parameter

When designing a function, it is inevitable to pass an array or pointer to the function. How should the function receive it?

One dimensional array parameter transfer

How to receive parameters?

int main(){
    int arr[10];
    int* arr2 = {0};
    test(arr);
    test2(arr2);
    return 0;
}

test() receive parameters

void test(int arr[]){}
void test(int arr[10]){}//Any size can be defined.
void test(int *arr){}//Directly accept the transmitted address.

test2() accepts parameters

void test2(int *arr[20]){}//Any size can be defined.
void test2(int *arr[]){}
void test2(int **arr){}

Two dimensional array parameter transfer

How to receive parameters?

int main(){
    int arr[3][5] = {0};
    test(arr);//Two dimensional array parameter transfer
    return 0;
}

test() accepts parameters

void test(int arr[3][5]){}
void test(int arr[][5]){}
//Note that when passing parameters to a two-dimensional array, rows can be omitted, but columns must not be omitted.

Because in a two-dimensional array, you can't know how many rows there are, but you must know how many elements there are in a row.

The accepted parameter is in the form of a pointer

void test(int (*arr)[5]){}
//The pointer parameter should point to the address of the first element. The first element of a two-dimensional array is an array.
//Because it must be a pointer, the array pointer is used. The size of the pointing array is 5 and the type of array element is int.

You cannot directly use the first level pointer or the second level pointer. You cannot directly use the address of the first element of the first array.
The pointer parameter should point to the address of the first element. The first element of a two-dimensional array is an array.

Primary pointer transfer parameter

Receive parameters

void test(int* p){}
void test2(char* p){}//What type of pointer accepts what type of parameter

How to pass the parameter? Backward push back: the function receives the pointer and can pass the address to the function or the variable storing the pointer. Also note the type of pointer.

int main(){
    int a = 10;
    int* p1 = &a;
    test1(&a);
    test1(p1);
    char ch = 'h';
    char* pc = &ch;
    test2(&ch);
    test2(pc);
    return 0;
}

Secondary pointer transfer parameter

Customize the function form and understand how to receive parameters.

void test(int** p){}

How to pass parameters to a function? Think about what is the secondary pointer and the expression of the secondary pointer.
Parsing: secondary pointer variable, address of primary pointer and array name of primary pointer array.

int main(){
    int *p;//Primary pointer
    int **pp = &*p;//Address of the first level pointer
    int* arr[10];//Pointer array. The array contains pointers. At this point, the array name is equivalent to a secondary pointer.
    test(&*p);
    test(pp);
    test(arr);
    
}

Function pointer

Array pointers are pointers to arrays and function pointers are pointers to functions. Is a pointer to the address of the function.

int Add(int x,int y){
    int z =0;
    z = x + y;
    return z;
}
int main(){
    int a = 10;
    int b = 20;
    printf("%p\n",&Add);
    printf("%p\n",Add);
    return 0;
}

We find that this is the same as array names and & array names. Both the function name and & function name are addresses of functions.

For array pointers, we have

int arr[10] = { 0 };
int (*p)[10] = &arr;

So what about function pointers?

int Add(int x,int y){}
int (*pa)(int,int) = Add;//*First combine with pa to indicate that it is a pointer.
Use of function pointers
int Add(int x,int y){
    int z =0;
    z = x + y;
    return z;
}
int main(){
    int a = 10;
    int b = 20;
	int (*pa)(int,int) = Add;
    printf("%d\n",(*pa)(2,3));//(*pa)==Add
    return 0;
}

Of course, different types of function pointers are defined in different ways.

Use case 2

void Print(char* str){ printf("%s\n",str);}
int main(){
    void (*p)(char*) = Print;
    (*p)("hello world");
    return 0;
}

A simple analysis: pfun1 and pfun2 which can store the address of the function.

void (*pfun1)();
void* pfun2();

Analysis: the first step is to store the address, find what you need, find the pointer, which is the pointer, and pfun1 is the pointer.

pfun1 stores pointers. pfun1 combines with * first. The pointer points to a function without parameters. The return value type of this function is void (equivalent to no return value).

Read interesting code
//Code 1
(*(void (*)())0)();
//Code 2
void (*signal(int, void(*)(int)))(int);

Analysis: Code 1. First, void (*p)() is a function pointer, and p is the variable name of the function pointer. Void (*) () this is the type of a pointer function. If you put a type in the parentheses before 0, you are forced to convert 0 into the address of a function pointer. Then use * to dereference. Dereference is a function of type void (*) (). (* (type) 0). This is understood as calling the function at address 0.

Code 2, void(*)(int) is a function pointer type, and the parameters received by this function are of type int. signal(int, void(*)(int)) this is the declaration of a function. There are two parameters, one is of type int and the other is void(*)(int). (* signal(int, void(*)(int)) (int) so this is another function. The pointer parameter is int and the return type is void.

//Another understanding of code 2.
typedef void(*pfun_t)(int);//pfun_t rename the pointer type void(*)(int)
pfun_f signal(int,pfun_f);

Overview of code 2: signal is a function declaration; The signal function has two parameters. The first one is int. The second is the function pointer. The parameter of the function pointed to by the function pointer is int and the return type is void; The return type of the signal function is also a function pointer: the parameter of the function pointed to by the function pointer is int and the return value is void.

Function pointer supplement
int Add(int x,int y){
    int z =0;
    z = x + y;
    return z;
}
int main(){
    int a = 10;
    int b = 20;
	int (*pa)(int,int) = Add;
    printf("%d\n",(pa)(2,3));
    printf("%d\n",(*pa)(2,3));
    printf("%d\n",(**pa)(2,3));
    return 0;
}

All three call forms are the same correct output. Then * here does not play the role of dereference. However, we generally use the first two, which are easy to understand: the first, pa==Add, which is equivalent to a level-1 pointer and can be used directly. The second * pa dereference variable name is used. The * here is just a decoration. But note that if there is a * bracket, it must be added.

Function pointer array

Array is a storage space stored in the same type of data. Can we store function pointers in it?

We have learned about pointer array, so the function pointer also exists in the array. Is it the same as pointer function.

So how?

int (*parr[10])();

parr is first combined with [10], indicating that parr is an array and int(*) () is a function pointer.

Purpose of function pointer: transfer table.

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

// subtraction
int Sub(int x, int y) { return x - y; }

// multiplication
int Mul(int x, int y) { return x * y; }

// division
int Div(int x, int y) { return x / y; }

int main() {
    //Definition of array of function pointers parr[4] array variable name
    int (*parr[4])(int, int) = {Add, Sub, Mul, Div};
    for (int i = 0; i < 4; ++i) {
        // Call of function pointer array
        printf("%d\n", parr[i](2, 3));
    }
    return 0;
}
Little practice

char* my_strcpy(char* dest,const char* src){}

  1. Write a function pointer pf that can point to my_strcpy
  2. Write a function pointer array pfArr, which can store 4 my_ Address of strcpy function
//1. 
char* (*pf)(char* ,const char*);
//2.
char* (*pfArr[4])(char* ,const char*) = {my_strcpy};
Micro calculator
// 1. Do not use pointer calculator
int add(int a, int b) {
    return a + b;
}

int sub(int a, int b) {
    return a - b;
}

int mul(int a, int b) {
    return a * b;
}

int div(int a, int b) {
    return a / b;
}

int main() {
    int x, y;
    int input = 1;
    int res = 0;
    do {
        printf("*************************\n");
        printf(" 1:add     2:sub \n");
        printf(" 3:mul     4:div    0.exit\n");
        printf("*************************\n");
        printf("Please select:");
        scanf("%d", &input);
        switch (input) {
            case 1:
                printf("Input operand:");
                scanf("%d %d", &x, &y);
                res = add(x, y);
                printf("res = %d\n", res);
                break;
            case 2:
                printf("Input operand:");
                scanf("%d %d", &x, &y);
                res = sub(x, y);
                printf("res = %d\n", res);
                break;
            case 3:
                printf("Input operand:");
                scanf("%d %d", &x, &y);
                res = mul(x, y);
                printf("res = %d\n", res);
                break;
            case 4:
                printf("Input operand:");
                scanf("%d %d", &x, &y);
                res = div(x, y);
                printf("res = %d\n", res);
                break;
            case 0:
                printf("Exit program\n");
                break;
            default:
                printf("Selection error\n");
                break;
        }
    } while (input);

    return 0;
}
//Use function pointer array
int add(int a, int b) {
    return a + b;
}

int sub(int a, int b) {
    return a - b;
}

int mul(int a, int b) {
    return a * b;
}

int div(int a, int b) {
    return a / b;
}

int main() {
    int x, y;
    int input = 1;
    int res = 0;
    int (*p[5])(int x, int y) = {0, add, sub, mul, div}; //Transfer table
    while (input) {
        printf("*************************\n");
        printf(" 1:add  2:sub \n");
        printf(" 3:mul  4:div \n");
        printf("*************************\n");
        printf("Please select:");
        scanf(" %d", &input);
        if ((input <= 4 && input >= 1)) {
            printf("Input operand:");
            scanf("%d %d", &x, &y);
            res = (*p[input])(x, y);
            printf("res = %d\n", res);
        } else if(input == 0) {
            printf("Exit program");
        }
        else printf("Incorrect input\n");
    }
    return 0;
}

Pointer to array of function pointers

Array + pointer -- > array pointer
Function + pointer -- > function pointer
Function pointer + array -- > function pointer array
Function pointer array + pointer -- > pointer to function pointer array

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.

void test(char* str){ printf("%s\n",str); }
int main(){
    //Function pointer
    void (*pf)(char*) = test;
    //Function pointer array pfArr
    void (*pfArr[4])(char*);
    pfArr[0] = test;//Use of function pointer array
    //Pointer to the array of function pointers ppfArr.
    void (*(*ppfArr)[4])(char*) = &pfArr;
    return 0;
}

void (*(*ppfArr)[4])(char*) = &pfArr; Re parsing: (* ppfarr) is a pointer, (* ppfarr) [4] points to an array, (* (* ppfarr) [4]) is an array of function pointers, the parameter of each function in the array is (char *), and the return value is void.

Callback function

A callback function is a function called through a pointer. If the pointer (address) of a function is passed as a parameter to another function, when the function is used to call the function it points to, we mean that it is a callback function. The callback function is not called directly by the implementer of the function, but by another party when a specific event or condition occurs, which is used to respond to the event or condition.

In the first edition of the micro calculator, the switch statement is actually redundant. The redundant part is other statements except calling functions. Can we package these redundant parts into a function? The part of the function call is then accessed with a pointer.

Micro calculator
int add(int a, int b) {
    return a + b;
}

int sub(int a, int b) {
    return a - b;
}

int mul(int a, int b) {
    return a * b;
}

int div(int a, int b) {
    return a / b;
}
void Calc(int (*pf)(int,int)){
    int x = 0;
    int y = 0;
    printf("Input operand:");
    scanf("%d %d", &x, &y);
    printf("res = %d\n", pf(x,y));
}
void Calc(int (*pf)(int, int)) {
    int x = 0;
    int y = 0;
    printf("Input operand:");
    scanf("%d %d", &x, &y);
    printf("res = %d\n", pf(x, y));
}

int main() {
    int x, y;
    int input = 1;
    int res = 0;
    do {
        printf("*************************\n");
        printf(" 1:add     2:sub \n");
        printf(" 3:mul     4:div    0.exit\n");
        printf("*************************\n");
        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("Exit program\n");
                break;
            default:
                printf("Selection error\n");
                break;
        }
    } while (input);

    return 0;
}

The Calc() function receives the passed pointer parameter, and then calls the function of the passed function through this parameter. Then the passed function (called function) is called a callback function.

Understanding callback functions with qsort
  1. Review of bubble function
//The bubble sort here is the optimized bubble sort
void bubble_sort(int arr[],int sz){
    for(int i = 0; i<sz-1;i++){
        //Define tag
        int isSort = 1;
        //Every bubble
        for(int j = 0;j<sz-i-1;j++){
            if(arr[j]>arr[j+1]){
                int temp = arr[j];// exchange
                arr[j]=arr[j+1];
                arr[j+1] = temp;
                isSort = 0;
            }
        }
        if (isSort == 1) break;		//Final judgment
    }
}
int main(){
	int arr[10] = {9,8,7,6,5,4,3,2,1,0};
    int sz = sizeof(arr)/sizeof(arr[0]);
    bubble_sort(arr,sz);
    int i = 0;
    for(i = 0;i < sz; i++){
        printf("%d",arr[i]);
    }
}

Originally, we may think it is enough to use, but let's think carefully. If we pass in a floating point number or a structure, do we have to write the function again? We certainly don't want to. What can I do? At this time, we thought that we used the concept of callback function when making microcomputers. Can I use it here?

Before solving the above problems, let's see what others do.

  1. qsort() library function, using quick sort.
void qsort (void* base, size_t num, size_t size,
            int (*compar)(const void*,const void*));
//  void* base 		 Start and end position of target array
//  size_t num 		 The size of the array
//  size_t size 		 The size of the element
//  int (*compar)(const void*,const void*) 		 Comparison function

A pointer of type void * can receive any type of address. However, pointers of void type cannot be dereferenced. This type of pointer cannot add or subtract integers. Therefore, forced type conversion can be performed when operation is required.

Use of qsort() function

#include <stdlib.h>
#include <string.h>
typedef struct Stu {
    char name[20];
    int age;
} Stu;

int compare_int(const void *elem1, const void *elem2) {
    return *(int *) elem1 - *(int *) elem2;
}

void test1() {
    int arr[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
    int sz = sizeof(arr) / sizeof(arr[0]);
    //compare_int() requires that the return value of this function is a large return > 0, a small return < 0, and the same return 0
    qsort(arr, sz, sizeof(arr[0]), compare_int);
    int i = 0;
    for (i = 0; i < sz; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

//qsort requires that the return value of this function be int
int compare_float(const void *elem1, const void *elem2) {
    return (int) (*(float *) elem1 - *(float *) elem2);
}

void test2() {
    float arr[] = {9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0};
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), compare_float);
    int i = 0;
    for (i = 0; i < sz; i++) {
        printf("%f ", arr[i]);
    }
    printf("\n");
}
//Sort by age
int compare_Stu_by_age(const void *elem1, const void *elem2){
    //(Stu*)elem1 force type conversion.
    return (int )(((Stu*)elem1)->age - ((Stu*)elem2)->age);
}
//Sort by name
int compare_Stu_by_name(const void *elem1, const void *elem2){
    // strcmp is required to compare strings
    return strcmp(((Stu*)elem1)->name, ((Stu*)elem2)->name);
}
void test3() {
    Stu arr[3] = {{"zhangsan", 20},
                {"lisi",     30},
                {"wangwu",   18}};
    int sz = sizeof(arr) / sizeof(arr[0]);
    // Sort by age
    qsort(arr, sz, sizeof(arr[0]), compare_Stu_by_age);
    // Print
    int i = 0;
    for (i = 0; i < sz; i++) {
        printf("%s ", arr[i].name);
        printf("%d ", arr[i].age);
    }
    printf("\n");
    // Sort by name
    qsort(arr, sz, sizeof(arr[0]), compare_Stu_by_name);
    // Print
    i = 0;
    for (i = 0; i < sz; i++) {
        printf("%s ", arr[i].name);
        printf("%d ", arr[i].age);
    }
    printf("\n");
}

int main() {
    test1();//Compare int
    test2();//Compare float
    test3();//Comparison structure
    return 0;
}

Qsort (parameter 1, parameter 2, parameter 3, parameter 4);
First parameter: the address of the first element of the array to be sorted
The second parameter: the element size of the array to be sorted
The third parameter: the size of each element of the array to be sorted - in bytes
The fourth parameter is the function pointer, which compares the address of the function used by the two elements - this function is implemented by the user himself.
The two parameters of the function pointer are the addresses of the two elements to be compared. The return value of the function is of type int.

We found that the qsort function in the library function can realize almost all types of sorting except integer. Can our fake sorting also be designed like this?

First, let's consider the parameters of the function. 1. You need to know the address of the first element (where it starts), 2 The size of the entire array (to determine how many rounds of operation are required.) 3. What is the type of operation, but the type cannot pass parameters, so we can pass the size (width) of this type. 4. The address of the function where the two values are compared.

Main function realization

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++;
    }
}
//The bubble sort here is the optimized bubble sort
// (* compare)(void* elem1,void* elem2) function pointer. A parameter used to compare two numbers.
void bubble_sort(void *base, int sz, int width, int (*compare)(const void *elem1, const void *elem2)) {
    //Big wheel
    for (int i = 0; i < sz - 1; i++) {
        //Define tag
        int isSort = 1;
        //Every bubble
        for (int j = 0; j < sz - i - 1; j++) {
            // Comparison of two elements
            //If it's a structure, we don't have a general method. So what do you do????
            //We can learn qsort and let users write their own comparison methods.
            if (compare((char *) base + j * width, (char *) base + (j + 1) * width) > 0) {
                // exchange
                Swap((char *) base + j * width, (char *) base + (j + 1) * width, width);
                isSort = 0;
            }
        }
        if (isSort == 1) break;        //Final judgment
    }
}

Analysis: the idea of bubble sorting is used as a whole. Difficulties: I think there are two difficulties: 1 When bubble sorting is used to judge two elements, because the pointer of void type is passed in, it cannot be used directly. However, we have the width of the variable to be compared. By controlling the step size through the char pointer, we can accurately match to the first address, and then compare the quantity elements. (there was a question here. How can char get all the variables when accessing the first address of the variable? The answer is that this is a callback function. After callback to the function written by the user, we perform a forced type conversion. At this time, we access all the variables.) After the comparison, the two values should be exchanged. 2. The second problem is the exchange of two values: the step size is equivalent to the interval between two variables. As long as you start from the left and exchange one by one from the right through this interval. You can exchange all the contents in the address where the two variables are located. (here is my diagram).

Complete code display

#include <string.h>

void bubble_sort(void *base, int sz, int width, int (*compare)(const void *elem1, const void *elem2));

typedef struct Stu {
    char name[20];
    int age;
} Stu;

int compare_int(const void *elem1, const void *elem2) {
    return *(int *) elem1 - *(int *) elem2;
}

void test1() {
    int arr[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
    int sz = sizeof(arr) / sizeof(arr[0]);
    //compare_int() requires that the return value of this function is a large return > 0, a small return < 0, and the same return 0
    bubble_sort(arr, sz, sizeof(arr[0]), compare_int);
    int i = 0;
    for (i = 0; i < sz; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

//qsort requires that the return value of this function be int
int compare_float(const void *elem1, const void *elem2) {
    return (int) (*(float *) elem1 - *(float *) elem2);
}

void test2() {
    float arr[] = {9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0};
    int sz = sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, sz, sizeof(arr[0]), compare_float);
    int i = 0;
    for (i = 0; i < sz; i++) {
        printf("%f ", arr[i]);
    }
    printf("\n");
}

//Sort by age
int compare_Stu_by_age(const void *elem1, const void *elem2) {
    //(Stu*)elem1 force type conversion.
    return (int) (((Stu *) elem1)->age - ((Stu *) elem2)->age);
}

//Sort by name
int compare_Stu_by_name(const void *elem1, const void *elem2) {
    // strcmp is required to compare strings
    return strcmp(((Stu *) elem1)->name, ((Stu *) elem2)->name);
}

void test3() {
    Stu arr[3] = {{"zhangsan", 20},
                  {"lisi",     30},
                  {"wangwu",   18}};
    int sz = sizeof(arr) / sizeof(arr[0]);
    // Sort by age
    bubble_sort(arr, sz, sizeof(arr[0]), compare_Stu_by_age);
    // Print
    int i = 0;
    for (i = 0; i < sz; i++) {
        printf("%s ", arr[i].name);
        printf("%d ", arr[i].age);
    }
    printf("\n");
    // Sort by name
    bubble_sort(arr, sz, sizeof(arr[0]), compare_Stu_by_name);
    // Print
    i = 0;
    for (i = 0; i < sz; i++) {
        printf("%s ", arr[i].name);
        printf("%d ", arr[i].age);
    }
    printf("\n");

}


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++;
    }
}

//The bubble sort here is the optimized bubble sort
// (* compare)(void* elem1,void* elem2) function pointer. A parameter used to compare two numbers.
void bubble_sort(void *base, int sz, int width, int (*compare)(const void *elem1, const void *elem2)) {
    //Big wheel
    for (int i = 0; i < sz - 1; i++) {
        //Define tag
        int isSort = 1;
        //Every bubble
        for (int j = 0; j < sz - i - 1; j++) {
            // Comparison of two elements
            //If it's a structure, we don't have a general method. So what do you do????
            //We can learn qsort and let users write their own comparison methods.
            if (compare((char *) base + j * width, (char *) base + (j + 1) * width) > 0) {
                // exchange
                Swap((char *) base + j * width, (char *) base + (j + 1) * width, width);
                isSort = 0;
            }
        }
        if (isSort == 1) break;        //Final judgment
    }
}

int main() {
    test1();//Compare int
    test2();//Compare float
    test3();//Comparison structure
    return 0;
}

Pointer and array analysis of interview questions

  1. One dimensional array
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
//Sizeof (array name). At this time, the array name represents the whole array. The total size of the array is calculated, and 4 * 4 = 16 bytes should be output (omitted later)
printf("%d\n",sizeof(a+0));
//a+0 represents the first element address, and + 0 calculates the size of the first element address. 32-bit platform output 4 64 bit platform output 8
printf("%d\n",sizeof(*a));
//First, dereference. A represents the address of the first element, * a is the first element. The size of the first element is calculated, and the int size is 4.
printf("%d\n",sizeof(a+1));
//Same as sizeof (a + 0) 32-bit platform output 4 64 bit platform output 8
printf("%d\n",sizeof(a[1]));
//a[1] represents the second element and calculates the size of the second element. int type is 4.
printf("%d\n",sizeof(&a));
//&A is the address to fetch the array. The size of the address is also 32-bit platform output 4 and 64 bit platform output 8.
printf("%d\n",sizeof(*&a));
//*&A refers to the entire array by dereferencing the array address. The size of the entire array is calculated. Should output 4 * 4 = 16
//Understanding 2: * & in * & A, the two operators counteract each other, and finally it is equivalent to sizeof(a).
printf("%d\n",sizeof(&a+1));
//&A the address of array (& A + 1) is the next address of the entire array address. As long as it is an address, the size is 32-bit platform 4 and 64 bit platform 8
printf("%d\n",sizeof(&a[0]));
//&A [0] takes the address of the first element. As long as it is an address, the size is 32-bit platform 4 and 64 bit platform 8
printf("%d\n",sizeof(&a[0]+1));
//&A [0] the address of the first element, + 1 is the address of the second element. As long as it is an address, the size is 32-bit platform 4 and 64 bit platform 8
  1. Character array

(1) Initialize and store directly in the array

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
//Sizeof (array name). At this time, the array name represents the whole array. The total size of the array is calculated, and 1 * 6 = 6 should be output
printf("%d\n", sizeof(arr+0));
//arr+0 represents the first element address, and + 0 calculates the size of the first element address. 32-bit platform output 4 64 bit platform output 8
printf("%d\n", sizeof(*arr));
//First, dereference. Arr represents the address of the first element, * arr is the first element. The size of the first element is calculated, and the char type size is 1
printf("%d\n", sizeof(arr[1]));
//The size of the second element. char type size 1
printf("%d\n", sizeof(&arr));
//&Address stored in arr array, address, 32-bit platform 4, 64 bit platform 8.
printf("%d\n", sizeof(&arr+1));
//Store the next address after the address of the entire arr array. Address, 32-bit platform 4, 64 bit platform 8.
printf("%d\n", sizeof(&arr[0]+1));
//The address of the second element. Address, 32-bit platform 4, 64 bit platform 8.


printf("%d\n", strlen(arr));
//End calculation encountered '\ 0'. However, when using an array to initialize storage, it should be a random value.
printf("%d\n", strlen(arr+0));
//strlen(arr) and strlen(arr+0) are exactly the same. Find '\ 0' from the first element address.
printf("%d\n", strlen(*arr));
//The strlen() parameter should be an address* arr is the first element. Element a is converted to ASCII code 97, and then access 97.
//The program may crash directly.
printf("%d\n", strlen(arr[1]));
//ditto
printf("%d\n", strlen(&arr));
//The address of the whole element is the address of the first element. Look backward for '\ 0', which is a random value.
printf("%d\n", strlen(&arr+1));
//Find '\ 0' from the next address after the end of the entire array. Random value
printf("%d\n", strlen(&arr[0]+1));
//Look back for '\ 0' starting with the second element. Random value.
//But there is a connection between the above random values. 
//strlen(arr)=strlen(arr+0)=strlen(&arr)= strlen(&arr+1)+6 = strlen(&arr[0]+1)+1

(2) Initialize with string

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
//There is' \ 0 'at the end of the actual storage, so the result: 7 * 1 = 7
printf("%d\n", sizeof(arr+0));
//The first element address + 0 is also the first element address, and the address size is 4 / 8.
printf("%d\n", sizeof(*arr));
//The first element address dereference is the first element with a size of 1
printf("%d\n", sizeof(arr[1]));
//The second element has a size of 1
printf("%d\n", sizeof(&arr));
//Although it is the address of the array, it is also the address. 4/8
printf("%d\n", sizeof(&arr+1));
//At first glance, the address is 4 / 8
printf("%d\n", sizeof(&arr[0]+1));
//Still address


printf("%d\n", strlen(arr));
//6
printf("%d\n", strlen(arr+0));
//6
printf("%d\n", strlen(*arr));
//Failed to access the first element. Access the address corresponding to the ASCII code of the first value. Illegal access to memory, the program may directly error.
printf("%d\n", strlen(arr[1]));
//Ibid
printf("%d\n", strlen(&arr));
//The address of the array is still 6. There will be a warning. The address of the array char (* P) [7] = & arr, and the type of & arr is char(*)[7]
//But because it's an address, the program can run.
printf("%d\n", strlen(&arr+1));
//The start address of the next array after the end of this array. There will be warnings for the same reasons. The result is a random value.
printf("%d\n", strlen(&arr[0]+1));
//The length of the second element of the array to '\ 0'. 6-1 = 5
  1. Constant string address passed to pointer.
char *p = "abcdef";
printf("%d\n", sizeof(p));
//Pointer size, address size 4 / 8
printf("%d\n", sizeof(p+1));
//What you get is the address of character b, which is still address 4 / 8
printf("%d\n", sizeof(*p));
//The dereference address accesses the character char with a size of 1
printf("%d\n", sizeof(p[0]));
//int arr[10],arr[0]==*(arr+0),p[0]==*(p+0). 
//p[0] accesses a with a size of 1
printf("%d\n", sizeof(&p));
//Address 4 / 8
printf("%d\n", sizeof(&p+1));
//Address 4 / 8
printf("%d\n", sizeof(&p[0]+1));
//Take the address of a and add one to get the address of b. Address 4 / 8


printf("%d\n", strlen(p));
//The address is passed in, so it can be calculated. The length from the first element to '\ 0' is 6
printf("%d\n", strlen(p+1));
//p is the address of a and p+1 is the address of b. Length to '\ 0' 5
printf("%d\n", strlen(*p));
//*p access the address corresponding to the ASCII code of the first value. Illegal access to memory, the program may directly error.
printf("%d\n", strlen(p[0]));
//ditto
printf("%d\n", strlen(&p));
//p is a pointer, the content is the address, and & p finds the address of the content. Not the address this content points to.
//However, because the length is sought, it depends on whether a group of results in the content is exactly 0, which may end. Overall, it is still a random value.
//Note here that it reads the contents of memory.
printf("%d\n", strlen(&p+1));
//The next address of the address of p. the principle is the same as above. Random value
printf("%d\n", strlen(&p[0]+1));
//First, p[0] is a, + 1 is b, and the length is 6-1 = 5
  1. Two dimensional array
int a[3][4] = {0};
printf("%d\n",sizeof(a));
//The size of the whole array is 3 * 4 * 4 = 48
printf("%d\n",sizeof(a[0][0]));
//Size of the first element in the first line int 4
printf("%d\n",sizeof(a[0]));
//The size of the array in the first row is 4 * 4 = 16
//a[0] is equivalent to the array name of the first row as a one-dimensional array. sizeof(arr[0]) puts the array list in sizeof() and calculates the size of the first row
printf("%d\n",sizeof(a[0]+1));
//a[0] is equivalent to the array name of the first row, which is the address of the first element of the first row, and a[0]+1 is the address of the second element of the first row. Address: 4 / 8
printf("%d\n",sizeof(*(a[0]+1)));
//According to the previous analysis, a[0]+1 is the address of the second element in the first line, and the dereference is the element at this position, with the size int of 4
printf("%d\n",sizeof(a+1));
//A is the array name of a two-dimensional array. There is neither sizeof(a) nor & (a), so a is the address of the first element. When a two-dimensional array is regarded as a one-dimensional array, the first element of the two-dimensional array is its first row, and a is the address of the first row (first element). a+1 is the address of the second line. Size 4 / 8
printf("%d\n",sizeof(*(a+1)));
//sizeof(a[1]) calculates the size of the second line in bytes 4 * 4 = 16
printf("%d\n",sizeof(&a[0]+1));
//The address of the second line, because &a[0] the address of the first line is taken. Address size 4
printf("%d\n",sizeof(*(&a[0]+1)));
//Calculate the size of the second row. The address dereference of the second line is the second line.
printf("%d\n",sizeof(*a));
//A is the first element address - the first line address * a is the first line. Here is the size of the first line. sixteen
printf("%d\n",sizeof(a[3]));
//Since the sizeof internal is not involved in the calculation, just look at its form, assume that there is a fourth line with a size of 16

Summary:

1. Sizeof (array name), where the array name represents the entire array and calculates the size of the array.
2. & array name. This array name represents the whole array and the address of the whole array is taken out
3. In addition, all array names are the address of the first element.

Pointer written test questions

Written test question 1: what is the result of program execution?

int main() {
    int a[5] = {1, 2, 3, 4, 5};
    int *ptr = (int *) (&a + 1);
    printf("%d,%d", *(a + 1), *(ptr - 1));
    return 0;
}

Analysis: the answer is 2,5& A + 1 is the next address to skip the entire array. Array pointer plus one is an array pointer type and cannot be stored in integer pointer, so forced type conversion is carried out. When outputting, the first value output is the address of the first element of the array plus one, that is, the address of the second element. Then, the second element is obtained by de referencing, so the first output is 2; ptr is the next address to skip the entire array, and ptr is a pointer of type int, then the pointer minus one is its previous address, so it should point to the last element of array a, so the second output is 5.

Written test question 2:

struct Test
{
 int Num;
 char *pcName;
 short sDate;
 char cha[2];
 short sBa[4];
}*p;
//Suppose the value of p is 0x100000. What are the values of the expressions in the following table?
//It is known that the variable size of the structure Test type is 20 bytes
int main()
{
 printf("%p\n", p + 0x1);
 printf("%p\n", (unsigned long)p + 0x1);
 printf("%p\n", (unsigned int*)p + 0x1);
 return 0; 
}

Resolution: 0x100014 0x100001 0x100004. First, what is p? P is the structure pointer; 0x1 is the decimal 1. What is the next address to skip this structure in the first print? 0x100000(16)+20(10)=0x100014(16). For the second printing, first convert p to unsigned long integer and then add one. Print out according to address: 0x100000(16)+1=0x100001(16). For the third printing, first convert P into an unsigned int pointer. Adding one means skipping a pointer. The size of a pointer is 0x100000(16)+4(10)=0x100004(16).

Written test question 3:

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;
}

Resolution: 42 million. First, explain ptr1[-1], the content in the same test question 1. ptr1 is the first address after skipping the array, and ptr1[-1] is the last int of this address. That's a[3]. For the second, we directly illustrate (yellow is the first step, blue is the second step, and red is the third step). Because our computer is a small end storage mode, when storing the specific elements of the array, we store them as follows. (int *)((int)a + 1). The first is to forcibly convert the address of a to int, then add 1 to it, and then convert it to an int pointer. Finally, it is the part representing the red frame column. Read this part according to the small end storage mode: 0x02000000. Remove the high-order 0 during output.

Written test question 4:

int main()
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    int *p;
    p = a[0];
    printf( "%d", p[0]);
	return 0;
}

The answer is 1. (0, 1), (2, 3), (4, 5) first think that this is a comma expression, so int a [3] [2] = {(0, 1), (2, 3), (4, 5)}; This sentence is equivalent to int a[3][2]={1,3,5}. When stored in memory, the first line is 1, the second line is 3, the first line is 5, and the rest are all 0. a[0] is the array in the first row. There is no sizeof or & so it represents the address of the first element. p[0] is equivalent to * (p+0) is 1.

Written test question 5

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;
}

Analysis: the answer is FFFFFFFFFFFFFFFC,-4. int(*p)[4], is an array of pointers. Forcibly assign a[5][5] to p, and the result is as shown below (array distribution). Measure the address of the blue dot in the figure. Because the weight is the low address minus the high address, the answer is - 4. However, when outputting in the form of% p, it should output the complement corresponding to - 4, and the corresponding hexadecimal number fffffffffffc (32 bits are FFFFFFFC), - 4.

Written test question 6:

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;
}

The answer is 10, 5& Arr is the entire array, and the calculated PTR is the first address after the entire array. When outputting, the int pointer is - 1, and then the address is taken. The result should be the last element of aa array, 10. aa + 1 is the address of the second line, and then the dereference will get the second line, and then force the type to be converted to a pointer. When outputting ptr2 - 1, the address is subtracted by one and int * is subtracted. At this time, the address of the last line in the previous line of the second line is taken, and then dereference to obtain that the element at this position is 5.

Supplement:

int arr[] = {1,2,3,4};
int* p = arr;

In this case, * (P + 2) < = > P [2] < = > * (arr + 2) < = > arr [2]

Written test question 7

int main()
{
	char* a[] = {"work","at","alibaba"};
	char**pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

The answer is at. A is an array of pointers. A represents the address of the first element, which is the address of the first string "work". Because a itself is a pointer, then this pointer is stored in the secondary pointer pa. pa + + is equivalent to a+1; Is the address of the second string. After dereferencing, the second string is "at".

Written examination question 8

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;
}

Analysis: the answer is: POINT,ER,ST,.

Note: 1 The value has changed since the increment. 2. Is the address of the string or the address of the character taken. Although it is an address, the operation is different.

FIRST char *c [] = {"ENTER", "NEW", "POINT", "FIRST"}; Is an array of pointers (pointers stored in the array). char**cp[] = {c+3,c+2,c+1,c}; In C is the address of the FIRST element. In this case, CP [] is equivalent to reversing the order of array C. CP is the address of the FIRST element, which is stored in the cpp secondary pointer variable.

In the first printf, + + cpp is added to the pointer, that is, the address of the second element in cp. the first dereference obtains the address of the third element in c. The second dereference refers to the address of the first character of the string, and output "% s" to get "POINT".

In the second printf, according to the priority, first + + then dereference and then - then dereference and then add three. However, here, at the first printf, we refer to the address of the second element in cp through cpp. So 1 First, + + points to the address of the third element in cp. Re dereference will get the address of the second element in c (I don't know how to read the first printf again), and then - get the address of the first element in c. re dereference is the address of the first character of the first string in c. Finally, add 3 to get the string "ER" starting from the fourth character.

In the third printf, * cpp[-2]+3 is equivalent to * (* (cpp-2))+3, and then + 3. Firstly, before calculation, it is emphasized that CPP has pointed to the third element of cp, (cpp-2) refers to the first element of cp. dereference obtains the fourth element string of c, and then dereference obtains the address of the first character in the fourth string of c. Output '% s' gets' ST'.

In the fourth printf, cpp[-1][-1]+1 is equivalent to * (* (cpp-1)-1)+1. Because we know that cp updates the position in the second printf (to the position of the third element of cp), but it is not modified in the third printf, so (cpp-1) represents the position of the second element in cp, and the dereference accesses the address of the third element string in c. On this basis, first - 1 gets the address of the second string in c, and then dereference is the address of the first character of the second string in c, and then + 1 is the second character of the second string in c. Output "% s" gets "EW".

Keywords: C Back-end

Added by jedimaster_att on Wed, 12 Jan 2022 14:55:57 +0200