The strongest C language tutorial in history - pointers (primary)

content

1. What is a pointer?

2. Pointers and pointer types

2.1 Pointer + - Integer

2.2 Dereference of pointers

3. Wild pointer

3.1 Causes of wild pointers

3.2 How to avoid wild pointers

4. Pointer arithmetic

4.1 Pointer + - Integer

4.2 Pointer-Pointer

4.3 Relational operations on pointers

5. Pointers and arrays

 6. Secondary pointer

7. Array of pointers


1. What is a pointer?

What are pointers?

2 points for pointer understanding:

  • 1. A pointer is the number of a smallest unit in memory, that is, an address, which is what we often call a pointer or an address.
  • 2. The pointer that is usually spoken in spoken language usually refers to a pointer variable, which is a variable used to store memory addresses, that is, a pointer is a variable

Summary: A pointer is an address, and a pointer in colloquial language usually refers to a pointer variable.

Then we can understand it like this:

RAM

pointer variable

We can use the & (address operator) to take out the actual address of the variable's memory, and store the address in a variable, which is a pointer variable.

#include <stdio.h>
int main()
{
	int a = 10;//在内存中开辟一块空间
	int* p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
	   //a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
	中,p就是一个之指针变量。
		return 0;
}

Summarize:

A pointer variable, a variable used to store an address. (The value stored in the pointer is treated as an address).

The problem here is:

  • How big is a small unit? (1 byte)
  • How to address?

After careful calculation and weighing, we found that it is more appropriate to give a byte to a corresponding address.

For a 32-bit machine, assuming that there are 32 address lines, then assuming that each address line generates a high level (high voltage) and a low level (low voltage) when addressing, it is (1 or 0);

Then the address generated by the 32 address lines will be:

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000001

...

11111111 11111111 11111111 11111111

There are 2 to 32 addresses here.

Each address identifies a byte, then we can give (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G free for addressing.

In the same way, for a 64-bit machine, if you give 64 address lines, how much space can be addressed, and you can calculate it yourself. The calculation method is as shown above, and will not be calculated here.

Here we understand: On a 32-bit machine, the address is a binary sequence composed of 32 0s or 1s, and the address must be stored in 4 bytes of space, so the size of a pointer variable should be 4 bytes. . Then if on a 64-bit machine, if there are 64 address lines, the size of a pointer variable is 8 bytes in order to store an address.

Summary: Pointers are used to store addresses, and addresses uniquely mark an address space. The size of a pointer is 4 bytes on 32-bit platforms and 8 bytes on 64-bit platforms.

2. Pointers and pointer types

Here we are discussing: we all know the type of pointer, variables have different types, integer, floating point, etc.

Does the pointer have a type? To be precise: yes. When there is code like this:

int num = 10;
p = &num;

To save &num (the address of num) into p, we know that p is a pointer variable, so what is its type? We give the pointer variable the corresponding type.

char  *pc = NULL;
int   *pi = NULL;
short *ps = NULL;
long  *pl = NULL;
float *pf = NULL;
double *pd = NULL;

As you can see here, pointers are defined as: type + * .

In fact: the char* type pointer is to store the address of the char type variable.

A pointer of type short* is used to store the address of a variable of type short.

A pointer of type int* is used to store the address of a variable of type int. What's the point of that pointer type? We will explain below!

2.1 Pointer + - Integer

#include <stdio.h>
int main()
{
 int n = 10;
 char *pc = (char*)&n;
 int *pi = &n;
 
 printf("%p\n", &n);
 printf("%p\n", pc);
 printf("%p\n", pc+1);
 printf("%p\n", pi);
 printf("%p\n", pi+1);
 return  0;
}

Summary: The type of the pointer determines how far (distance) the pointer goes forward or backward.

2.2 Dereference of pointers

#include <stdio.h>
int main()
{
	int n = 0x11223344;
	char* pc = (char*)&n;
	int* pi = &n;
	*pc = 0;   //重点在调试的过程中观察内存的变化。
	*pi = 0;   //重点在调试的过程中观察内存的变化。
	return 0;
}

The following figure shows the memory space of n after executing *pc = 0

As can be seen from the figure, after we execute the statement, only the value corresponding to the space of one byte has been changed!

 The following figure shows the memory space after executing *pi = 0, which is consistent with the pointer type we defined, because the pc we defined is a pointer variable of type char, and char only occupies four bytes of memory space.

 As can be seen from the figure, we changed the value corresponding to the four-byte space this time, which is consistent with the pointer type we defined, because pi is a pointer variable of type int, and int occupies four bytes in memory Space.

Summarize:

 The type of the pointer determines how much authority (how many bytes can be manipulated) to dereference the pointer.

For example: a char* pointer dereference can only access one byte, and an int* pointer dereference can access four bytes.

3. Wild pointer

Concept: A wild pointer is where the pointer points to is unknowable (random, incorrect, unspecified)

3.1 Causes of wild pointers

1. The pointer is not initialized

#include <stdio.h>
int main()
{
    int* p;//局部变量指针未初始化,默认为随机值
    *p = 20;
    return 0;
}

2. Pointer out-of-bounds access

#include <stdio.h>
int main()
{
    int arr[10] = { 0 };
    int* p = arr;
    int i = 0;
    for (i = 0; i <= 11; i++)
    {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
    }
    return 0;
}

3. The space pointed to by the pointer is released (will be discussed later, but will not be discussed here for the time being)

3.2 How to avoid wild pointers

  • 1. Pointer initialization
  • 2. Beware of pointer out of bounds
  • 3. The pointer pointing to the space is released even if it is set to NULL
  • 4. Avoid returning the address of a local variable
  • 5. Check the validity of the pointer before use
#include <stdio.h>
int main()
{
    int* p = NULL;
    //....
    int a = 10;
    p = &a;
    if (p != NULL)
    {
        *p = 20;
    }
    return 0;
}

4. Pointer arithmetic

4.1 Pointer + - Integer

#include<stdio.h>
int main()
{
	int x = 0;
	int* p = &x;
	printf("%p\n", p);
	printf("%p\n", p + 1);
	char* p2 = (char*)&x;
	printf("%p\n", p2);
	printf("%p\n", p2+1);
	return 0;
}

 Pointer+-integer is actually the number of bytes that spans the variable type pointed to by the pointer multiplied by the number we add. For example, in the above example, we first performed a +1 operation on the integer pointer p, and then we added p After +1 is printed in the form of an address, compared with the original p, 4 bytes are added. Similarly, the latter p2 is a pointer of char type. After we perform +1 operation on it, compared with the original p2 address , increased by 1 byte.

4.2 Pointer-Pointer

#include<stdio.h>
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int* p1 = &arr[4];
	int* p2 = &arr[0];
	printf("%d", p1 - p2);
	return 0;
}

