C language - pointer elementary

 Hello, everyone, today we are going to learn pointers in C language. Today we mainly study elementary pointers, and we will continue to learn advanced pointers later.

Table of contents

1. What is a pointer

2. Pointers and pointer types

2.1 pointer + - integer

2.2 Dereferencing 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?

In C, a pointer is a special variable that can store the memory address of another variable . Pointer variables can be used to access and modify data stored in memory.

The address of a variable is stored in a pointer variable, and the value of the variable pointed to by the pointer can be accessed through the dereference operator (*). For example, an integer variable and a pointer to it can be declared and used as follows:

In this example, we define an  a integer variable named , whose value is  10. Then, we define a  a pointer variable to  point to p, use the address operator  & to get  a the address, and store that address in the pointer  p . * Finally, we output the value of the variable pointed to by the pointer  through the dereference operator  , that is,  the value of  p the variable is output  .a10

Two key points for pointer understanding:

1. The pointer is the number of the smallest unit in memory, that is, the address

2. The pointer in the spoken language usually refers to the pointer variable, which is a variable used to store the memory address

We can use the following diagram to understand memory: 

  • 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, 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 to store an address.

Pointer size:

int main()
{
	printf("%d\n", sizeof(char*));
	printf("%d\n", sizeof(short*));
	printf("%d\n", sizeof(int*));
	printf("%d\n", sizeof(long*));
	printf("%d\n", sizeof(float*));
	printf("%d\n", sizeof(double*));
	return 0;
}

On a 32-bit machine, the size of the pointer is 4 bytes

 On a 64-bit machine, the pointer size is 8 bytes

Summarize 

Pointer variables are used to store addresses, which uniquely identify a memory unit.

The pointer size is 4 bytes on 32-bit platforms and 8 bytes on 64-bit platforms

2. Pointers and pointer types

Here we are discussing:

type of pointer

We all know that variables have different types, integer, floating point, etc. Does the pointer have a type?

To be precise: yes.

The pointer of char* type is used to store the address of char type variable.

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

The pointer of type int* is used to store the address of variable of type int.

What is the meaning of the pointer type?

Function one:

 If a pointer pa of type int* is used to receive the address of a, when *pa is changed, the contents of the 4 bytes of a are changed:

 If the pointer pa of char type is used to receive the address of a, when *pa is changed, only the content in one byte changes:

 Int* pointer dereference accesses 4 bytes, char* type pointer dereference accesses 1 byte

Summary: The type of pointer determines how much authority (how many bytes can be manipulated) when dereferencing the pointer.

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

Left with two:

Observe the following code:

 we discover:

Add 1 to pa of int* type, and the address increases by 4 bits backward

The pb of char* type is added by 1, and the address is increased by 1 bit backward

Summary: The type of pointer determines how much (distance) the pointer takes one step forward or backward.

2.1 pointer + - integer

In the C language, pointers can perform operations of adding and subtracting integers. The meaning of this operation is to add or subtract an integer amount to the address pointed by the pointer, so as to realize the movement of the pointer position.

Here is some sample code:

 In this example, we define an integer array a and a character pointer s.

For an integer pointer p, it points to the first element of array a, and pointer addition can be used to increment p to point to the second element of array a.

For the character pointer s, it points to the first character of a string, and s is increased by 2 through pointer addition, pointing to the third character of the string.

Arrays are accessed using pointers:

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

In this code, the pointer variable p is used to directly access the array elements, which means that the memory location pointed to by p + i is the address of the array arr[i]. The assignment operation is performed on this address to realize the modification of the array value. At the same time, using pointer addition can traverse array elements more conveniently.

It should be noted that the operation of adding and subtracting integers by pointers sometimes crosses the boundary of the array and accesses illegal memory locations. Therefore, when adding and subtracting integers by pointers, it should be ensured that the pointers will not cross the bounds.

2.2 Dereferencing of pointers

This code demonstrates how to operate on the high/low bits of an integer variable n and observe the changes in memory. An integer variable n is defined in the code, and the hexadecimal value 0x11223344 is assigned to it, and then the pointer pc pointing to the char type and the pointer pi pointing to the int type are used to operate on n.

First, point the pointer pc to n and convert it to a pointer to char type. In this way, the pc can operate on n high/low bits one by one, because the char type only occupies one byte. Next, assign the first byte pointed to by pc to 0, which will set the lowest byte of n to 0, and leave the other bytes unchanged.

Then, operate on the integer variable n through the pointer pi. Because pi is a pointer to an integer variable n, it can operate on the entire n. Assigning 0 to the value pointed to by pi will set the entire value of n to 0.

It should be noted that when operating on the high/low bits of the integer variable n, the endianness of the machine must be considered, otherwise it may cause problems in data reading and writing.

Summarize:

The type of the pointer determines how much authority (how many bytes can be manipulated) when dereferencing the pointer. For example: dereferencing a char* pointer can only access one byte, while dereferencing an int* pointer can access four bytes.

3. Wild Pointer

Concept: A wild pointer means that the location pointed to by the pointer is unknown (random, incorrect, and without clear restrictions)

A wild pointer refers to a pointer that has not been initialized or has been released, and it points to an unknown or inaccessible address in memory. The appearance of wild pointers may cause problems such as program crashes and memory leaks.

3.1 Causes of wild pointers

 1. The pointer is not assigned an initial value, or is assigned an unknown value, causing the pointer to point to an unknown address.

 2. The pointer has been released, but the program still tries to access the memory space pointed to by the pointer.

