指针
指针是一种底层的数据类型,可以实现对各种内存的操作,无论内存是位于栈上还是堆上,有权限就可以操作。如果说变量是盒子,那指针就是盒子的编号或者标签,*
就是一种表示指针的操作符。
指针可以根据描述对象分为很多种,但是其本质就是一种表达内存的数据。一个非野指针或非空指针变量会拥有两个内存地址,一个是本身位于栈上的内存地址,另一个是其指向的内存地址。
为了解释的更具体,Minloha采购了C Primer Plus
并且深入的和书籍交流了一番,做出以下解释
结构体指针
结构体指针就是指向一个结构体变量的指针,其调用都可以像正常结构体对象一样。先写出一个结构体并实现结构体指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <iostream> using namespace std;
struct A { int number; } T; int main() { T.number = 123123; A* p = &T; A* p2 = new A; p->number = 114514; p2->number = 123; cout << T.number << endl; cout << p2->number << endl; }
|
对结构体A
,在它声明的同时也创建了一个对象T
,这个对象可以被一个同为A
的指针指向,这个指针就叫结构体指针,也就是代码中的*p
,指针的初始化可以通过取值符&
取已有对象的地址。
底层的说是不存在变量什么的,都是内存地址的移动,所以当指针被一个对象地址初始化之后,指针就是对象了,之后对指针的增删改都会直接影响到对象,因为它们两个是在一个内存上生长的。
野指针&空指针&无类型指针
野指针是一种指向地址不确定的指针,空指针就是指向地址为空的指针,无类型指针就是用void
定义的指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <iostream> using namespace std;
int main() { int num = 114514; void* a = # int* b = # int* c = (int *)malloc(2); cout << *(int*) a << endl; cout << *b << endl; cout << *c << endl; free(c); cout << *c << endl; }
|
在内存管理部分中要尽可能避免这小部分提到的指针,主要是为了保证内存的安全性。
数组指针
数组的定义是int array[]
,而这个array
就是一种数组指针,指向了数组头
1 2 3 4 5 6 7 8 9 10 11
| [[include]] <iostream> using namespace std;
int main() { int list[] = { 1,2,3,4,9 }; int* p = list; for (int i = 0; i < 5;i++) { cout << p[i] << endl; } }
|
函数指针
函数指针与指针函数一直被区分着,其实可以用偏意词的角度理解,函数指针即指向函数的指针,指针函数就是返回值为指针的函数。
1 2 3 4 5 6 7 8 9 10 11
| [[include]] <iostream> using namespace std;
int* func(int n) { return (int*)n; } int main() { cout << *func(9) << endl; }
|
函数指针的写法是类型 (*指针名)(参数表)
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| [[include]] <iostream> using namespace std;
int add(int a,int b){ return a+b; }
int sub(int a,int b) { return a-b; }
int (*func)(int a,int b);
int main() { func = add; cout << func(2,1) << endl; func = sub; cout << func(2,1) << endl; }
|
多级指针
指针也是一种数据类型,也会在栈中留下自己的地址,而指向指针地址的指针就叫二级指针,无限套娃下去就会出现各种等级的指针,这里说说二级指针。
在出现函数传参为指针的时候需要注意,函数所传的指针是以引用的形式传递,意味着离开函数后不会对原有指针产生任何影响,这时就需要二级指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| [[include]] <iostream> using namespace std;
int gb = 4;
void getAddress(int *q) { cout << "函数内:q=" << q << "|" << "&q=" << &q << endl; q = &gb; cout << "函数内:q=" << q << "|" << "&q=" << &q << endl; }
int main() { int num = 3; cout << "num=" << num << "|" << "&num=" << &num << endl; int* p = # getAddress(p); cout << "p=" << p << "|" << "&p=" << &p << endl; }
控制台输出: num=3|&num=00000045030FFB34 函数内:q=00000045030FFB34|&q=00000045030FFB10 函数内:q=00007FF75DD5D000|&q=00000045030FFB10 p=00000045030FFB34|&p=00000045030FFB58
|
此时的p
指向的地址并未被修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| [[include]] <iostream> using namespace std;
int gb = 4;
void getAddress(int **q) { cout << "函数内:q=" << q << "|" << "&q=" << &q << endl; *q = &gb; cout << "函数内:q=" << q << "|" << "&q=" << &q << endl; }
int main() { int num = 3; cout << "&gb=" << &gb << "|" << "&num=" << &num << endl; int* p = # getAddress(&p); cout << "p=" << p << "|" << "&p=" << &p << endl; }
控制台输出: &gb=00007FF6F5CBD000|&num=00000051BA2FF9C4 函数内:q=00000051BA2FF9E8|&q=00000051BA2FF9A0 函数内:q=00000051BA2FF9E8|&q=00000051BA2FF9A0 p=00007FF6F5CBD000|&p=00000051BA2FF9E8
|
可以明显看到经过函数操作后传递的参数&p
指向被修改为gb
的地址了,在函数内直接通过指针的地址实现的二级指针修改了参数指针指向地址……(嗯)
总结
挺复杂的,但是理解之后很爽!非常建议收藏保存。