The result of the above subtraction is 4. Why is the result of 4? Because the difference between p1 and p2 is 4 integer elements, why do we emphasize the integer element in this place? Because the pointer types on both sides of the operator are integer pointers, in fact , the subtraction of pointer type variables is the subtraction of the address represented by the pointer, and the resulting value is divided by the space occupied by the pointer. The number of bytes is not easy for everyone to understand. It is a little abstract. I will give you a simple example. For example, in the above example, the address value stored in p1 is 16, and the value stored in p2 is 0. , then we divide the 16 obtained by subtracting p1 from p2 by the data type pointed to by p1 and p2, that is, the integer type. The number of bytes occupied by each integer element is 4, so the result after dividing 16 by 4 is 4, that is, the final output result on the screen is 4, so many small partners asked, in the above example, if we cast the type of the two pointers to char type, will the result be the same? It's changed to 16, because according to the reasoning method I gave above, it should be like this, because the number of bytes occupied by the char type is 1, and the result of dividing 16 by 1 is still 16. Next, let's show the code. Is it just like our reasoning that it came out as 16.

 Like our derivation, this confirms that our derivation is correct!

4.3 Relational operations on pointers

Since the address stored in the pointer variable is essentially just a string of data, the size can also be compared! I won't show you an example here, because it is not difficult to understand, but I will emphasize a key point for everyone below!

standard regulation:

Comparison of a pointer to an array element with a pointer to a memory location after the last element of the array is allowed, but comparison with a pointer to a memory location before the first element is not allowed.

5. Pointers and arrays

Let's look at an example:

#include <stdio.h>
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    return 0;
}

operation result:

It can be seen that the array name and the address of the first element of the array are the same.

Conclusion: The array name represents the address of the first element of the array. (except for 2 cases, which are explained in the array chapter)

Then it is possible to write code like this:

int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int *p = arr;//p存放的是数组首元素的地址

Since the array name can be stored in a pointer as an address, it is possible for us to use the pointer to access one.

E.g:

#include <stdio.h>
int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
    int* p = arr; //指针存放数组首元素的地址
    int sz = sizeof(arr) / sizeof(arr[0]);
    for (int i = 0; i < sz; i++)
    {
        printf("&arr[%d] = %p   <====> p+%d = %p\n", i, &arr[i], i, p + i);
    }
    return 0;
}

So p+i actually calculates the address of the subscript i of the array arr.

Then we can access the array directly through the pointer.

as follows:

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	int* p = arr; //指针存放数组首元素的地址
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

 6. Secondary pointer

A pointer variable is also a variable, and a variable has an address. Where is the address of the pointer variable stored? This is the secondary pointer.

The operations for secondary pointers are:

  • *ppa dereferences the address in ppa to find pa, and *ppa actually accesses pa.
int b = 20;
*ppa = &b;//等价于 pa = &b;
  • **ppa first finds pa through *ppa, and then dereferences pa: *pa, that finds a .
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;

7. Array of pointers

Is an array of pointers a pointer or an array?

Answer: It is an array. is an array of pointers.

Arrays We already know integer arrays, character arrays.

int arr1[5];
char arr2[6];

 What about an array of pointers?

int* arr3[5];//是什么?

arr3 is an array with five elements, each element is an integer pointer.

Guess you like

Origin blog.csdn.net/m0_57304511/article/details/122259605