C pointer and array combination (26)

        We mentioned earlier that the essence of an array is a continuous memory space , then its size is sizeof(array_type) * array_size , and the array name can be regarded as a constant pointer to the first element of the array . So the question is, what is the meaning of the array a + 1? What was the result? What is the meaning of pointer arithmetic? The results of it? Let's take a look at a sample code, the code is as follows

#include <stdio.h>

intmain()
{
    int a[5] = {0};
    int* p = NULL;
    
    printf("a = 0x%X\n", (unsigned int)(a));
    printf("a + 1 = 0x%X\n", (unsigned int)(a + 1));
    
    printf("p = 0x%X\n", (unsigned int)(p));
    printf("p + 1 = 0x%X\n", (unsigned int)(p + 1));
    
    return 0;
}

        The compilation result is as follows

picture.png

        We see that the array a is equivalent to a constant pointer, and it points to the address of the first element, a + 1 is the address of the first element plus 4, which is the address of the second element of the array. Since the pointer is of type p int, p + 1 is equivalent to adding 4.

        A pointer is a special variable that operates with integers as p + n <==> (unsigned int)p + n*sizeof(*p) ; then when the pointer points to an element of an array of the same type: p + 1 will point to the next element of the current element; p - 1 will point to the previous element of the current element. Only subtraction is supported between pointers, and the pointer types involved in the subtraction must be the same. p1 - ​​p2 <==> ((unsigned int)p1 - (unsigned int)p2)/sizeof(type) ; note: a> pointer subtraction only makes sense if both pointers point to elements in the same array , which means the subscript difference of the element pointed to by the pointer; b> When the elements pointed to by the two pointers are not in the same array, the result is defined .

        Pointers can also perform relational operations ( <, <=, >, >= ). The premise of pointer relational operations is to point to elements in the same array at the same time; the comparison operation between any two pointers ( ==, != ), The pointer types participating in the comparison operation must be the same.

        Let's take a look at a sample code, the code is as follows

#include <stdio.h>

#define DIM(a) (sizeof(a) / sizeof(*a))

intmain()
{
    char s[] = {'H', 'e', 'l', 'l', 'o'};
    char* pBegin = s;
    char* pEnd = s + DIM(s); // Key point
    char* p = NULL;
    
    printf("pBegin = %p\n", pBegin);
    printf("pEnd = %p\n", pEnd);
    
    printf("Size: %d\n", pEnd - pBegin);
    
    for(p=pBegin; p<pEnd; p++)
    {
        printf("%c", *p);
    }
    
    printf("\n");
   
    return 0;
}

        The macro we defined in line 3 is to find the number of elements of this array, the pointer pEnd defined in line 9 is the address of the first element of the array plus the number of array elements, then it just points to the criticality of the last element of the array. This is a gray area in C, and it's legal in C. Let's take a look at the compilation results

picture.png

        We see that the result is as we thought, because it is an array of type char, so pEnd = pBegin + 5.

        Array names can be used as constant pointers, so can pointers also be used as array names? We will go on to say later that there are two ways to access the array: 1. Access the elements in the array in the form of subscripts; 2. Access the elements in the array in the form of pointers . So what is the difference between these two methods? It is more efficient than the subscripted form when the pointer is moved in the array in fixed increments. In particular, it is more efficient when the pointer increment is 1 and the hardware has a hardware increment model. The subscript form and the pointer form are also converted to each other: a[n] <==> *(a + n) <==> *(n + a) <==> n[a] . Isn't this notation weird? But after theoretical derivation, it is completely established. Let's see if we support this way of writing.

#include <stdio.h>

intmain()
{
    int a[5] = {0};
    int* p = a;
    int i = 0;
    
    for(i=0; i<5; i++)
    {
        p[i] = i + 1;
    }
    
    for(i=0; i<5; i++)
    {
        printf("a[%d] = %d\n", i, *(a + i));
    }
    
    printf("\n");
    
    for(i=0; i<5; i++)
    {
        i[a] = i + 10;
    }
    
    for(i=0; i<5; i++)
    {
        printf("p[%d] = %d\n", i, p[i]);
    }
    
    return 0;
}

        We see that the pointer p is defined on the 6th line of the program and it points to the array a. The next step is whether the pointer can also be used as an array name as we said before. If it can, the 11th line will not report an error. In line 16, we print the value in the array a in the form of a pointer, in line 23, we verify whether the above i[a] is correct, and in line 28, we access the array by subscripting. Let me see the compilation result

