[C language] from introduction to earth (Advanced pointer Chapter 1)

preface:
This article is the advanced level of pointer. In the previous article, we have a basic understanding of pointer, but we don't have an in-depth understanding of it. Therefore, we enter the advanced pointer and have an in-depth understanding of the relationship between pointer and other knowledge.

If you haven't read the pointer, you can click the link below to learn first:
[C language] from introduction to earth (pointer)

In this chapter, we understand:

  1. A pointer is a variable used to store an address. It is an address that uniquely identifies a piece of memory space.
  2. The size of the pointer is fixed at 4 / 8 bytes (32-bit platform / 64 bit platform).
  3. Pointers are typed. The pointer type determines the ± integer step size of the pointer and the permission of pointer dereference operation.
  4. How various operations of pointers are carried out also has its significance.

Next, let's understand the deeper and more detailed connection between pointer and other knowledge:

1, Character pointer

Among the pointer types, we know that one pointer type is character pointer. A created by char * a is a character pointer variable, and char * is the type of character pointer.

Let's go back and forth:

int main()
{
	char ch = 'w';//Create a character variable ch and save w.
	char* p1 = &ch;//Put the address of ch in p1.

	char* p2 = "hello";
	//Here is p2 pointing to the address where hello is stored?
	printf("%c\n", *p);
	return 0;
}

We all know what the first two lines of code mean. Is the third line really an address that stores a few characters in p2? The answer is: No.

The meaning here is that "hello" is actually a constant string, and the function of the expression is to assign the address of the first character "h" of the constant string to p, because dereference to p can only access one byte. That is, the p pointer points to the string, but only the address of h is stored, * p gets h.

Because the string is continuous, you can find the next character when you find the first character. When you need to print the string, you can write printf("%s\n",p). You can print the string only by providing the first character of the string

That is, the character pointer can point to either a character or a string, but it stores the address of the first character of the string. However, since the character pointer points to a constant string, the constant string is stored in the constant area in memory and cannot be modified. We can add const before the character pointer to protect it.

int main()
{

	char* p = "hello";
	*p = 'a';//Prohibit modification
    
    //const char* p = "hello";
    //Add const to limit that a variable is not allowed to be changed.
	return 0;

Exception thrown:

Directly report an error and don't let you run:

In this way, let's look at another piece of code:

int main()
{
    char str1[] = "hello world.";
    char str2[] = "hello world.";
    const char* str3 = "hello world.";
    const char* str4 = "hello world.";

    if (str1 == str2)
        printf("str1 and str2 are same\n");
    else
        printf("str1 and str2 are not same\n");

    if (str3 == str4)
        printf("str3 and str4 are same\n");
    else
        printf("str3 and str4 are not same\n");

    return 0;
}

This code allows us to more clearly understand the difference between character storage and character pointer storage. First, let's look at the running results:

The meaning of this code is to compare the differences between two character arrays and two character pointers when they are stored. It compares the addresses of two arrays or pointers. The result is that one is not equal to two and three is equal to four.

Let's first look at one and two: one and two are an array of characters. We use the array name when comparing. We know that the array name represents the address of the first element. Therefore, str1 here represents the address of the h of the hello world he stores, and str2 represents the address of the h of the hello world he stores. However, the array is created independently, so the addresses of the two h are different!

Then look at three and four: three and four are character pointers. As we mentioned above, the character pointer points to the string, stores the address of the first character, and the string is a constant string (constants cannot be modified). First, create a str3 pointer to the constant string, and then create a str4 pointer to the constant string. Because the string already exists in the constant area, it will not be recreated. So these two 'h' are 'h' of the same address.


So the summary is:

C/C + + will store the constant string in a separate memory area when several pointers. When pointing to the same string, they actually point to the same block of memory. However, when initializing different arrays with the same constant string, different memory blocks will be opened up.

2, Pointer array

Let's recall the pointer array first:

int main()
{
	int arr1[10];
	//An array that holds integer variables is called an integer array

	char arr2[10];
	//The array storing character variables is called character array

	int* arr3[10];
	//Therefore, the array storing pointers is called pointer array

	return 0;
}

So:

Pointer array is an array of pointers.

Then what is the use of pointer arrays? Let's think that the pointer array can store multiple addresses, which can also be understood as storing multiple values. Let's try:

int main()
{
	int a = 10;
    int b = 20;
	int c = 30;
	int d = 40;

	int * arr[] = { &a,&b,&c,&d };

    int i = 0
	for (i = 0; i < 4; i++)
	{
		printf("%d ", *(arr[i]));
	}
     //Print results 10 20 30 40
	return 0;
}

This code first stores the addresses of the four variables in abcd, and then dereference the pointer array when printing to get the value of the variable. In contrast, string printing is much simpler, because the string can find the following characters through the first character, and print the whole string directly, while the shaping array needs to find the address of each character.

Let's take another example. If I want to print multiple arrays, we know that the array name is the address of the first element, and the array can be found by relying on the array name when the array is stored. Let's try with the pointer array:

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

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

	return 0;
}

