为什么学习指针?
- 阅读他人代码
- 与API沟通
- 有时候必须要使用指针
什么是指针?
不幸的是,这个问题有一个非常简单的答案:
A pointer is just a variable that holds a memory address
有疑问请参考 link【C++中阶教程0】内存/二进制/变量
更不幸的是,如果你去问百度:
WTF…
其实指针的使用忌讳想得太多,越想越容易乱套;但如果想用好的话,必须刨根问底学习透彻,不要害怕,耐心读完本文便会拨云见日。
C++里,
* 为 声明指针和间接引用 操作符
& 为 address of 操作符
再次强调:
A pointer is just a variable that holds a memory address
- 指针的本质,就是一个变量,它的声明格式也完全一致
- 指针变量hold的是指针类型,但是它需要知道自己指向的类型(单纯理解为兼容性即可)
int main() 术语版
{
int n = 5; 声明1个变量,名字是n,编译器分配4个byte来存储它的值5,简称“初始化为5”
int* p; 声明1个特殊变量:指针,名字是p,编译器分配4个byte,没有初始化,指向随机位置
p = &n; &n = address of n,告诉p往哪儿指
*p = *p + 64; *p = 引用p指向的目标,也就是5,加上64得出神奇数字69
return 0;
}
int main() 大白话版
{
int n = 5; 楼里来了个新住户n
int* p; 买了个小本儿,叫p
p = &n; 小本儿上记下了n的门牌号
*p = *p + 64; 查阅小本儿,找n家里去了
return 0;
}
来看几种其它情况
int main()
{
int n = 5;
int* p;
p = 4; 错误,不能将int赋给int pointer
*p = *p + 64;
return 0;
}
int main()
{
int n = 5;
float f = 6.9f;
int* p;
p = &f; 错误,不能将float pointer赋给int pointer
*p = *p + 64;
return 0;
}
int main()
{
int n = 5;
int* p;
p = nullptr; 正确,nullptr很常用,为特殊值null pointer,表明这个指针现在毛都没指,
*p = *p + 64;
return 0;
}
int main()
{
int n = 5;
int* p = &n;
*p = *p + 64;
int* q = p; 正确,类型兼容,现在q和p都指向n
return 0;
}
三次强调: pointer也是变量,就同样需要分配对应类型对应大小的内存
int main()
{
int n = 5;
int* p = &n;
*p = *p + 64;
int* q = p;
return 0;
}
左右程序效果相等
int main() int main()
{ {
int n = 5; int n = 5;
int* p = &n; n = n + 64;
*p = *p + 64;
int* q = p; return 0;
}
return 0;
}
这个傻蛋程序只是为了单纯展示指针,事实上,除非在必要情况下否则尽量不要使用指针
来看需要使用指针的情况
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp
}
int main()
{
int x = 6;
int y = 9;
swap(x, y); / swap()接收x,y的'值'然后执行,不交换x,y本身的数据
}
void swap(int* pa, int* pb)
{
int temp = *pa;
*pa = *pb;
*pb = temp
}
int main()
{
int x = 6;
int y = 9;
swap(&x, &y); / swap()接收x,y的'内存地址'然后执行,住户没换,门牌号换了,等价于值交换了
}
指针 pointer和引用 reference的对比:
(双向和单向的区别,因为指针多出的功能,自然地它需要多出语法来区分功能)
// pointer
int* ptr = &n; / 初始化声明
int* ptr; / 无初始化声明
ptr = &m; / 改变指针
*ptr = 69; / 改变目标
// reference
int& ref = m; / 正确,物质必须存在才能引用它
int& ref2; / 错误,引用必须初始化
ref = n; / 不能改变目标,这样会把n的值存储到m内
但这里却正说明了,很多时候,强大不代表最好
void swap(int& a, int& b) / 更加清晰明了,不容易搞砸
{
int temp = a;
a = b;
b = temp
}
int main()
{
int x = 6;
int y = 9;
swap(x, y); / swap()接收x,y的'引用'然后执行,交换x,y本身的数据
}
使用优先级潜规则:variable directly > reference > pointer
class示范
重要: ->操作符
example A
class Poop
{
public:
int pizza;
int cola;
char mix;
};
int main()
{
Poop myPoop;
Poop* p_Poop = &myPoop;
int* p_Pizza = &myPoop.pizza;
int* p_Cola = &myPoop.cola;
char* p_Mix = &(*p_Poop).mix; / 此行等于 char* p_Mix = &p_Poop->mix;
return 0;
}
example B
class Dump
{
public:
Vec2& operator+=(const Vec2& rhs)
{
return *this = *this + rhs;
/ 实则this是1个指针,所有非静态成员函数都会自动定义this
/ this指向成员函数所调用的成员,this可用于return自己的引用,或将自己的地址传给其它函数
/ 非静态成员函数 = non-static member function
}
}
再来看一下this的“去除模糊”的使用
class Dump
{
public:
float BustAss(float x, float y) const
{
return x*x + y*y; / 参数与成员重名,return后只会是参数自乘
return this->x*x + this->y*y; / 假设你想参数*成员,必须要这样
/ 仍然建议永远保持成员member和参数parameter名字不同
}
public:
float x;
float y;
}
example C
int main()
{
int arr[4] = { 10, 11, 12, 13 };
int* ptr = &arr[1];
*ptr = 69; / 11 变 69
return 0;
}
example D
int main()
{
int arr[4] = { 10, 11, 12, 13 };
int* ptr = &arr[1];
ptr[2] = 69; / 4 变 69
return 0;
}
example E
int main()
{
int arr[4] = { 10, 11, 12, 13 };
int* ptr = arr; / 光屁股的array表现和pointer一样,arr用来定位这个数组开始的地址
*arr = 69; / 10 变 69
ptr[2] = 69; / 12 变 69
// pointer和array的区别
int n = 69;
arr = &n; / 错误,不能改变arr目标
int sizeP = sizeof(ptr); / sizeP = 4
int sizeA = sizeof(arr); / sizeA = 16
return 0;
}
请认真体会这所有结构之中的相关之美,此后我们还会遇到很多
Array求和
int sum(int* ptr, int size)
{
int sum = 0;
for (int i = 0; i < size, i++)
{
sum += ptr[i];
}
return sum;
}
int main()
{
int arr[4] = { 10, 11, 12, 13 };
int result = sum(arr, 4);
return 0;
}