Code one:

Code two:

 This program has the problem of returning a pointer to a local variable, that is, returning a pointer to a local variable to the caller. The specific problem is in  test() the function. The variable defined in the function  a is a local variable. After the function is executed, the system will take back the memory space of this variable, and the memory space requested in the function will be released immediately. Therefore,  a the memory space where the variable is located has been released after the function is executed, and the  &a memory space pointed to by the returned pointer is no longer the memory space of the variable, so this pointer is invalid, called a "dangling pointer".

3. The pointer is out of bounds and points to a location that does not belong to the memory space pointed to by the pointer.

Code one:

 Code two:

In this program, there is a problem that the pointer p is out of bounds, which leads to the memory out of bounds of the arr array. The specific problem is that the number of statement loops in the "for" loop exceeds the length of the array  *(p++) = i;. Illegal access will occur when it is used, which is a common wild pointer error.

The change method can be to check the loop range and modify it to loop 10 times.

3.2 How to avoid wild pointers

1. Pointer initialization

When defining a pointer variable, it is best to initialize it as a null pointer to prevent the pointer from being used without initialization, causing unexpected behavior.

2. Beware of pointer out of bounds

To avoid pointer out of bounds, you should pay attention to the following points:

  1. Confirm the length of the array: When defining the array, ensure that the length of the array is sufficient to avoid out-of-bounds access to the array.

  2. Check the array subscript: When using the array subscript, make sure that the array subscript does not cross the boundary. For example, when the subscript is less than 0 or greater than or equal to the length of the array, the array may exceed the boundary.

  3. Scope of the pointer: Since the life cycle of the pointer variable is very long, it is necessary to ensure that the memory space pointed to by the pointer variable has not been released, so as to avoid the problem that the pointer is out of bounds because the memory space pointed to by the pointer variable has been released.

  4. Scope of use of pointers: When using pointers, ensure the effective range of the memory space pointed to by the pointer variable, and avoid accessing memory other than the memory space pointed to by the pointer, causing the pointer to go out of bounds.

  5. Pointer type: When using a pointer to access a memory block, ensure that the type of the pointer matches the type of the pointed memory block to avoid access out-of-bounds problems caused by type mismatches.

3. The pointer points to the space to release, and set NULL in time

4. Avoid returning the address of a local variable

5. Check the validity of the pointer before using it

Before using a pointer variable, it is better to check if the pointer is null to avoid exceptions or errors thrown by null pointers

4. Pointer arithmetic

4.1 pointer + - integer

This part is the same as 2.1, with new examples added:

By pointing vp to the address of the first element of the array (&values[0]), the entire array can be traversed using a for loop.

The statements in the for loop use pointer addition, making vp point to the next array element each time. Add * before the array element to get the value of the array element pointed to by vp, and assign this value to 0. In this way, vp points to every element in the array and sets them to 0. Note that there is no loop variable defined in the loop, and the condition check and iteration are all in the loop statement, which is also a common way of writing a for loop.

It should be noted that the conditional judgment in the loop is vp < &values[N_VALUES], in the pointer comparison, the pointer actually corresponds to a memory address, the size of the address is compared, and its judgment is based on the ascending order of the addresses , so The address pointed to by vp must be less than &values[N_VALUES] during comparison, otherwise it is out of bounds.

4.2 Pointer-Pointer

Analyze the result of the following code:

 In this code, the address difference between two elements in the array is calculated, and the number of elements between them is obtained by subtracting pointers. Since array elements are stored contiguously in memory, the address difference between them is the number of elements between them.

 Preconditions for pointer-pointer arithmetic:

Pointer subtraction can only be performed if the pointers point to elements of the same array.

4.3 Relational operations on pointers

 Code simplification, this modifies the code as follows:

 

 In fact, the task can be successfully completed on most compilers, but we should still avoid writing this way, because the standard does not guarantee that it will work.

standard regulation:

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

5. Pointers and arrays

The link between pointers and arrays:

In an array, the array name is actually the address of the first element of the array, and the array name == address == pointer

When we know the address of the first element of the array, because the array is stored continuously, the array can be traversed and accessed through the pointer

 

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 address of the secondary pointer is stored in the tertiary pointer:

 

 

7. Array of pointers

 An array of pointers is an array containing pointers, that is, each element of the array is a pointer to a certain type . An array of pointers can conveniently represent and manipulate a set of pointers for storing and accessing multiple data structures.

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "hello world";
	char arr3[] = "cuihua";

	//指针数组
	char* parr[] = { arr1,arr2,arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%s\n", parr[i]);
	}

	return 0;
}

 The following is an example of using an array of int pointers:

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 };
	int i = 0, j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			//printf("%d ", parr[i][j]);
			printf("%d ", *(parr[i] + j));
		}
		printf("\n");
	}
	return 0;
}

 This code demonstrates a simple example containing an array of pointers. In this example, we define three arrays of integers  arr1, arr2 and  arr3, and then create an array of pointers  parr. parrEach element in is a pointer to an integer array, ie  arr1, arr2 or  arr3.

In this example, we can  *(parr[i] + j) access the  th  element i in the integer array pointed to by the th pointer element  byj

This method  parr[i][j] is the same as the effect used, but the expression method is slightly different. The method of using pointers and array subscripts here is a form of pointer arithmetic, that is, first use to  parr[i] obtain a pointer to an integer array, and then use to  *(parr[i] + j) access the array pointed to by the pointer.

 

Guess you like

Origin blog.csdn.net/m0_73648729/article/details/130701751