Here, we first create three arrays, and then create a pointer array to store the array names of the three arrays, that is, the first element addresses of the three arrays. Then, when printing, use the array name as the address of the first element to find the array for printing.

So why can p[i][j] this representation print out these arrays? In fact, we learned that the composition of array and array subscript is actually the dereference of array + subscript, that is, the first element of the array moves forward by subscript units. for instance:

Therefore, p[i] above can be regarded as * (p+i), and p[i][j] can be regarded as * (p[i]+j). Therefore, add ordinal numbers to get the elements behind this array.

Furthermore, we can combine the previous:

int main()
{
	const char* arr[5] = {"abcdef", "bcdefg", "cdefg", "dhijk", "efijk"};
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%s\n", arr[i]);
	}


	return 0;
}

The character pointer and pointer array are linked together. The character pointer array is used to store multiple strings. When printing, because the character pointer stores the address of the first character of the string, and then when we print the string with% s, it will stop when \ 0 is used. Therefore, when we print, the pointer will find the address of the first character of the string, Then print continuously until \ 0 is encountered, which will finish printing the string, and the other strings are the same.

3, Array pointer

1. Definition of array pointer

① Are array pointers pointers? Or an array?

The answer is: pointer.

We learned the pointer array above. It is said that the array storing pointers is called pointer array. Let's reason the same way. In fact, array pointers are pointers to arrays:

int main()
{
	int * p1;
	//Shaping pointer - > pointer to shaping

	char * p2;
	//Character pointer - > pointer to character

	int (*p3)[10];
	//So array pointer - > pointer to array

	return 0;
}

Let's judge which of the following is the pointer array:

int *p1 [10];
int (*p2) [10];

The answer must be the second? The first one is written on it. It is a pointer array. The rest refers to array pointers. The explanation is as follows:

p2 is first combined with * to indicate that p2 is a pointer variable, and then points to an array of 10 integers. So p2 is a pointer to an array, called an array pointer.

Let's have a deeper understanding:

int main()
{
    int a=0;
    int * pa=&a;//The shaping address is stored in the shaping pointer

    char b=0;
    char * pb=&b;//The character address is stored in the character pointer

	int arr[10] = {0};//Create array
	int* p = arr;//The integer pointer points to the address of the first element of the array
	
	int (* parr)[10] = &arr;
    //The array address is stored in the array pointer.
    //*parr is a pointer variable that points to the following [10] shaped array. Together, it is an array pointer.
    //Note: the priority of [] is higher than that of * sign, so () must be added to ensure that parr is combined with * first.
	return 0;
}

We won't talk about the first few pointers. The last parr is first combined with * so it is a pointer, and then points to an array, so this pointer points to an array, which is an array pointer. Moreover, since it is a variable, there are types. For example, the type of int * pa is to remove pa, that is, int *, so the analogy shows that the type of int (* parr)[10] array pointer is int (*) [10].

2. & array name VS array name

Q: What are arr and & arr?

	int main()
{
	int arr[10] = {0};

	printf("%p\n",arr);
	printf("%p\n",&arr[0]);
	printf("%p\n", &arr);
	return 0;
}

We know that the array name is the address of the first element of the array. What is the address array name? So let's study what kind of guy is printed out:

Good guy, three elements print out the same address. How can we distinguish them? We can try to add one to them to distinguish the difference behind them. We know that the size of a variable ± skip depends on its variable type, so we can judge the difference between the three.

int main()
{
	int arr[10] = {0};

	printf("%p    ->    %p  (arr After adding 1)\n",arr,arr+1);
	printf("%p    ->    %p  (&arr[0]After adding 1)\n",&arr[0],&arr[0]+1);
	printf("%p    ->    %p  (&arr After adding 1)\n",&arr,&arr+1);
	return 0;
}

