|
1 |
| -# Chapter 5: Memory and Pointer |
| 1 | +# Chapter 5: Pointers and Dynamic Memory Management |
| 2 | + |
| 3 | +A pointer in C/C++ can be used to access a specific memory location, and has very good efficiency. It is one of the unique advantages of C/C++, and also is a challenging knowledge point. You must be very careful to use it. Otherwise, some bugs will tend to be introduced. |
2 | 4 |
|
3 | 5 | ## Pointers
|
4 | 6 |
|
5 |
| -## Pointers and Arrays |
| 7 | +Firstly, we should understand that a pointer is a variable for an address. Like other kinds of variables, a pointer has its own value, and the value is an address in the memory. |
6 | 8 |
|
7 |
| -## Allocate memory: C style |
| 9 | +There is an operator `&` which can return the address of a variable or an object. If we have `int num = 10;`, we can get the address of `num` by `&num`. So we can declare a pointer and assign an address to it. |
8 | 10 |
|
9 |
| -## Allocate memory: C++ style |
| 11 | +```C++ |
| 12 | +int num = 10; |
| 13 | +int * p1 = NULL, * p2 = NULL; // declaration two pointers, initialized to 0 |
| 14 | +p1 = # // take the address of num, assign to p1 |
| 15 | +p2 = # // take the address of num, assign to p2 |
| 16 | +*p1 = 20; // assign 20 to num |
| 17 | +*p2 = 30; // assign 20 to num |
| 18 | +``` |
| 19 | + |
| 20 | + |
| 21 | + |
| 22 | +In the previous source code, two pointers `p1` and `p2` are declared. Both of them are assigned the address of `num`. It means the two pointers point to the same memory, and the two pointers regards there is an `int` at that position of the memory. |
| 23 | + |
| 24 | +Another operator here is `*` which is for pointer dereference. It can dereference the pointer a to access the object. The following two lines are equivalent since `p1` is a pointer to `num`. |
| 25 | + |
| 26 | +```C++ |
| 27 | +*p1 = 20; |
| 28 | +num = 20; |
| 29 | +``` |
| 30 | + |
| 31 | +Pointers can not only point to some fundamental types such as `int`, `float`, they can also point to objects of structures or objects, and even functions. In the following code, a structure `stu` is declared and initialized. Then its address is initialized to a pointer `pStu`. The structure and the pointer is illustrated in the figure. |
| 32 | + |
| 33 | +Since `stu` is a structure, we can use `.` like `stu.name` to access its members. We can also use the pointer of `stu` to do that. The operator will be `->`, not be `.`. `stu.name` is equivalent to `pStu->name`. |
| 34 | + |
| 35 | +```C++ |
| 36 | +struct Student |
| 37 | +{ |
| 38 | + char name[4]; |
| 39 | + int born; |
| 40 | + bool male; |
| 41 | +}; |
| 42 | +//declare and initialize a structure |
| 43 | +Student stu = {"Yu", 2000, true}; |
| 44 | +//assign the address of stu to pStu |
| 45 | +Student * pStu = &stu; |
| 46 | +//change members of the structure through pointer pStu |
| 47 | +strncpy(pStu->name, "Li", 4); |
| 48 | +pStu->born = 2001; |
| 49 | +(*pStu).born = 2002; |
| 50 | +pStu->male = false; |
| 51 | +``` |
| 52 | +
|
| 53 | + |
| 54 | +
|
| 55 | +The address value contained in pointer can be printed out. |
| 56 | +```C++ |
| 57 | +printf("Address of stu: %p\n", pStu); //C style |
| 58 | +cout << "Address of stu: " << pStu << endl; //C++ style |
| 59 | +cout << "Address of stu: " << &stu << endl; |
| 60 | +cout << "Address of member name: " << &(pStu->name) << endl; |
| 61 | +cout << "Address of member born: " << &(pStu->born) << endl; |
| 62 | +cout << "Address of member male: " << &(pStu->male) << endl; |
| 63 | +``` |
| 64 | + |
| 65 | +The address is an unsigned integer. It is a 32-bit unsigned integer on most 32-bit OS, and a 64-bit unsigned integer for most current 64-bit systems. You can run the following code to check how many bits your system uses for addresses. |
| 66 | + |
| 67 | +```C++ |
| 68 | +cout << "sizeof(int *) = " << sizeof(int *) << endl; // 4 or 8 |
| 69 | +cout << "sizeof(Student *) = " << sizeof(Student *) << endl; // 4 or 8 |
| 70 | +cout << "sizeof(pStu) = " << sizeof(pStu) << endl; // 4 or 8 |
| 71 | +``` |
| 72 | + |
| 73 | +### Pointers of Pointers |
| 74 | + |
| 75 | +Since a pointer is a variable, the variable will also be stored in memory and has its own address. Then another pointer can also point to this pointer. The figure shows the variable `num` in the example source code. Pointer `p` points to `num`, and Pointer `pp` points to `p`. `*(*pp) = 20` will change the value of number to `20`. |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | +```C++ |
| 80 | +//pointer-pointer.cpp |
| 81 | +#include <iostream> |
| 82 | +using namespace std; |
| 83 | + |
| 84 | +int main() |
| 85 | +{ |
| 86 | + int num = 10; |
| 87 | + int * p = # |
| 88 | + int ** pp = &p; |
| 89 | + *(*pp) = 20; |
| 90 | + |
| 91 | + cout << "num = " << num << endl; |
| 92 | + |
| 93 | + return 0; |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +### Constant pointers |
| 98 | + |
| 99 | +If the `const` type qualifier is put before a fundamental type like `const int num = 1;`, the value of `num` cannot be changed after its initialization. |
| 100 | + |
| 101 | +If `const` is put before the type name of a pointer as in the following example, you cannot change the value that the pointer points to. |
| 102 | + |
| 103 | +```C++ |
| 104 | +int num = 1; |
| 105 | +//You cannot change the value that p1 points to through p1 |
| 106 | +const int * p1 = # |
| 107 | +*p1 = 3; //error |
| 108 | +num = 3; //okay |
| 109 | +``` |
10 | 110 |
|
| 111 | +But you can change the pointer itself. |
11 | 112 |
|
12 |
| -`const int * p; int * const p;` |
| 113 | +```C++ |
| 114 | +p1 = &another; //okay |
| 115 | +``` |
| 116 | + |
| 117 | +`const` can also be put between `*` and the name. If so, the pointer will alway point to that memory, cannot pointer to other places. But the value in that memory can be changed. |
| 118 | + |
| 119 | +```C++ |
| 120 | +//You cannot change value of p2 (address) |
| 121 | +int * const p2 = # |
| 122 | +*p2 = 3; //okay |
| 123 | +p2 = &another; //error |
| 124 | +``` |
| 125 | + |
| 126 | +If two `const` are used as follows, then neither the address nor the value can be changed. |
| 127 | +```C++ |
| 128 | +//You can change neither |
| 129 | +const int* const p3 = # |
| 130 | +*p3 = 3; //error |
| 131 | +p3 = &another; // error |
| 132 | +``` |
| 133 | + |
| 134 | +### Pointers and Arrays |
| 135 | + |
| 136 | +The elements in an array are also stored in memory and have their addresses. The following code shows how to get the addresses of the first 4 elements in an array and assign them to 4 pointer variables. After printing out the addresses, you can find those addresses have an interval of `sizeof(Student)`. Member `born` of the second element can be accessed by `students[1].born` or `p1->born`. |
| 137 | + |
| 138 | +```C++ |
| 139 | +//pointer-array.cpp |
| 140 | +Student students[128]; |
| 141 | +Student * p0 = &students[0]; |
| 142 | +Student * p1 = &students[1]; |
| 143 | +Student * p2 = &students[2]; |
| 144 | +Student * p3 = &students[3]; |
| 145 | + |
| 146 | +printf("p0 = %p\n", p0); |
| 147 | +printf("p1 = %p\n", p1); |
| 148 | +printf("p2 = %p\n", p2); |
| 149 | +printf("p3 = %p\n", p3); |
| 150 | + |
| 151 | +//the same behavior |
| 152 | +students[1].born = 2000; |
| 153 | +p1->born = 2000; |
| 154 | +``` |
| 155 | +
|
| 156 | +You can consider an array name as a pointer. The difference between an array name and a pointer is that an array can only point to the same memory. An array name can be regarded as a constant pointer. |
| 157 | +
|
| 158 | +We print the value of `&students`, `students` and `&students[0]`, it can be found that the three addresses are exactly the same, and are the address of the first element in array `students`. |
| 159 | +
|
| 160 | +```C++ |
| 161 | +//pointer-array.cpp |
| 162 | +printf("&students = %p\n", &students); |
| 163 | +printf("students = %p\n", students); |
| 164 | +printf("&students[0] = %p\n", &students[0]); |
| 165 | +``` |
| 166 | + |
| 167 | +If we assign an array (the address) to a pointer `p`, then `p` can used in an array style (`p[0]`) to access the elements. `p` is a pointer to a position of the memory and regard that position as the starting address of an object. But `p` does not know how many elements are there. We must be very careful with the out-of-bound error. |
| 168 | + |
| 169 | +```C++ |
| 170 | +//pointer-array.cpp |
| 171 | +Student * p = students; |
| 172 | +p[0].born = 2000; |
| 173 | +p[1].born = 2001; |
| 174 | +p[2].born = 2002; |
| 175 | +``` |
| 176 | + |
| 177 | +Since the value of a pointer is an address, an integer number, you can perform arithmetic operations on a pointer. But be careful that the address will not increase 1 after you perform `p + 1`. `p + num` or `num + p` points to the *num-th* element of the array `p`. `p - num` points to the *-num-th* element. The address value changes according to the data type. If the data type is `float *`, then the address will increase 4 bytes after `p + 1` or `p++`. |
| 178 | + |
| 179 | +```C++ |
| 180 | +//arithmetic.cpp |
| 181 | +int numbers[4] = {0, 1, 2, 3}; |
| 182 | +int * p = numbers + 1; // point to the element with value 1 |
| 183 | +p++; // point to the element with value 2 |
| 184 | + |
| 185 | +*p = 20; //change 2 to 20 |
| 186 | +*(p-1) = 10; //change 1 to 10 |
| 187 | +p[1] = 30; //change 3 to 30 |
| 188 | +``` |
| 189 | +
|
| 190 | + |
| 191 | +
|
| 192 | +
|
| 193 | +We do not know how many elements followed the address that a pointer points to, and we also do not know a pointer is from an integer or an integer array. The following code can be compiled successfully, but the memory accessing will be out-of-bound. |
| 194 | +
|
| 195 | +```C++ |
| 196 | +int num = 0; |
| 197 | +int * p = # |
| 198 | +p[-1] = 2; //out of bound |
| 199 | +p[0] = 3; //okay |
| 200 | +*(p+1) = 4; //out of bound |
| 201 | +```` |
| 202 | +
|
| 203 | +
|
| 204 | +## Allocate memory: C style |
| 205 | +
|
| 206 | +## Allocate memory: C++ style |
13 | 207 |
|
14 |
| -## Common mistakes of using a pointer |
15 | 208 |
|
16 |
| -* null pointer, random pointer |
17 |
| -* out of bound |
18 |
| -* memory leak |
19 | 209 |
|
20 | 210 | ## Lab
|
21 | 211 |
|
|
0 commit comments