指针笔记
001什么是指针
指针描述了数据在内存中的位置,标示了一个占据存储空间的实体,在这一段空间起始位置的相对距离值。在 C/C++语言中,指针一般被认为是指针变量,指针变量的内容存储的是其指向的对象的首地址,指向的对象可以是变量(指针变量也是变量),数组,函数等占据存储空间的实体。

002指针与指针变量
1 #include <iostream>
2
3 using namespace std;
4
5
6
7
8
9 int main()
10
11 {
12
13 int a=0x123;
14
15 //输出变量a的地址
16
17 printf("a的地址=%p \n",&a);//0061fecc
18
19 //定义指针变量,存储变量a的地址
20
21 int *p=&a;
22
23 printf("p的值是=%p \n",p);//0061fecc
24
25 //通过指针输出变量a的值
26
27 printf("a = %d \n",*p);//0x123
28
29 //输出指针变量p的地址
30
31 printf("p的地址=%p\n",&p);//0061fec8
32
33 return 0;
34
35 }
36
37 /*
38
39 001指针和指针变量
40
41 */
42
43


003指针变量的空间大小
1 #include <iostream>
2
3 using namespace std;
4
5 int main()
6
7 {
8
9
10
11 int *p1=NULL;
12
13 int **p2=NULL;
14
15 int ***p3=NULL;
16
17 int ****p4=NULL;
18
19 char*p5=NULL;
20
21 float*p6=NULL;
22
23 double* p7=NULL;
24
25
26
27 printf("sizeof(p1)=%d \n",sizeof(p1));
28
29 printf("sizeof(p2)=%d \n",sizeof(p2));
30
31 printf("sizeof(p3)=%d \n",sizeof(p3));
32
33 printf("sizeof(p4)=%d \n",sizeof(p4));
34
35 printf("sizeof(p5)=%d \n",sizeof(p5));
36
37 printf("sizeof(p6)=%d \n",sizeof(p6));
38
39 printf("sizeof(p7)=%d \n",sizeof(p7));
40
41
42
43 return 0;
44
45 }
46
47 /*
48
49 * 结论:指针变量的空间大小是固定值,与指向的数据类型无关,只跟编译平台有关。
50
51 * 32位机器占有4个字节,64位机器占有8字节。
52
53 sizeof(p1)=4
54
55 sizeof(p2)=4
56
57 sizeof(p3)=4
58
59 sizeof(p4)=4
60
61 sizeof(p5)=4
62
63 sizeof(p6)=4
64
65 sizeof(p7)=4
66
67 */
68
69
004指针变量的步长(宽度)
1 #include <iostream>
2
3 using namespace std;
4
5 int main()
6
7 {
8
9 int a=0x123;
10
11 int *p1=&a;
12
13 printf("p1 =%p \n",p1);
14
15 printf("p1+1=%p \n",p1+1);
16
17
18
19 char c='b';
20
21 char*p2=&c;
22
23 printf("p2 =%p \n",p2);
24
25 printf("p2+1=%p \n",p2+1);
26
27
28
29 double d=12.343;
30
31 double *p3=&d;
32
33 printf("p3 =%p \n",p3);
34
35 printf("p3+1=%p \n",p3+1);
36
37
38
39
40
41
42
43 int array[3]={1,2,3};
44
45 printf("array =%p \n",array);
46
47 printf("&array =%p \n",&array);
48
49 printf("array+1 =%p \n",array+1);
50
51 printf("&array+1 =%p \n",&array+1);
52
53 printf("&array[0]=%p \n",&array[0]);
54
55 printf("&array[1]=%p \n",&array[1]);
56
57 printf("&array[2]=%p \n",&array[2]);
58
59
60
61 return 0;
62
63 }
64
65 /*
66
67 * 结论:(1)整形变量的步长是4个字节,字符类型的步长是1个字节,双精度浮点类型是8个字节
68
69 * (2)数组名是数组首个元素的地址,加1是加一个元素的长度,&数组名是数组的首地址,加1是整个数组的长度
70
71 * (3)指针的步长是指向的变量类型占有的内存空间的大小。本质是指向下一个变量。
72
73 *
74
75 *
76
77 p1 =0061fec0
78
79 p1+1=0061fec4
80
81
82
83 p2 =0061febf
84
85 p2+1=0061fec0
86
87
88
89 p3 =0061feb0
90
91 p3+1=0061feb8
92
93
94
95 array =0061fea4
96
97 &array =0061fea4
98
99
100
101 array+1 =0061fea8 +4 一个元素的值,
102
103 &array+1 =0061feb0 +12 整个数组的值
104
105
106
107 &array[0]=0061fea4
108
109 &array[1]=0061fea8
110
111 &array[2]=0061feac
112
113 *
114
115 */
005野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的。
1 #include <iostream>
2
3 using namespace std;
4
5
6
7 int*myFunction()
8
9 {
10
11 int a=10;
12
13 return &a;
14
15 }
16
17
18
19 int main()
20
21 {
22
23 //情形1
24
25 int a;
26
27 printf("a =%d \n",a);//17503780 a的值每次都是随机的
28
29
30
31 int *p;
32
33 printf("p= %p \n",p);//00000068
34
35 //printf("*p=%d \n",*p);//有的编译器会报错
36
37
38
39 //情形2
40
41 int array[3]={1,2,3};
42
43 for(int i=0;i<5;i++)//数组越界
44
45 {
46
47 printf("a[i]=%d \n",*(&array[i]));//只是读取,正常
48
49 }
50
51 /*
52
53 a[i]=1
54
55 a[i]=2
56
57 a[i]=3
58
59 a[i]=0
60
61 a[i]=104
62
63 */
64
65 //情形3:
66
67 int*p1=myFunction();
68
69 printf("*p1=%d \n",*p1);//error,不会有输出
70
71 return 0;
72
73 }
74
75 /*
76
77 * 结论:局部变量,如果定义之后没有初始化,其值是随机值。
78
79 * 情形1:使用了未初始化的指针变量
80
81 * 情形2:数组下表越界
82
83 * 情形3:函数返回局部变量的地址—使用了已经销毁的内存
84
85 *
86
87 */
006空指针
1 #include <iostream>
2
3 using namespace std;
4
5
6
7 int main()
8
9 {
10
11 int a=0x123;
12
13 int *p;
14
15 printf("p =%p \n",p);
16
17 //*p=123;//error
18
19 //printf("*p=%d \n",*p);//error
20
21
22
23 //建议定义时候初始化为空指针
24
25 int *p1=NULL;//0x00000000内存地址
26
27 //能不能把0x00000000读取出来?不能读取,也不能赋值,系统保留的地址
28
29 //printf("0x00000000=%d \n",*p1);//error
30
31
32
33 //c++11 使用nullptr
34
35 int *p2=nullptr;
36
37 //使用方法
38
39 if(p1==NULL)
40
41 {
42
43 //给p1指针变量赋值
44
45
46
47 printf("p1是空指针 \n");
48
49 }
50
51 p1=NULL;
52
53
54
55 return 0;
56
57 }
58
59 /*
60
61 * 结论:定义指针变量时候,如果目前不知道指向哪里,指向NULL.
62
63 * 指针使用完毕一定要赋值为NULL.
64
65 *
66
67 */
68
69
007void指针—万能指针类型
1 #include <iostream>
2
3 using namespace std;
4
5
6
7 int main()
8
9 {
10
11 int a=0x123;
12
13 char c='c';
14
15
16
17 int *p1=&a;
18
19 char*p2=&c;
20
21
22
23 printf("p1= %d \n",p1);//根据p1指向int类型,从首地址开始读取4个字节
24
25 printf("p2= %d \n",p2);//根据p2指向char类型,从首地址读取1个字节
26
27
28
29 printf("*p1= %d \n",*p1);
30
31 printf("*p2= %d \n",*p2);
32
33
34
35 void*p3=p2;//ok
36
37 //printf("*p3= %d \n",*p3);//error--不知道读取多少个字节
38
39 printf("*p3= %d \n",*(char*)p3);//c 99
40
41 return 0;
42
43 }
44
45 /*
46
47 * 结论:void类型指针可以用来接收所有的指针类型,但是因为不知道指向哪里,所以没办法解引用。
48
49 * 解引用必须强制类型转换。
50
51 *
52
53 */
54
55
008const指针
1 #include <iostream>
2
3 using namespace std;
4
5
6
7
8
9
10
11 int main()
12
13 {
14
15 //const用来修饰常量
16
17 const int a=0x123;
18
19 printf("a=%d \n",a);
20
21 //a=456;
22
23
24
25 const int*p=&a;//修饰内存空间
26
27 //*p=456;//error,const指针变量不能解引用修改原来地址空间的值
28
29 printf("*p=%d \n",*p);
30
31
32
33 //修改指针本身,不能指向其他内存空间
34
35 int b=0x123;
36
37 int *const p2=&b;//指向不能修改,可以更改内存空间的值,解引用正常
38
39 *p2=0x456;
40
41 printf("*p2=%d \n",*p2);//0x456
42
43 int c=0x456;
44
45 //p2=&c;//error read only
46
47
48
49 //(3)指向和内存空间都不能修改
50
51 const int*const p3=&a;
52
53 printf("*p3=%d \n",*p3);
54
55
56
57 return 0;
58
59 }
60
61 /*
62
63 * 结论:
64
65 * (1)const int*p=&a;//修饰内存空间
66
67 * (2)int *const p2=&b;//指向不能修改,可以更改内存空间的值,解引用正常
68
69 * (3)指向和内存空间都不能修改
70
71 * const int*const p3=&a;
72
73 *
74
75 */
009多级指针—指针的指针
1 #include <stdio.h>
2
3
4
5 int main(int argc, char* argv[])
6
7 {
8
9 int a = 0x123;
10
11 //p1是变量名,p1的类型是存储整形变量的内存地址的类型
12
13 int* p1 = &a;//定义p1存储a的地址
14
15 printf("&a=%p \n", &a);
16
17 printf("p1=%p \n", p1);
18
19
20
21 //定义p2存储p1的地址
22
23 //p2是变量名字,p2的类型是指向 存储整形变量内存地址的类型
24
25 int** p2 = &p1;
26
27 printf("p2=%p \n", p2);
28
29
30
31 //定义p3存储p2的地址
32
33 int*** p3 = &p2;
34
35 printf("p3=%p \n", p3);
36
37
38
39 //解引用输出
40
41 printf("a=%d \n", a);
42
43 printf("*p1=%d \n", *p1);
44
45 printf("**p2=%d \n", **p2);
46
47 printf("***p3=%d \n", ***p3);
48
49 return 0;
50
51 }
52
53 /*
54
55 * 指针有数据类型,指针的数据类型就是地址类型+它指向的数据类型
56
57 *
58
59 &a=00000099B1CFF6C4
60
61 p1=00000099B1CFF6C4
62
63 p2=00000099B1CFF6E8
64
65 p3=00000099B1CFF708
66
67 a=291
68
69 *p1=291
70
71 **p2=291
72
73 ***p3=291
74
75
76
77 */
78
79
010 用指针操作数组
1 #include <stdio.h>
2
3
4
5 int main(int argc, char* argv[])
6
7 {
8
9 int a[3] = { 11,22,33 };
10
11 printf("a =%p \n", a);//数组名,就是数组首个元素的地址,等价于&a[0]
12
13 printf("&a =%p \n", &a);//整个数组的指针,+1跳过整个数组
14
15 printf("&a[0]=%p \n", &a[0]);//数组首个元素的地址
16
17
18
19 printf("a+1 =%p \n", a+1);
20
21 printf("&a+1 =%p \n", &a+1);
22
23 printf("&a[0]+1=%p \n", &a[0]+1);
24
25
26
27 //数组名就是数组首元素的地址
28
29 printf("sizeof(a)=%d \n", sizeof(a));//12,整个数组大小
30
31 printf("sizeof(&a)=%d \n", sizeof(&a));//8 指针的大小
32
33 int* p = a;
34
35 printf("sizeof(p)=%d \n", sizeof(p));//64位占有8个字节,地址类型
36
37
38
39
40
41 //用指针操作数组
42
43 for (int i = 0; i < 3; i++)
44
45 {
46
47 printf("数组元素地址=%p,a[%d]=%d \n", &a[i], i, a[i]);
48
49 //printf("数组元素地址=%p,a[%d]=%d \n", &a[i], i, *(&a[i]));//ok
50
51 }
52
53
54
55
56
57 return 0;
58
59 }
60
61
62
63 /*
64
65 a =000000E4B697FAA8
66
67 &a =000000E4B697FAA8
68
69 &a[0]=000000E4B697FAA8
70
71
72
73 a+1 =000000E4B697FAAC //+4
74
75 &a+1 =000000E4B697FAB4 //+12
76
77 &a[0]+1=000000E4B697FAAC //+4
78
79 sizeof(a)=12
80
81 sizeof(&a)=8
82
83 sizeof(p)=8
84
85
86
87 数组元素地址=000000E4B697FAA8,a[0]=11
88
89 数组元素地址=000000E4B697FAAC,a[1]=22
90
91 数组元素地址=000000E4B697FAB0,a[2]=33
92
93
94
95
96
97 */
98
99
011用指针操作数组—多维数组
1 #include <stdio.h>
2
3
4
5 int main(int argc, char* argv[])
6
7 {
8
9 int a[2][3] = { {1,2,3},{4,5,6} };
10
11
12
13 printf("a =%p \n", a);//等价于&a[0]
14
15 printf("&a =%p \n", &a);//整个数组的指针,
16
17 printf("a[0] =%p \n", a[0]);//数组中首个元素a[0][0]的地址
18
19 printf("&a[0] =%p \n",&a[0]);//数组中首个元素a[0][0]的地址
20
21 printf("&a[0][0]=%p \n", &a[0][0]);//数组中首个元素a[0][0]的地址
22
23
24
25 printf("sizeof(a)=%d \n", sizeof(a));//整个数组 24
26
27 printf("sizeof(a[0])=%d \n", sizeof(a[0]));//一维度12
28
29 printf("sizeof(a[0][0])=%d \n", sizeof(a[0][0]));//元素的长度 4
30
31
32
33
34
35 printf("a+1 =%p \n", a+1);//等价于&a[0] +12 一个维度
36
37 printf("&a+1 =%p \n", &a+1);//整个数组的指针, +24,跳过整个数组
38
39 printf("a[0]+1 =%p \n", a[0]+1);//数组中首个元素a[0][0]的地址
40
41 printf("&a[0]+1 =%p \n", &a[0]+1);//数组中首个元素a[0]的地址
42
43 printf("&a[0][0]+1=%p \n", &a[0][0]+1);//数组中首个元素a[0][0]的地址
44
45
46
47 return 0;
48
49 }
50
51 /*
52
53 a =0000001CEDCFFB08
54
55 &a =0000001CEDCFFB08
56
57 a[0] =0000001CEDCFFB08
58
59 &a[0] =0000001CEDCFFB08
60
61 &a[0][0]=0000001CEDCFFB08
62
63 sizeof(a)=24
64
65 sizeof(a[0])=12
66
67 sizeof(a[0][0])=4
68
69 a+1 =0000001CEDCFFB14 +12
70
71 &a+1 =0000001CEDCFFB20 +24
72
73 a[0]+1 =0000001CEDCFFB0C +4
74
75 &a[0]+1 =0000001CEDCFFB14 +12
76
77 &a[0][0]+1=0000001CEDCFFB0C +4
78
79
80
81
82
83 规定:如果将二维数组作为参数传递给函数,那么在函数的参数声明中必须指明数组的列数,数组的行数没有太大关系,可以指定也可以不指定。因为函数调用时传递的是一个指针,它指向由行向量够成的一维数组。因此二维数组作为函数参数正确写法如下所示:
84
85
86
87 void Func(int array[3][10]);
88
89
90
91 void Func(int array[ ][10]);
92
93
94
95 因为数组的行数无关紧要,所以还可以写成如下形式:
96
97
98
99 void Func(int (*array)[10]); 注意*array需要用括号括起来。--数组指针类型
100
101 这种形式的声明参数是一个指针,它指向具有10个元素的一维数组。因为[]的优先级比*的优先级高,故*array必须用括号括起来,否则变成了
102
103 void Func(int *array[10]);
104
105 这时候参数相当于是声明了一个数组,该数组有10个元素,其中每个元素都是一个指向整型对象的指针。
106
107
108
109 */
110
111
012指针运算—指针与整数的运算
指针只能做加减运算,没有乘除运算,并且要严防指针越界。
1 #include<stdio.h>
2
3
4
5 int main(void)
6
7 {
8
9 int a[4] = { 11,22,33,44 };
10
11 printf("a[%d]=%d,address =%p \n", 0, a[0], a);
12
13 printf("a[%d]=%d,address =%p \n", 1, a[1], a + 1);
14
15 printf("a[%d]=%d,address =%p \n", 2, a[2], a + 2);
16
17 printf("a[%d]=%d,address =%p \n", 3, a[3], a + 3);
18
19
20
21 printf("=======================================\n");
22
23 //指针减法
24
25 int* p1 = &a[3];
26
27 //这里输出的是33,先执行右侧的语句
28
29 printf("p[%d]=%d,address =%p \n", 3, *p1, p1--);
30
31 printf("p[%d]=%d,address =%p \n", 2, *p1, p1--);
32
33 printf("p[%d]=%d,address =%p \n", 1, *p1, p1--);
34
35 printf("p[%d]=%d,address =%p \n", 0, *p1, p1--);
36
37
38
39
40
41 return 0;
42
43 }
44
45 /*
46
47 结论:指针的加减是以指向的数据类型为步长的。
48
49
50
51 a[0]=11,address =000000A713F6F658
52
53 a[1]=22,address =000000A713F6F65C
54
55 a[2]=33,address =000000A713F6F660
56
57 a[3]=44,address =000000A713F6F664
58
59 =======================================
60
61 p[3]=33,address =000000A713F6F664
62
63 p[2]=22,address =000000A713F6F660
64
65 p[1]=11,address =000000A713F6F65C
66
67 p[0]=-858993460,address =000000A713F6F658
68
69 */
013指针运算-指针与指针的运算
1 #include<stdio.h>
2
3
4
5 int main(void)
6
7 {
8
9 int a[4] = { 11,22,33,44 };
10
11 //(1)比较运算 指针可以判断是否相等,
12
13 int* p1 = a;
14
15 int* p2 = &a[0];
16
17 int* p3 = &a[2];
18
19 if (p1 == p2)
20
21 {
22
23 printf("p1==p2 \n");
24
25 }
26
27 if (p3 > p2)
28
29 {
30
31 printf("p3>p2 \n");
32
33 }
34
35 //printf("p1+p2=%p \n",p1+p2);
36
37
38
39 return 0;
40
41 }
42
43 /*
44
45 结论:
46
47 (1)指针运算的前提:相同类型的指针进行比较
48
49 (2)指针与指针相加编译不能通过。
50
51 (3)指针相减求出的是两个元素在内存之间的距离。
52
53 (4)指针运算严防指针越界,指针越界一般读取没有问题,但是写有问题。
54
55
56
57 */
013指针数组
数组元素都是指针类型的数据的数组。
1 #include<stdio.h>
2
3
4
5 int main(void)
6
7 {
8
9 int a[4] = { 11,22,33,44 };
10
11 int *p = a;
12
13 printf("sizeof(a)=%d \n", sizeof(a));//16--整个数组的长度
14
15 printf("sizeof(p)=%d \n", sizeof(p));//64位系统 8
16
17
18
19 printf("*p =%d \n", *p);
20
21 printf("*(p+1)=%d \n", *(p+1));
22
23 printf("*(p+2)=%d \n", *(p+2));
24
25 printf("*(p+3)=%d \n", *(p+3));
26
27
28
29 printf("*a =%d \n", *a);
30
31 printf("*(a+1)=%d \n", *(a + 1));
32
33 printf("*(a+2)=%d \n", *(a + 2));
34
35 printf("*(a+3)=%d \n", *(a + 3));
36
37
38
39 //例子:指针和数组的关系
40
41 int number = 10;
42
43 int* p1 = &number;
44
45 printf("p1[0]=%d \n",p1[0]);//可以通过数组形式访问变量p1[0]等价于*(p1+0)
46
47
48
49 return 0;
50
51 }
52
53 /*
54
55 结论:(1)数组名是指向数组首元素的指针,步长是指向下一个元素.
56
57
58
59 */
60
61
62
63 #include<stdio.h>
64
65
66
67 int main(void)
68
69 {
70
71 int a = 11, b = 22, c = 33;
72
73 int arr[3] = { a,b,c };//数组
74
75 //定义并初始化指针数组
76
77 int* pArr[3] = { &a,&b,&c };
78
79 //输出元素的值
80
81 printf("pArr[0]=%p \n", pArr[0]);
82
83 printf("pArr[1]=%p \n", pArr[1]);
84
85 printf("pArr[2]=%p \n", pArr[2]);
86
87
88
89 printf("pArr[0]=%p \n", *(pArr + 0));
90
91 printf("pArr[1]=%p \n", *(pArr + 1));
92
93 printf("pArr[2]=%p \n", *(pArr + 2));
94
95
96
97
98
99 return 0;
100
101 }
102
103 /*
104
105 结论:(1)指针数组的定义,数组元素都是指针(地址)
106
107
108
109 */
110
111
112
113 #include<stdio.h>
114
115
116
117 int main(void)
118
119 {
120
121 int a = 11, b = 22, c = 33;
122
123 int arr[3] = { a,b,c };//数组
124
125 //定义并初始化指针数组
126
127 int* pArr[3] = { &a,&b,&c };
128
129 //输出元素的值
130
131 printf("pArr[0]=%p,*pArr[0]=%d \n", pArr[0],*pArr[0]);
132
133 printf("pArr[1]=%p,*pArr[1]=%d \n", pArr[1],*pArr[1]);
134
135 printf("pArr[2]=%p,*pArr[2]=%d \n", pArr[2],*pArr[2]);
136
137
138
139 int** p1 = pArr;
140
141 printf("*p1=%p,**p1=%d \n", *p1, **p1);
142
143 p1++;
144
145 printf("*p1=%p,**p1=%d \n", *p1, **p1);
146
147 p1++;
148
149 printf("*p1=%p,**p1=%d \n", *p1, **p1);
150
151
152
153 return 0;
154
155 }
156
157 /*
158
159 结论:(1)指针数组的数组名是一个二级指针,可以通过二级指针来访问指针数组的元素。
160
161
162
163 */
014指针作为函数参数
1 #include<stdio.h>
2
3 void Swap(int *a, int *b)
4
5 {
6
7 int c = *b;
8
9 *b = *a;
10
11 *a = c;
12
13 }
14
15
16
17 int main(void)
18
19 {
20
21 int a = 11;
22
23 int b = 22;
24
25 Swap(&a, &b);
26
27 printf("a=%d \n", a);
28
29 printf("b=%d \n", b);
30
31
32
33 return 0;
34
35 }
015一维数组作为函数参数
1 #include<stdio.h>
2
3
4
5 //void myFunc(int* p, int len)//ok
6
7 void myFunc(int p[3], int len)
8
9 {
10
11 //printf("数组首地址=%p,数组的长度=%d \n", p, sizeof(p) / sizeof(p[0]));//这里的p已经退化为一个指针
12
13 for (int i = 0; i < len; i++)
14
15 {
16
17 printf("p[%d]=%d \n", i, p[i]);
18
19 }
20
21
22
23 }
24
25
26
27 int main(void)
28
29 {
30
31 int a[3] = { 11,22,33 };
32
33 //这里使用sizeof(a)获取的是整个数组的长度
34
35 printf("数组首地址=%p,数组的长度=%d \n", a, sizeof(a) / sizeof(a[0]));
36
37
38
39 myFunc(a, sizeof(a) / sizeof(a[0]));
40
41
42
43
44
45 return 0;
46
47 }
48
49 /*
50
51 结论:
52
53 数组首地址=000000E4FECFF8B8,数组的长度=3
54
55 数组首地址=000000E4FECFF8B8,数组的长度=2
56
57
58
59 数组名作为函数参数,已经退化为一个指针,sizeof(数组名)得到的是指针的长度
60
61
62
63 */
64
65
016指针作为函数的返回值
1 #include<stdio.h>
2
3
4
5 /*
6
7 (1)函数执行完毕,a的内存控件就释放了
8
9 解决办法:(1)将a定义为全局变量。
10
11 (2)使用malloc分配内存空间
12
13 */
14
15 int* myFunc()
16
17 {
18
19 int a = 123;
20
21 return &a;
22
23 }
24
25
26
27 int main(void)
28
29 {
30
31 int* p = myFunc();
32
33 printf("*p=%d \n", *p);
34
35
36
37 return 0;
38
39 }
40
41 /*
42
43 结论:如果使用指针作为函数返回值,要确保函数调用结束指针空间不会被销毁,否则会变成野指针。
44
45
46
47
48
49 */
017字符数组指针
1 #include<stdio.h>
2
3 #include<string.h>
4
5
6
7 int main(void)
8
9 {
10
11 char c = 'a';
12
13 char* p1 = &c;
14
15 printf("*p1=%c \n", *p1);
16
17
18
19 //定义字符数组
20
21 char arr[] = { 'a','b','c','d' };
22
23 char arr02[] = "abcd";//这里会自动添加 \0结束标志
24
25 char* p2 = arr;
26
27 printf("%c %s \n",*p2,p2);//a abcd烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫赭瘹W
28
29
30
31 //写操作
32
33 *p2='A';
34
35 printf("%s \n",p2);//Abcd烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫豇o憹
36
37
38
39 char* p3 = arr02;
40
41 printf("%s \n", p3);//abcd
42
43
44
45 printf("arr=%d,arr02=%d \n", strlen(arr), strlen(arr02)); //36, p3 = 4
46
47 printf("arr=%d,arr02=%d \n", sizeof(arr), sizeof(arr02));//p2=4,p3=5
48
49
50
51 return 0;
52
53 }
54
55 /*
56
57 结论:
58
59 (1)c语言没有字符串,用字符数组模拟字符串,当出现\0,表示字符串结束
60
61 (2)strlen计算的是有效的字符个数,sizeof计算的是占有的内存字节数,包含\0
62
63
64
65 */
66
67
68
69 const char* p = "abc";
70
71 printf("p=%s \n", p);
72
73 /*
74
75 (1)在栈区定义一个char*类型的变量p
76
77 (2)在常量区开辟一个空间,存储字符串“abc”
78
79 (3)将常量区“abc”对应的地址赋值给p,可以修改p的指向,但是不能修改p指向的值
80
81 */
018字符指针数组
1 #include<stdio.h>
2
3 #include<string.h>
4
5
6
7 int main(void)
8
9 {
10
11 char a[] = "abc";//每一个
12
13 char b[] = "def";
14
15 char c[] = "xyz";
16
17
18
19 char* p[3] = { a,b,c };
20
21 printf("sizeof(p)=%d \n", sizeof(p));//24 元素类型是char*类型
22
23
24
25 for (int i = 0; i < 3; i++)
26
27 {
28
29 printf("p[%d]=%p \n", i, p[i]);
30
31 }
32
33 /*
34
35 p[0]=000000FD022FF974
36
37 p[1]=000000FD022FF994
38
39 p[2]=000000FD022FF9B4
40
41 */
42
43 //指针数组可以通过二维指针来访问
44
45 char** pp = p;
46
47 printf("sizeof(pp)=%d \n", sizeof(pp));//64位系统占有8位
48
49
50
51 for (int i = 0; i < 3; i++)
52
53 {
54
55 //通过二级指针输出内存的值
56
57 printf("值=%s \n", *(pp++));
58
59 //printf("值=%s \n", pp[i]);//ok
60
61 }
62
63
64
65
66
67
68
69 return 0;
70
71 }
72
73
019字符指针数组初始化过程
1 #include<stdio.h>
2
3
4
5 int main(void)
6
7 {
8
9 char a = 'a';
10
11 char b = 'b';
12
13 char* p = &a;//字符指针类型变量
14
15 char c[] = { a,b,'c' };
16
17 //字符类型数组,此处的a,b与上面的a,b无关,只是做了数据的拷贝,内存空间是独立的
18
19
20
21 printf("%p,%p,%c,%c \n", &a, &b, a, b);
22
23 printf("%p,%p,%c,%c \n", &c[0], &c[1], c[0], c[1]);
24
25
26
27 //字符指针类型的数组
28
29 char* p2[] = { &a,p,c };
30
31 //p2[0]与 p2[1] 的值是相等的,解引用后也是相等的a
32
33 //p2[2]的值是不一样的,但是解引用之后也是一样的a
34
35
36
37 for (int i = 0; i < 3; i++)
38
39 {
40
41 printf("%p,%p,%c \n", &p2[i], p2[i], *p2[i]);
42
43 }
44
45
46
47 return 0;
48
49 }
50
51 /*
52
53 结论:使用数组,和指针变量初始化字符指针数组
54
55
56
57 00000099FAB4F8A4,00000099FAB4F8C4,a,b
58
59 00000099FAB4F904,00000099FAB4F905,a,b
60
61
62
63 00000099FAB4F928,00000099FAB4F8A4,a
64
65 00000099FAB4F930,00000099FAB4F8A4,a
66
67 00000099FAB4F938,00000099FAB4F904,a
68
69
70
71 */
72
73
74
75 //字符常量初始化字符指针数组
76
77 #include<stdio.h>
78
79
80
81 int main(void)
82
83 {
84
85 //局部变量放在栈区
86
87 //常量是放在常量区
88
89 //有双引号的,称之为字符串常量,所以,双引号里面的内容,一定会在常量区有一份
90
91
92
93 char c[] = "abc"; //字符类型数组,定义
94
95 //初始化过程
96
97 //1 先在栈区定定义一个 char 数组的变量,并分配空间
98
99 //2 去常量区找,有没有 abc\0,如果没有,就在常量区写一份,然后再往栈区拷贝一份,
100
101 //如果常量区有,就直接拷贝回来
102
103
104
105 //d[]="def";//数组不能这样赋值
106
107 //d[0]="d";//双引号后面有\0结尾 //char x[2]="ab";//长度不够
108
109 //d[0]='d';//正确
110
111
112
113 const char* p1 = "abc";
114
115 const char* p2 = "abc";
116
117 const char* p3 = "def";
118
119 //1 先在栈区定定义一个 char *的变量,并分配空间
120
121 //2 去常量区找,有没有 abc\0,如果没有,就在常量区写一份,
122
123 //并将常量区对应的指针地址给到栈区的变量,如果有,直接拿常量区指针地址
124
125 printf("p1=%p \n", p1);
126
127 printf("p2=%p \n", p2);
128
129 printf("p3=%p \n", p3);
130
131 /*
132
133 p1=00007FF652B39CA4
134
135 p2=00007FF652B39CA4
136
137 p3=00007FF652B39CA8
138
139 结论:p1==p2
140
141 */
142
143 //数组就会拷贝,指针地址不同(分配了内存空间,将数据拷贝进去)
144
145 char x[] = "abc";
146
147 char y[] = "abc";
148
149 printf("x=%p \n", x);
150
151 printf("y=%p \n", y);
152
153 /*
154
155 x=000000D83ED2F784
156
157 y=000000D83ED2F7A4
158
159 */
160
161
162
163 return 0;
164
165 }
166
167
168
169 #include<stdio.h>
170
171
172
173 int main(void)
174
175 {
176
177 //只要字符串在常量区存在,则新的指针变量指向该字符串的地址时,都是使用同一个常量区的地址
178
179 const char* p1 = "abc";
180
181 const char* p2[] = { "abc","abc",p1 };
182
183
184
185 for (int i = 0; i < 3; i++)
186
187 {
188
189 printf("p2[%d]=%p \n", i, p2[i]);
190
191 /*
192
193 p2[0]=00007FF6FC579CA4
194
195 p2[1]=00007FF6FC579CA4
196
197 p2[2]=00007FF6FC579CA4
198
199 */
200
201 }
202
203
204
205 char c2[] = "abc";
206
207 const char* p3[] = { c2,"abc","abc",p1 };
208
209
210
211 for (int i = 0; i < 4; i++)
212
213 {
214
215 printf("p3[%d]=%p \n", i, p3[i]);
216
217 /*
218
219 p3[0]=000000C0FFAFF7C4 //值不同
220
221 p3[1]=00007FF6700C9CA4
222
223 p3[2]=00007FF6700C9CA4
224
225 p3[3]=00007FF6700C9CA4
226
227 */
228
229 }
230
231
232
233 return 0;
234
235 }
020指针数组和数组指针
1 #include<stdio.h>
2
3
4
5 int main(void)
6
7 {
8
9 char a = 'a';
10
11 char b = 'b';
12
13 char* p = &a;
14
15 char c[] = { a,b,'c' };
16
17 char* p2[] = { &a,p,c };//字符指针类型的数组 指针数组
18
19
20
21 //数组指针
22
23 char(*p1)[3] = &c;//p1是数组指针,指向的是一个有着3个元素的char类型的指针
24
25 printf("c=%p,p1=%p \n", c, p1);
26
27 printf("c+1=%p,p1+1=%p \n", c+1, p1+1);
28
29 /*
30
31 c=00000072D46FF824,p1=00000072D46FF824
32
33 c+1=00000072D46FF825,p1+1=00000072D46FF827
34
35 */
36
37
38
39 for (int i = 0; i < 3; i++)
40
41 {
42
43 printf("%p--%c \n", &((*p1)[i]), (*p1)[i]);//*p1==>c
44
45
46
47 /*
48
49 00000072D46FF824--a
50
51 00000072D46FF825--b
52
53 00000072D46FF826--c
54
55 */
56
57 }
58
59
60
61 char* p3 = c;//字符指针类型变量,其指向的是数组第一个元素,相当于&c[0]
62
63 for (int i = 0; i < 3; i++)
64
65 {
66
67 printf("%p--%c \n", (p3 + i), *(p3 + i));
68
69
70
71 /*
72
73 00000072D46FF824--a
74
75 00000072D46FF825--b
76
77 00000072D46FF826--c
78
79 */
80
81
82
83 }
84
85
86
87 return 0;
88
89 }
90
91 /*
92
93 结论:数组是一种数据类型,数据类型可以定义
94
95 char(*p1)[3] = &c
96
97
98
99 c=00000072D46FF824,p1=00000072D46FF824
100
101 c+1=00000072D46FF825,p1+1=00000072D46FF827
102
103
104
105 00000072D46FF824--a
106
107 00000072D46FF825--b
108
109 00000072D46FF826--c
110
111
112
113 00000072D46FF824--a
114
115 00000072D46FF825--b
116
117 00000072D46FF826--c
118
119
120
121 */
122
123
021函数指针
1 #include<stdio.h>
2
3
4
5 void myFunc()
6
7 {
8
9 printf("hello world \n");
10
11 }
12
13 //有参数
14
15 int myAdd(int number01, int number02)
16
17 {
18
19 return number01 + number02;
20
21 }
22
23
24
25 int main(void)
26
27 {
28
29 //获取函数指针
30
31 void (*pFunc)() = &myFunc;
32
33 //&myFunc=00007FF7D45C1163,pFunc=00007FF7D45C1163
34
35 printf("&myFunc=%p,pFunc=%p \n", &myFunc, pFunc);
36
37
38
39 //用指针函数调用函数--解引用
40
41 (*pFunc)();
42
43
44
45 //有参函数
46
47 int (*pMyAdd)(int, int) = &myAdd;
48
49 //int sum = pMyAdd(11, 22);//ok
50
51 int sum = (*pMyAdd)(11, 22);
52
53 printf("sum=%d \n", sum);
54
55
56
57 return 0;
58
59 }
60
61 /*
62
63 结论:函数也有数据类型,所有函数指针也可以定义变量。
64
65
66
67 函数指针定义形式:
68
69 函数返回值类型(*指针变量名)(函数参数列表)
70
71 调用方法:(*指针变量名)(参数列表)
72
73
74
75 */
76
77
022函数指针数组
本质上是数组,里面的元素都是函数指针
函数指针数组={函数指针,函数指针,函数指针};
1 #include<stdio.h>
2
3
4
5 int myFunc01(int a, int b) { return a + b; };
6
7 int myFunc02(int a, int b) { return a - b; };
8
9 int myFunc03(int a, int b) { return a * b; };
10
11 int myFunc04(int a, int b) { return a / b; };
12
13
14
15 int main(void)
16
17 {
18
19 int a = 22;
20
21 int b = 11;
22
23 int (*p1)(int, int) = &myFunc01;
24
25 int (*p2)(int, int) = &myFunc02;
26
27 int (*p3)(int, int) = &myFunc03;
28
29 int (*p4)(int, int) = &myFunc04;
30
31
32
33 //定义函数指针数组
34
35 int (*pArr[4])(int, int) = { p1,p2,p3,p4 };
36
37 for (int i = 0; i < 4; i++)
38
39 {
40
41 printf("(*pArr[i])(a, b)=%d \n", (*pArr[i])(a, b));
42
43 }
44
45 return 0;
46
47 }
48
49 /*
50
51 结论:定义函数指针数组方法
52
53 */
54
55
023函数指针底层原理解析
1 #include<stdio.h>
2
3
4
5 void myFunc()
6
7 {
8
9 printf("hello world \n");
10
11 }
12
13
14
15 int main(void)
16
17 {
18
19 void (*p1)() = &myFunc;
20
21 void (*p2)() = &myFunc;
22
23
24
25 printf("p1==p2 %d \n", p1 == p2);//1
26
27 printf("&myFunc==myFunc %d \n", &myFunc == myFunc);//1
28
29
30
31 myFunc();//每次调用函数是把代码拷贝到这里执行还是跳转过去执行??
32
33 //下面的写法能调用,不推荐写
34
35 (*myFunc)();//ok
36
37 (&myFunc)();//ok
38
39 (*p1)();//ok
40
41 (**p1)();//ok
42
43 (***p1)();//ok
44
45 (****p1)();//ok
46
47 return 0;
48
49 }
50
51 /*
52
53 * 问题:怎么找到函数的代码块执行的?
54
55 * 根据函数地址定位到内存地址,()表示执行对应的代码块。
56
57 结论:函数名就是一个隐式的指针,函数名在程序里面,就是指针地址的别名。
58
59
60
61 */
62
63
024main函数参数和回调函数
1 #include<stdio.h>
2
3
4
5 int myFunc1(int a, int b) { return a + b; }
6
7 int myFunc2(int a, int b) { return a - b; }
8
9
10
11 //测试函数指针类型的函数--函数指针来实现
12
13 int testFunc(int (*pFunc)(int, int),int a,int b)
14
15 {
16
17 return (*pFunc)(a, b);
18
19 }
20
21
22
23 int main(int argc,char *argv[])
24
25 {
26
27 //第一个参数是程序的文件名,省下的参数是程序需要传递的参数
28
29 printf("argc=%d \n", argc);
30
31 printf("argv[0]=%s \n", argv[0]);
32
33
34
35 //回调函数
36
37 int a = 11;
38
39 int b = 22;
40
41 printf("通过回调函数调用=%d \n", testFunc(myFunc1, a, b));
42
43 printf("通过回调函数调用=%d \n", testFunc(myFunc2, a, b));
44
45
46
47 return 0;
48
49 }
50
51 /*
52
53 结论:
54
55 (1)main函数参数第一个是传递的参数个数,第二个是一个字符指针数组
56
57 (2)回调函数需要通过函数指针来实现
58
59 解耦合--让函数开发者和函数使用者可以分开实现。
60
61 2023年5月6日 09:26:19
62
63 */
025结构体内存对齐
1 #include<stdio.h>
2
3 struct T1
4
5 {
6
7 int a;
8
9 int* p;
10
11 char c;
12
13 };
14
15
16
17 struct T2
18
19 {
20
21 char a;
22
23 };
24
25
26
27 struct T3
28
29 {
30
31 int* a;
32
33 };
34
35
36
37 int main(void)
38
39 {
40
41 printf("sizeof(T1)=%d \n", sizeof(T1));//24
42
43 printf("sizeof(T2)=%d \n", sizeof(T2));//1
44
45 printf("sizeof(T3)=%d \n", sizeof(T3));//8
46
47
48
49 return 0;
50
51 }
52
53 /*
54
55 结论:结构体一般默认是4K对齐
56
57 结构体整体大小与所有成员的大小有关,但是并不是简单的相加。这种现象我们称为内存对齐。
58
59
60
61 1 结构体变量的 起始地址 要能被其最大的成员整除
62
63 2 结构体变量的 总体大小 要能被其最大的成员整除
64
65 3 结构体变量的 每个成员 相对于开始地址的偏移量,要被其自身大小整除,如果长度不够,则在前一个成员后面补
66
67 */
68
69
026结构体指针
1 #include<stdio.h>
2
3 #include<string.h>
4
5 #include<stdlib.h>
6
7 struct T1
8
9 {
10
11 int a;
12
13 char name[10];
14
15
16
17 };
18
19 int main(void)
20
21 {
22
23 struct T1 t;
24
25 t.a = 11;
26
27 strcpy_s(t.name, "aaaa");
28
29 printf("sizeof(t)=%d \n", sizeof(t));//16
30
31
32
33 //定义结构体指针,来进行访问
34
35 struct T1* pt = &t;
36
37 printf("pt->a=%d,pt->name=%s \n", pt->a, pt->name);
38
39
40
41 //动态内存分配方式
42
43 struct T1* pt2 = (struct T1*)malloc(sizeof(T1));
44
45
46
47 free(pt2);
48
49 return 0;
50
51 }
027结构体多级指针
1 #include<stdio.h>
2
3 #include<string.h>
4
5 #include<stdlib.h>
6
7
8
9 struct T2
10
11 {
12
13 int c;
14
15 char* p;
16
17 };
18
19 struct T1
20
21 {
22
23 int a;
24
25 char *name;
26
27 struct T2* t2;
28
29 };
30
31 int main(void)
32
33 {
34
35
36
37 //动态内存分配方式
38
39 struct T1* p1 = (struct T1*)malloc(sizeof(T1));//堆区,要手动释放
40
41 p1->a = 11;
42
43 p1->name = (char*)malloc(128);
44
45 strcpy_s(p1->name, strlen("aaaa")+1, "aaaa");
46
47
48
49 struct T2* t2 = (struct T2*)malloc(sizeof(struct T2));
50
51 t2->c = 22;
52
53 t2->p = (char*)malloc(128);
54
55 strcpy_s(t2->p,strlen("bbbbb") + 1, "bbbbb");
56
57 printf("t2->p=%s \n", t2->p);
58
59
60
61 free(p1->name);
62
63 free(t2->p);
64
65 free(t2);
66
67 free(p1);
68
69
70
71 return 0;
72
73 }
74
75 /*
76
77 结论:
78
79 1 指针在使用前一定要分配空间,否则就是野指针
80
81 2 使用 malloc 分配的空间是在堆区,一定要手动释放,有多少个 malloc 就应该有多少个与其对应的 free
82
83 3 使用 free 释放空间的时候,要从小到大,从里到外
84
85 */
86
87
028calloc与realloc
1 #include<stdio.h>
2
3 #include<string.h>
4
5 #include<stdlib.h>
6
7
8
9 int main(void)
10
11 {
12
13 int* p1 = (int*)malloc(sizeof(int) * 10);//手动清0
14
15 //memset(p1, 0, sizeof(int) * 10);
16
17 int* p2 = (int*)calloc(10, sizeof(int));//自动清0
18
19
20
21 for (int i = 0; i < 10; i++)
22
23 {
24
25 printf("%d==", p1[i]);//随机值
26
27 }
28
29 printf("\n");
30
31 for (int i = 0; i < 10; i++)
32
33 {
34
35 printf("%d==", p2[i]);//0
36
37 }
38
39 printf("\n");
40
41 /*
42
43 -842150451==-842150451==-842150451==-842150451==-842150451==-842150451==
44
45 -842150451==-842150451==-842150451==-842150451==
46
47 0==0==0==0==0==0==0==0==0==0==
48
49 */
50
51
52
53 p1 = (int*)realloc(p1, sizeof(int) * 100);//将p1更改为100*4大小
54
55
56
57 free(p1);
58
59 free(p2);
60
61
62
63 return 0;
64
65 }
66
67 /*
68
69 结论:
70
71 (1)
72
73 函数原型:void* calloc(unsigned int num,unsigned int size);
74
75 功能:在内存的动态存储区中分配num个长度为size的连续空间,函数返回一个指向分配起始地址的指针;
76
77 如果分配不成功,返回NULL。
78
79 calloc分配内存会自动清0,malloc不会自动清零。
80
81 (2)void *realloc(void *mem_address, unsigned int newsize);
82
83 指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。
84
85 realloc 是表示将原有的指针变量的空间进行扩充,如果原指针指向的空间后面还有足够大的空间,
86
87 就是直接在原地址扩充,
88
89 * 如果原地址后面没有足够大的空间,则会开辟新地址,并将原来的数据拷贝到新空间
90
91 */
92
93
029C和C++中动态内存管理
1 #include<stdio.h>
2
3 #include<string.h>
4
5 #include<stdlib.h>
6
7
8
9 int main(void)
10
11 {
12
13
14
15 //C语言形式
16
17 int* p = NULL;
18
19 p = (int*)malloc(sizeof(int));
20
21 if (p != NULL)
22
23 {
24
25 *p = 123;
26
27 printf("*p=%d \n", *p);
28
29 free(p);
30
31 }
32
33
34
35 //C++形式
36
37 int* p1 = new int;
38
39 if (p1 != NULL)
40
41 {
42
43 *p1 = 123;
44
45 printf("*p1=%d \n", *p1);
46
47 delete p1;
48
49 }
50
51 int* p2 = new int(4);
52
53 if (p2 != NULL)
54
55 {
56
57 *p2 = 123;
58
59 printf("*p2=%d \n", *p2);
60
61 delete p2;
62
63 }
64
65
66
67 int* p3 = new int[4];
68
69 if (p3 != NULL)
70
71 {
72
73 *p3 = 123;
74
75 *(p3 + 1) = 456;
76
77 *(p3 + 2) = 789;
78
79 *(p3 + 3) = 101;
80
81 printf("*p3[0]=%d \n", p3[0]);
82
83 printf("*p3[1]=%d \n", p3[1]);
84
85 printf("*p3[2]=%d \n", p3[2]);
86
87 printf("*p3[3]=%d \n", p3[3]);
88
89 delete []p3;
90
91 }
92
93 return 0;
94
95 }
96
97 /*
98
99 结论:
100
101 * 栈区 局部变量,由程序自己控制,分配和销毁操作非常快,无法手动管理,但是栈区的空间是有限的,如果把非常大的一个变量放在栈区,或者是执行多次压栈的操作,有可能会爆栈
102
103 * 堆区 手动开辟的内存空间都在这里,只要不是特别大,一般都能分配成功,一般是用 malloc/new 来开辟,用 free/delete 来释放,由开发者手动操作
104
105 * 常量区 双引号
106
107 * 静态区,全局变量区
108
109 * 代码区
110
111 *
112
113 * malloc/free 一般在c语言中使用,是函数
114
115 * new/delete 一般在c++中使用,是运算符,本质上也是做内存管理,但是做的事情比malloc/free 更多
116
117
118
119 1 指针变量名=new 类型关键字
120
121 2 指针变量名=new 类型关键字(初始值)
122
123 3 指针变量名=new 类型关键字[]
124
125 */
126
127
030NULL与nullptr
1 #include<stdio.h>
2
3 #include<string.h>
4
5 #include<stdlib.h>
6
7
8
9
10
11 int main(void)
12
13 {
14
15 int* p1 = NULL;
16
17 int* p2 = nullptr;
18
19 char* p3 = NULL;
20
21 char* p4 = nullptr;
22
23 //printf("p1==p3 %d \n", p1 == p3);//error类型不兼容
24
25 printf("p1==p3 %d \n", p1 == p2);//1
26
27
28
29 int a = NULL;
30
31 //int b = nullptr;//error
32
33 return 0;
34
35 }
36
37 /*
38
39 结论:
40
41 * NULL和nullptr 在表示指针类型的变量时,是相等的,都是表示空指针
42
43 *但是两者是完全不一样的数据类型,没办法直接等于
44
45 * NULL 在表示指针类型的变量时,是空指针,实际上是int 0
46
47 * 在c++中,一般用nullptr 表示指针类型变量的初始值
48
49
50
51 */
52
53
031mew和delete
1 #include<iostream>
2
3 using namespace std;
4
5
6
7 class T
8
9 {
10
11 public:
12
13 T() { cout << "T 构造函数" << endl; }
14
15 ~T() { cout << "T 析构函数" << endl; }
16
17 };
18
19
20
21 int main(void)
22
23 {
24
25 int* p1 = (int*)malloc(sizeof(int));
26
27 if (p1 != NULL)
28
29 {
30
31 free(p1);
32
33 }
34
35
36
37 int* p2 = new int;
38
39 if (p2 != NULL)
40
41 {
42
43 delete p2;
44
45 }
46
47
48
49 //自定义数据类型
50
51 T* p3 = (T*)malloc(sizeof(T));
52
53 if (p3 != NULL)
54
55 {
56
57 free(p3);
58
59 }
60
61
62
63 T* p4 = new T();
64
65 if (p4 != NULL)
66
67 {
68
69 delete p4;
70
71 }
72
73
74
75 int* p5 = new int[4];
76
77 delete[]p5;
78
79
80
81 T* p6 = new T[6];
82
83 delete[]p6;
84
85
86
87 return 0;
88
89 }
90
91 /*
92
93 结论:
94
95 * new 在分配空间的时候,还做了初始化操作,就是new 一个类的时候,如果有构造函数,则会调用,
96
97 delete 的时候,如果有析构,也会调用,malloc 和 free 不会
98
99 * 这个主要体现在自定义类里面,其它内置类型,没有构造和析构的时候,表现出来的是一样的效果
100
101 * new [] 和 delete []一定要成对使用
102
103
104
105 */
032智能指针介绍
1 #include<iostream>
2
3
4
5 int main(void)
6
7 {
8
9 //情景演示:当有多个指针指向同一个内存地址,如何释放????
10
11 int* p1 = new int;
12
13 int* p2 = p1;
14
15 int* p3 = p1;
16
17 int* p4 = p1;
18
19 printf("p1=%p \n", p1);
20
21 printf("p2=%p \n", p2);
22
23 printf("p3=%p \n", p3);
24
25 printf("p4=%p \n", p4);
26
27
28
29 delete p1;
30
31 //delete p2;
32
33 //delete p3;
34
35 //delete p4;
36
37 return 0;
38
39 }
40
41 /*
42
43 结论:
44
45 *
46
47 * malloc/free new/delete 都必须得特别小心,特别考验技术
48
49 *
50
51 * 从c++98开始,就引入了一个智能指针的概念,主要是为了解决动态内存分配繁琐的问题
52
53 * 智能指针其实是将new/delete 作了一层封装,对外提供接口,让开发者更方便的去做内存管理
54
55 *
56
57 * 智能指针--> new/delete --> malloc/free
58
59 *
60
61 * 优点就是方便进行动态内存管理,减少程序出bug的概率 (其实就是帮你进行delete 操作,保证忘记delete 或者有多个指针指向同一个内存地址而不方便释放的时候,保证程序不会出现内存泄漏)
62
63 *
64
65 * auto_ptr(c++98) 目前基本上不再使用,也不建议使用
66
67 *
68
69 * shared_ptr(c++11) 共享
70
71 * unique_ptr(c++11) 独占【可以想像成程序里面的锁】 基本上代替了 auto_ptr
72
73 * weak_ptr(c++11) 配合 shared_ptr 使用
74
75 *
76
77 *
78
79 p1=000001F68BE83B40
80
81 p2=000001F68BE83B40
82
83 p3=000001F68BE83B40
84
85 p4=000001F68BE83B40
86
87 *
88
89 */
90
91
033shared_ptr定义和初始化
1 #include<iostream>
2
3 using namespace std;
4
5
6
7 //智能指针做函数返回值
8
9 shared_ptr<int>myFunc(int a)
10
11 {
12
13 return make_shared<int>(a);
14
15 }
16
17
18
19 int main(void)
20
21 {
22
23 //定义
24
25 shared_ptr<int>p1;//shared_ptr定义完成后就是一个空指针
26
27
28
29 int* p2 = new int;//如果没有初始化,p2就是一个野指针
30
31 cout <<(p1 == nullptr) << endl;//1
32
33
34
35 //初始化方法1
36
37 shared_ptr<int>p3(new int(123));
38
39 //shared_ptr<int>p4 = new int(123);
40
41 //无法从“int * ”转换为“std::shared_ptr<int>”
42
43
44
45 shared_ptr<int>p4 = myFunc(123);
46
47 shared_ptr<int>p5 = p3;
48
49 shared_ptr<int>p6(p5);
50
51
52
53 //用new 定义的指针变量我们称之为裸指针 0x123456
54
55 int* p7 = new int(123);
56
57
58
59 //这种写法在语法上没有任何问题,但是不能这样使用,
60
61 //就是说,不建议裸指针来初始化智能指针,也就是说,不建议裸指针和智能指针混搭
62
63 shared_ptr<int>p8(p7);
64
65 //shared_ptr<int>p9 = p7;//不合法,原因同上
66
67
68
69 free(p2);
70
71 //free(p7);//已经初始化了p8,没办法释放了
72
73 return 0;
74
75 }
76
77 /*
78
79 结论:
80
81 类模板格式定义智能指针
82
83 shared_ptr<数据类型>指针变量名;
84
85 */
86
87
034shared_ptr共享原理和引用计数
1 #include<iostream>
2
3 using namespace std;
4
5
6
7 void func1(shared_ptr<int> a)
8
9 {
10
11 cout << a.use_count() << endl;
12
13 return;
14
15 }
16
17
18
19 shared_ptr<int> func2(shared_ptr<int>& a)
20
21 {
22
23 return a;
24
25 }
26
27
28
29 int main(void)
30
31 {
32
33 shared_ptr<int>p1 = make_shared<int>(123);
34
35 shared_ptr<int>p2 = p1;
36
37 shared_ptr<int>p3(p2);
38
39 p1.reset();//将p1的指向重置
40
41 cout << p2.use_count() << "--" << p3.use_count() << endl;//2--2
42
43
44
45 p2 = make_shared<int>(456);
46
47 cout << p2.use_count() << "--" << p3.use_count() << endl;//1--1
48
49
50
51 func1(p1);
52
53 shared_ptr<int>p4= func2(p1);
54
55
56
57 return 0;
58
59 }
60
61 /*
62
63 结论:
64
65 * shared_ptr 共享智能指针,所谓共享,是指有可能有其它的指针指向该块内存,同时也具有读写和销毁的权限
66
67 *
68
69 * 实现智能管理的原理是,每个shared_ptr 指针变量都会维护一个其指向自身指向的那个内存空间的引用计数器,并随时同步更新,以达到与其它shared_ptr同步的目的
70
71 * 维护这个计数器要有额外的开销
72
73 *
74
75 *
76
77 * 引用计数器的增加和删除
78
79 * 1 新建一个新的shared_ptr 并初始化其指向,此时,该变量的引用计数器为1
80
81 * 2 用上面的shared_ptr 变量初始化一个新的 shared_ptr,此时,指同同一个内存地址的shared_ptr 的引用计数器都要加1
82
83 * 3 如果将shared_ptr 作为一个实参传递到一个函数里面,在函数生命周期内,函数也有操作该内存的权限,引用计数器也要加1,如果作为引用传参,计数器不会加1
84
85 * 4 如果函数将一个shared_ptr 作为返回值返回,并且有用变量接收,则指向对应的内存地址的shared_ptr 引用计数器也要加1
86
87 *
88
89 *
90
91 * 引用计数器的减小和内存空间的释放
92
93 * 当指向内存空间的shared_ptr 不再指向该内存空间的时候,其它指向该空间的shared_ptr 的引用计数器都会减1
94
95 * 当最后一个指向该内存空间的shared_ptr 被释放或销毁时,该内存空间就
96
97 *
98
99 * shared_ptr 共享智能指针,所谓共享,是指有可能有其它的指针指向该块内存,同时也具有读写和销毁的权限
100
101 *
102
103 * 实现智能管理的原理是,每个shared_ptr 指针变量都会维护一个其指向自身指向的那个内存空间的引用计数器,并随时同步更新,以达到与其它shared_ptr同步的目的
104
105 * 维护这个计数器要有额外的开销
106
107 *
108
109 * 引用计数器的增加和删除
110
111 * 1 新建一个新的shared_ptr 并初始化其指向,此时,该变量的引用计数器为1
112
113 * 2 用上面的shared_ptr 变量初始化一个新的 shared_ptr,此时,指同同一个内存地址的shared_ptr 的引用计数器都要加1
114
115 * 3 如果将shared_ptr 作为一个实参传递到一个函数里面,在函数生命周期内,函数也有操作该内存的权限,引用计数器也要加1,如果作为引用传参,计数器不会加1
116
117 * 4 如果函数将一个shared_ptr 作为返回值返回,并且有用变量接收,则指向对应的内存地址的shared_ptr 引用计数器也要加1
118
119 *
120
121 *
122
123 * 引用计数器的减小和内存空间的释放
124
125 * 当指向内存空间的shared_ptr 不再指向该内存空间的时候,其它指向该空间的shared_ptr 的引用计数器都会减1
126
127 * 当最后一个指向该内存空间的shared_ptr 被释放或销毁时,该内存空间就会交还给操作系统
128
129 *
130
131 * 参数未使用,被编译器优化了
132
133 * 会交还给操作系统
134
135 * 参数未使用,被编译器优化了
136
137 */
138
139
035shared_ptr常用操作
1 #include<iostream>
2
3 using namespace std;
4
5
6
7 int main(void)
8
9 {
10
11 shared_ptr<int>p1(make_shared<int>(123));
12
13 cout << p1.unique() << endl;//1
14
15 cout << "p1引用计数p1.use_count() " << p1.use_count() << endl;//1
16
17 shared_ptr<int>p2(p1);
18
19 cout << "p1引用计数p1.use_count() " << p1.use_count() << endl;//2
20
21 cout << "p1引用计数p2.use_count() " << p2.use_count() << endl;//2
22
23 cout << p1.unique() << endl;//0
24
25 p1.reset(new int(456));
26
27 // p1.get() = 0000022EF53E8650 p2.get() = 0000022EF53E0830
28
29 cout << "p1.get()=" << p1.get() << "\t p2.get()=" << p2.get() << endl;
30
31
32
33 shared_ptr<int>p3(make_shared<int>(789));
34
35 cout << "*p1=" << *p1 << "\t" << "p1=" << p1 << endl;
36
37 cout << "*p3=" << *p3 << "\t" << "p3=" << p3 << endl;
38
39 p1.swap(p3);
40
41 cout << "*p1=" << *p1 << "\t" << "p1=" << p1 << endl;
42
43 cout << "*p3=" << *p3 << "\t" << "p3=" << p3 << endl;
44
45 /*
46
47 *p1=456 p1=00000162E2148E20
48
49 *p3=789 p3=00000162E2140180
50
51 *p1=789 p1=00000162E2140180
52
53 *p3=456 p3=00000162E2148E20
54
55 */
56
57
58
59 return 0;
60
61 }
62
63 /*
64
65 结论:
66
67 * use_count() shared_ptr 变量所指向的内存空间的引用次数
68
69 * unique() shared_ptr 是否是独占其指向的内存地址,如果只有一个shared_ptr 指向,那么值为true,
70
71 *如果有其它shared_ptr指向该内存地址,则为false
72
73 *如果是空指针,也是false
74
75 *
76
77 * reset() 重置,无参,如果是独占,则销毁其指向的内存空间,并将当前share_ptr变量置为空指针
78
79 * 如果不是独占,则将其它指向该内存的shared_ptr 变量的引用计数器减1,并将当前变量置为空指针
80
81 *
82
83 * 有参,如果是独占,则销毁其指向的内存空间,并将当前share_ptr变量指向新地址
84
85 * 如果不是独占,则将其它指向该内存的shared_ptr 变量的引用计数器减1,并将当前share_ptr变量指向新地址
86
87 *
88
89 * get() 取指针地址 是为了一些其它的需求或方法要使用地址
90
91 *
92
93 * swap() 交换指向
94
95
96
97 */
98
99
036shared_ptr删除器
1 #include<iostream>
2
3 using namespace std;
4
5
6
7 void myFunc01(int* p)
8
9 {
10
11 cout << "自定义删除器11" << endl;
12
13 delete p;
14
15 }
16
17 void myFunc02(int* p)
18
19 {
20
21 cout << "自定义删除器22" << endl;
22
23 delete p;
24
25 }
26
27
28
29 class T
30
31 {
32
33 public:
34
35 T() { cout << "构造函数" << endl; }
36
37 ~T() { cout << "析构函数" << endl; }
38
39 };
40
41 void myFunc03(T*p)
42
43 {
44
45 delete[]p;
46
47 }
48
49
50
51 int main(void)
52
53 {
54
55 T *pt = new T[4];
56
57 delete[]pt;
58
59
60
61 shared_ptr <T>p1(new T[3], myFunc03);//元素是数组时,必须自定义删除器
62
63 shared_ptr<T>p2(new T[3], default_delete<T[]>());
64
65
66
67 //c++17开始支持这种写法,就可以很好的处理数组删除的问题
68
69 //shared_ptr<T[]>(new T[3]);
70
71
72
73 //赋值操作也会自动赋值删除器
74
75 shared_ptr<int>p3(new int(123), myFunc01);
76
77 shared_ptr<int>p4(new int(123));
78
79 p4 = p3;
80
81
82
83 //可以使用lambda表达式实现
84
85 shared_ptr<char>c1(new char('a'), [](char*p) {
86
87 cout << "这里是lambda删除" << endl;
88
89 delete p;
90
91 });
92
93
94
95 auto myLambda = [](int*p)
96
97 {
98
99 cout << "lambda删除器" << endl;
100
101 delete p;
102
103 };
104
105 shared_ptr<int>p5(new int(123), myLambda);
106
107
108
109 return 0;
110
111 }
112
113 /*
114
115 结论:
116
117 * shared_ptr 自定义删除器
118
119 *
120
121 * make_shared 不支持
122
123 *
124
125 构造函数
126
127 构造函数
128
129 构造函数
130
131 构造函数
132
133 析构函数
134
135 析构函数
136
137 析构函数
138
139 析构函数
140
141 构造函数
142
143 构造函数
144
145 构造函数
146
147 构造函数
148
149 构造函数
150
151 构造函数
152
153 lambda删除器
154
155 这里是lambda删除
156
157 自定义删除器11
158
159 析构函数
160
161 析构函数
162
163 析构函数
164
165 析构函数
166
167 析构函数
168
169 析构函数
170
171 *
172
173 */
174
175
037weak_ptr
1 #include<iostream>
2
3 using namespace std;
4
5
6
7 int main(void)
8
9 {
10
11 shared_ptr<int>p1(new int(123));//ok
12
13 //weak_ptr<int>p2(new int(456));//error 没有与参数匹配的构造函数
14
15
16
17 //weak_ptr<int>p2(make_shared<int>(new int(456)));//ok
18
19 //weak_ptr<int>p2 = make_shared<int>(new int(456));//ok 但是不能操作内存
20
21 //使用shared_ptr初始化weak_ptr
22
23 weak_ptr<int>p2(p1);//1个shared_ptr指针,一个weak_ptr
24
25 auto p3 = p2.lock();////增加一个shared_ptr 计数器
26
27 shared_ptr<int>p4 = p2.lock();////增加一个shared_ptr 计数器
28
29
30
31 p2.reset();
32
33 p1.reset();
34
35 cout << p2.expired() << endl;//1 --只有强引用才有这个函数
36
37
38
39 return 0;
40
41 }
42
43 /*
44
45 结论:
46
47 * shared_ptr strong
48
49 * weak_ptr weak
50
51 * weak_ptr 不能用裸指针初始化,可以用make_shared初始化,
52
53 * 但是直接用make_shared 初始化没有任何意义,这是因为不能直接用weak_ptr 去操作对应的内存空间,必须依附于shared_ptr 来操作
54
55 * weak_ptr 就是打辅助
56
57 * 当一个shared_ptr 新增一个weak_ptr的时候,其weak_ptr 引用计数器会加1,当销毁一个weak_ptr的时候,其weak_ptr引用计数器会减1,
58
59 * 但是,不能直接使用weak_ptr 去操作指向的内存
60
61 *
62
63 * 相反,当一个shared_ptr 销毁时,指向其的weak_ptr 将会变成expired(过期,失去生命周期)的状态*
64
65 * lock(); 增加一个 shared_ptr 计数器(返回一个shared_ptr)
66
67 * use_count() shared_ptr 计数器
68
69 * expired() 是否过期,当其对应的shared_ptr 羡慕后,此处就返回true
70
71 * reset() 减少一个weak_ptr 引用计数器
72
73 */
038unique_ptr定义和初始化
1 #include<iostream>
2
3 using namespace std;
4
5
6
7 int main(void)
8
9 {
10
11 shared_ptr<int>p1(new int(123));
12
13 //只能有一个指针指向内存空间,所以有move语义
14
15 unique_ptr<int>p2(new int(456));
16
17
18
19 return 0;
20
21 }
22
23 /*
24
25 结论:
26
27 * shared_ptr 共享式智能指针,可以用时有多个shared_ptr 指向同一个内存空间,并且这些shared_ptr 变量都对该内存空间有操作权限,
28
29 * 而且这些shared_ptr 间共享引用计数器
30
31 * unique_ptr 独占式智能指针,同一时间只能有一个unique_ptr 变量指向同一个内存空间,
32
33 * 且只有此unique_ptr 对该内存空间拥有操作权限
34
35 *
36
37 *
38
39 * unique_ptr<int>p1 定义完了就是个空指针
40
41 *
42
43 *
44
45 * unique_ptr<int>p1;
46
47 p1 = make_unique<int>(123);
48
49 unique_ptr<int>p2(make_unique<int>(456)); make_unique c++14之后开始引入,且不支持自定义删除器
50
51 unique_ptr<int>p3(new int(789));
52
53
54
55 unique_ptr<int>p6=move(p1); 把p1 销毁,并且将p1原来维护的内存空间的权限转移给p6
56
57
58
59 p6 = make_unique<int>(321); 重新赋值,把原来维护的内存空间销毁掉
60
61
62
63 不支持
64
65 //unique_ptr<int>p4=new int(321);
66
67 //unique_ptr<int>p4(p1);//因为只能有一个指针指向内存空间
68
69 //unique_ptr<int>p5=p1;
70
71 */
72
73
039unique_ptr常用操作
1 #include<iostream>
2
3 using namespace std;
4
5
6
7 int main(void)
8
9 {
10
11 unique_ptr<int>p1 = make_unique<int>(123);
12
13 cout << "p1.get()" << p1.get() << endl;
14
15 unique_ptr<int>p2 = make_unique<int>(456);
16
17 p1.swap(p2);
18
19
20
21 //int* pTmp = p1.release();
22
23 p1.reset(new int(789));
24
25 return 0;
26
27 }
28
29 /*
30
31 结论:
32
33 * get() 返回裸指针
34
35 * =nullptr 把 unique_ptr 指向的内存空间销毁,交还系统,并将unique_ptr 置为空指针
36
37 * reset() 无参 把 unique_ptr 指向的内存空间销毁,交还系统,并将unique_ptr 置为空指针
38
39 *有参 把 unique_ptr 指向的原内存空间销毁,交还系统,并将unique_ptr 指向新地址
40
41 *
42
43 * release() 返回裸指针,将unique_ptr 变量置为空指针,此处的内存空间不再由unique_ptr 变量维护,需要手动释放
44
45 * reset(release())
46
47 * swap()
48
49 *
50
51
52
53 */
54
55
040unique_ptr删除器
1 #include<iostream>
2
3 using namespace std;
4
5 void func1(int* p)
6
7 {
8
9
10
11 cout << "这里是自定义删除器" << endl;
12
13 delete p;
14
15 p = nullptr;
16
17 }
18
19
20
21 int main(void)
22
23 {
24
25 //复习shared_ptr
26
27 //shared_ptr<int>p1(new int(123), func1);
28
29
30
31 //unique_ptr<数据类型,删除器类型>变量名(new 数据类型(初始值),删除器);
32
33 typedef void (*pFunc)(int*);
34
35 using pf = void(*)(int);
36
37 unique_ptr<int, pFunc>p1(new int(123), func1);
38
39
40
41 /*
42
43 shared_ptr<int>p1(new int(123),[](int *p){
44
45 cout<<"这里是lambda删除器"<<endl;
46
47 delete p;
48
49 p= nullptr;
50
51 });
52
53 */
54
55 auto lambda = [](int* p)
56
57 {
58
59 cout << "这里是lambda删除器" << endl;
60
61 delete p;
62
63 p = nullptr;
64
65 };
66
67 shared_ptr<int>p2(new int(123), lambda);
68
69 unique_ptr<int, decltype(lambda)>p3(new int(123), lambda);
70
71
72
73
74
75 //unique_ptr 转 shared_ptr
76
77 //当unique_ptr 为右值时,可转成shared_ptr
78
79 //右值,等号右边的值,必须得放在运算符右边 左值 运算符 右值
80
81
82
83 //0x00007fc92df04080
84
85 unique_ptr<int>p4 = make_unique<int>(123);//ok
86
87 shared_ptr<int>p5 = make_unique<int>(123);//ok
88
89
90
91 //shared_ptr<int>p3=p1;//error
92
93 shared_ptr<int>p6 = move(p1);//move 把左值转右值
94
95 cout << (p6 == nullptr) << endl;//0
96
97 return 0;
98
99 }
100
101
041智能指针的sizeof
1 #include<iostream>
2
3 using namespace std;
4
5
6
7
8
9 void func1(int* p)
10
11 {
12
13 //cout<<"这里是自定义删除器"<<endl;
14
15 delete p;
16
17 p = nullptr;
18
19 }
20
21 int main(void)
22
23 {
24
25 int a = 123;
26
27 int* p = &a;
28
29 cout << "sizeof(p)=" << sizeof(p) << endl;//8
30
31
32
33 shared_ptr<int>p2(new int(a));
34
35 shared_ptr<int>p3(make_shared<int>(a));
36
37 shared_ptr<int>p4(p2);
38
39
40
41 cout << sizeof(p2) << endl;//16
42
43 cout << sizeof(p3) << endl;//16
44
45 cout << sizeof(p4) << endl;//16
46
47
48
49 unique_ptr<int>p6(new int(a));
50
51 cout << sizeof(p6) << endl;//16
52
53
54
55 unique_ptr<char>p5(new char('a'));
56
57 cout << sizeof(p5) << endl;//8
58
59
60
61 using pf = void(*)(int*);
62
63 unique_ptr<int, pf>p7(new int(a), func1);
64
65 cout << sizeof(p7) << endl;//16
66
67
68
69 auto lambda = [](int* p)
70
71 {
72
73 delete p;
74
75 p = nullptr;
76
77 };
78
79
80
81 unique_ptr<int, decltype(lambda)>p8(new int(789), lambda);
82
83 cout << sizeof(p8) << endl;//8
84
85
86
87
88
89 //delete p;
90
91 return 0;
92
93 }
94
95 /*
96
97 结论:
98
99 * 64位机上,8个字节可以存一个内存地址,所以裸指针的大小是8个字节
100
101 * shared_ptr 里面,不仅仅只存了对应的堆区的数据域的地址,还有维护额外信息,
102
103 * 这些信息也要有空间来存储,所以,shared_ptr 里面有两个指针,就是16字节
104
105 * 同理,weak_ptr 也是一样 是 两个裸指针大小 16字节
106
107 *
108
109 * unique_ptr 不需要去维护额外的数据,所以只要存储一个指向数据域的指针即可
110
111 * 是一个裸指针大小,8字节
112
113 * 但是,如果有使用自定义删除器,则也需要维护自定义删除器的一些信息,
114
115 * 要再加一个指针大小的空间,此时是两个裸指针的大小 16字节
116
117 *
118
119 * 所以 unique_ptr 的大小是可变的
120
121 *
122
123 * 大家可以把智能指针想像成链表里面的一个数据节点,除了数据域之外,还有其它节点的指针域
124
125 */
在那巴山夜雨中寻一份寂静与洒脱。

浙公网安备 33010602011771号