picture.png

        We see that the program does not report errors and executes perfectly, which answers our questions and doubts above. But it should be noted: in modern compilers, the optimization rate of generated code has been greatly improved, and the efficiency of the subscript form is equivalent to that of the pointer form when the increment is fixed; but from the point of view of code readability and maintenance, The subscript form is better, which is why the arrays in the code we usually see are accessed in the subscript form.

        Let's go down and do another experiment to see the difference between arrays and pointers

test.c code

#include <stdio.h>

intmain()
{
    extern int a[];
    
    printf("&a = %p\n", &a);
    printf("a = %p\n", a);
    printf("*a = %d\n", *a);

    
    return 0;
}


ext.c code

int a[] = {1, 2, 3, 4, 5};

        We see that an array is defined in ext.c, we first access it in test.c as an array to see the print result

picture.png

        The result we see is consistent with what we thought, &a represents the address of the array, a represents the address of the first element of the array, and the two are the same. The value of *a is the value of the first element in the array. Let's change line 5 in test.c to extern int* a; so, let's take a look at the compilation result

picture.png

        We see a segfault, what's going on here? The value of array a is 1, and *a has a segmentation fault. In memory, the value of the array is stored as 0001 0002 ... 0005 (big endian machine). Then a is naturally 1. The address of 1 in the computer is in kernel mode. If a program in user mode wants to access the address in kernel mode, the computer will of course report an error.

        So what's the difference between a and &a? a is the address of the first element of the array, &a is the address of the entire array. The difference between a and &a is pointer arithmetic. a + 1 ==> (unsigned int)a + sizeof(*a); &a + 1 ==> (unsigned int)(&a) + sizeof(*&a) ==> (unsigned int)(&a) + sizeof( a);

        Let's take a look at a classic pointer arithmetic problem, which is also a written test interview question.

#include <stdio.h>

intmain()
{
    int a[5] = {1, 2, 3, 4, 5};
    int* p1 = (int*)(&a + 1); 
    int* p2 = (int*)((int)a + 1);
    int* p3 = (int*)(a + 1);
    
    printf("%d, %d, %d\n", p1[-1], p2[0], p3[1]);
    
    return 0;
}

        Let's look at the compilation results

picture.png

        Let's analyze, the first p1[-1] ==> p1[&a +1 - 1] =>p1[&a] , naturally its value is 5. p3[1] ==> (a + 1 +2) ==> (a + 2) , which is naturally 3. The second number feels like a random number, but after careful analysis, it is the first address plus 1, that is, it is shifted one bit back. In the little-endian system, this array is distributed like 1000 2000 ... 5000. If it is shifted by one bit, it becomes 0002, that is, 0x02000000 is converted to decimal and it is 33554432.

        When an array is used as a function parameter, the compiler compiles it into the corresponding pointer. Such as: void f(int a[]) <==> void f(int* a); void f(int a[5]) <==> void f(int* a) ; in general, when defining When the function has an array parameter, you need to define another parameter to calibrate the size of the array.

        Let's look at a sample code

#include <stdio.h>

void func1(char a[5])
{
    printf("In func1: sizeof(a) = %d\n", sizeof(a));
    
    *a = 'a';
    
    a = NULL;
}

void func2(char b[])
{
    printf("In func2: sizeof(b) = %d\n", sizeof(b));
    
    *b = 'b';
    
    b = NULL;
}

intmain()
{
    char array[10] = {0};
    
    func1(array);
    
    printf("array[0] = %c\n", array[0]);
    
    func2(array);
    
    printf("array[0] = %c\n", array[0]);
    
    return 0;
}

        We print its parameter size in func1, and assign it as a pointer and point to NULL, if it is an array, it will report an error. Let's take a look at the compilation results

picture.png

        We found that the array parameters of both functions are treated as pointers. Through the study of pointers and arrays in this section, the summary is as follows: 1. When an array is declared, the compiler automatically allocates a continuous memory space. When a pointer is declared, only a 4-byte space for holding the address value is allocated; 2. Pointers and integers An operation can be performed and the result is a pointer. Only the subtraction operation is supported between pointers, and the result is the subscript difference of the array elements; 3. Comparison operations are supported between pointers, and their types must be the same; 4. The array name and the pointer are only used in the same way. The essence of the array name is not a pointer. The essence is not an array; 5. The name of the array is not the address of the array, but the address of the first element of the array; 6. The array of the function is parameterized as a pointer.


        Welcome to learn C language together , you can add me QQ: 243343083 .

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324586820&siteId=291194637