Print results:

From the print results, our arr+1 and & arr [0] + 1 skip the size of an array element, so the array name is the address of the first element of the array. They can be understood to be the same, and the type is int * type. Then & arr+1 skips an array size, so & arr should store the address of the array, and its type is the type of storing the address of the array: int * [10].

But! Q: Is the statement that the array name is the address of the first element completely correct?

The above & arr is not correct, so we can only say that the array name can understand the address of the first element, not that the array name must be the address of the first element, except in the following two cases:

1. Not & array name.
2. Not sizeof (array name).

The first one we have understood above: the array name here represents the whole array, and the first element address is taken out.

Let's take a look at the second case, such as this Code:

{
	int arr[10] = { 0 };
	printf("%d", sizeof(arr));
    //Print result: 40
	return 0;
}

Note: sizeof (arr) the array name here represents the entire array. It calculates the size of the entire array in bytes. Therefore, when sizeof (arr), the array name cannot be directly used as the first element address.

What does it look like to store int * arr[10], that is, the pointer of the pointer array?

int main()
{
	int* a;
	int* (*pa)=&a;

	int* arr[10];
	int* (*parr)[10] = &arr;

	return 0;
}

Here, whether the address of the array is put into an array pointer, and then look at its type. It is int *, so first we need to store the address * parr, and then store the address pointing to the array, which is 10 elements, so it is * parr[10], and then the type of this variable is an integer pointer, so it is int * (*parr)[10].

After understanding the concept, let's compare the following code:

//Code one
int fun_1(int  arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d\n", arr[i]);

	}
}

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

	return 0;
}
//Code two
int fun_2(int * arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d\n", *(arr+i));

	}
}

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

	return 0;
}

In fact, the results of the two are the same, and the meaning of the two is the same. Code one is a common and easy to understand code form, and code two is the real meaning of code one.

Let's look at this code again:

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

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

	return 0; 
}

The print result of this code is the same as that of the first two, but the difference is that there is an array pointer in this code. First, we pass the address of the whole array and store it in the array pointer. Then we can treat the array of arr as a secondary pointer (arr [] level 1, int * level 1), and then get (* arr)[i] through the simplification we know in the previous study.

At this time, we found that the meaning of arr[i] in the first code is the same. It is fully explained here that dereferencing a pointer to an array is to find the array, that is, to get the array name. The array name is the address of the first element, so finally it is equivalent to the address of the first element + I, that is, to get the elements behind the array.

3. Use array pointer correctly

Although the array pointer is used above, it is only to illustrate the relationship and transformation between these, but the above codes are easier than the previous ones. Where will the array pointer be used? Let's take a look:

//Two dimensional array parameter transfer
void print1(int arr[3][5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", arr[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);//arr is the array name, and the array name is the address of the first element
	return 0;
}

Here, we want to call the function to print the two-dimensional array. When we call the function, when we pass the parameters, the first element address of the two-dimensional array is still passed. What is the first element address of the two-dimensional array? We can imagine a two-dimensional array as a one-dimensional array. Each row is an element, so three rows are three elements. Then what we pass is the array element in the first row. So our array pointer version method comes:

//Array pointer
void print2( int(*p)[5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; 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 };
	print2(arr, 3, 5);//arr is the array name, and the array name is the address of the first element
	return 0;
}

When we think of a two-dimensional array as a one-dimensional array. When the first row array is passed, we need to use the array pointer to receive it, and then it can run normally. When we get the elements of the two-dimensional array, * (p+i) is actually the i-th row array, because our p points to the first row array, Then add J, which is the j-th element in this array.

To summarize the entanglement between arrays and pointers:

int arr[5];
//Shaping array

int* parr1[10];
//parr[10] is an array, the element type is int *, and it is a pointer array 

int(*parr2)[10];
//parr2 is an array pointer pointing to 10 elements of the array

int(*parr3[10])[5];
//parr[10] is an array with 10 elements, and int (*)[5] is the type of array pointer
//Each element is an array pointer to an array of five elements.

Put the picture above to make you better understand ha:

In fact, an array stores the elements of the array pointer. The array pointer points to an array with 5 elements.

Well, that's all for this article. Advanced pointers and follow-up blogs, please continue to pay attention. Pay attention to one wave, learn from each other and make common progress.

There is another thing:

Keywords: C C++

Added by rjs34 on Fri, 19 Nov 2021 11:26:46 +0200