C++ 笔记(指针和引用)

date
Jun 27, 2021
slug
cpp-notes-pointersAndReferences
status
Published
tags
c++
summary
type
Post

概念

指针是一个变量,里面存储的是指向内存存储单元的一个地址。
int a = 5;
int *ptr = &a;
cout<<"变量 a 的内存地址:"<<ptr<<"值:"<<*ptr;
打印结果: 变量 a 的内存地址: 0x7ffdaea1eb0c 值: 5 变量 b 的地址:0x7ffdaea1eb08
a 就是个变量,ptr 是指针变量,ptr 里面存储的是 a 的内存地址。
引用是一种特殊的指针,可以理解为是对一个变量起了个别名。
int b=5;
int &ref = b;
cout <<"变量 b 的地址:"<<&b<<endl;
cout <<"引用 ref 的地址:"<<&ref<<endl;
cout <<"引用 ref 的值:"<<ref;
打印结果:
变量 b 的地址:0x7ffdd17efcd4
引用 ref 的地址:0x7ffdd17efcd4
引用 ref 的值:5

不同点

1、指针是一个对象,而引用不是。所以引用不可以为空,创建的时候,必须初始化,指针可以在任何时候被初始化。 2、指针可以有多级,但是引用只能一级(int **p 正确,而 int &&p 不正确)。 3、指针的值初始化之后可以改变,可以指向其他存储单元,引用初始化之后就不能再改变。 4、”sizeof 引用”得到的是所指向的变量 (对象) 的大小,而”sizeof 指针”得到的是指针本身的大小。 5、如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄漏。 6、指针和引用的自增(++)运算意义不一样。(一个地址 + 1,一个值 + 1)

指向指针的引用

int i = 42;
int *p;
int *&ref = p; //ref 是一个引用,引用了一个指针
ref = &i; //ref 相当于是 p 的别名,等价与 p=&i
cout << ref<<endl;
cout << p <<endl;
cout << *ref <<endl;
*ref = 100;
cout<< i;
打印结果:
0x7ffe7289e394
0x7ffe7289e394
42
100
复杂表达式从右向左一个一个阅读,离变量名最近的符号是对变量类型有直接影响,这里 *(&ref), 因此 ref 是一个引用,然后 (&ref) 是一个指针。

与 const 结合

1、const 和引用 常量的引用,必须也是一个常量。
const int ci=1024;// 这是一个常量
const int &r1=ci;// 正确,引用及其对应的对象都是常量
int &r2=ci;//c 错误,试图让一个非常量引用指向常量引用。注意这两行代码的区别
变量的引用
int i=42; // 一个变量,未初始化
const int &r1=i;//r1 是指向 i 的引用常量
const int &r2=42;//int &r2=42 就会报错了,因为引用必须初始化指向的是对象
const int &r3=r1*2;
cout<< r1<<endl;
cout << r3<<endl;
i=12;
cout<< r3<<endl; // r3 依旧引用当初的那个变量 i
cout<< i<<endl; //i 却已经变了,这是个忧伤的故事
int &r4=r1*2;// 报错,r4 是非常量引用,不能用常量引用来初始化。
常量引用指向变量的时候,即便变量修改了值,常量引用依旧指向的是当初的那个变量的值,不离不弃。 2、const 和指针 就是将指针定义为不可更改的常量。 顶层 const:表示地址本身是常量 底层 const:表示数据是常量
int num_b = 2;
int *const p_b = &num_b; // 顶层 const
int num_a = 1;
int const *p_a = &num_a; // 底层 const
double pi = 3.14;
const double *p = &pi;// 底层 const
*p = 30; // 错误,即便 pi 是变量,但是指针是常量,没办法修改数据
double x=32.2;
cout << *p<<endl;// 输出 3.14
p=&x;// 正确, 可以修改地址
cout << *p<<endl;// 输出 33.2
double pi = 3.14;
double *const p = &pi;// 顶层 const
*p = 30; // 正确,没办法修改地址,但是数据可以修改
double x=32.2;
p = &x; // 错误,不能修改地址
从上面的实验可以了解到,底层 const 虽然值是不可以改变的,但是可以通过指针地址的变动,从而修改里面的值。 顶层 const 指向的地址无法改变,那如果我指向一个 const 类型的变量,直接修改它的值,是不是就能变相修改 const 变量的值了呢,答案是不行的。
const double z = 3.14;
double *const y = &z;// 编译报错

作为函数参数

让指针或者引用作为函数参数来传递,是为了减少对实参的副本拷贝。 我的理解是指针和引用作为函数参数,都可以达到想要的效果,引用可以看作是对实参起了个别名,操作的是同一个对象。 函数参数是指针的函数:
void testPoint(int *p)
{
    int a=1;
    p=&a; // 获取它的值必须要写成 * p
    cout<<p<<""<<*p<<endl<<endl;
}
函数参数是引用的函数:
void testRef(int& ref)
{
    int a=1;
    ref=a; // 直接 ref 拿来获取用了
    cout<<&ref<<""<<ref<<endl<<endl;
}

指针函数和函数指针

函数指针: 一种特殊的指针,指向函数入口
/*
* 定义一个函数指针 p,只能指向返回值为 int,形参为两个 int 的函数
*/
int (*p)(int,int);
int max(int a, int b) {
    return a > b ? a : b;
}
int min(int a, int b) {
    return a < b ? a : b;
}
int main(){
    f = max; // 函数指针 f 指向求最大值的函数 max
    int c = (*f)(1, 2);
    f = min; // 函数指针 f 指向求最小值的函数 min
    c = (*f)(1, 2);
}
指针函数: 一个函数,返回值是指针
class Hello
{
public:
    string world;
};

Hello *p(string world)
{
    Hello *hello = new Hello();
    hello->world = world;
    return hello;
}
int main()
{
   Hello *hp = p("name");
    cout << "对象中的值"<<hp->world<<endl;
}
上面是用 new 的方式生成了一个 hello 实例,然后返回了这个实例的指针,乍看上去,应该出了作用域就应该被回收了,但是没有,并打印出了 name, 如果是简单类型,就回收了。
int *function(int a){
    int temp = 5;
    return &temp;
}
int main()
{
   int *hp = function(3);
   cout << "指针的值"<<hp<<endl; // 打印了 0
}
原因应该是用了 new,则需要显式 delete,否则内存不释放,就 GG 了。 那不用 new 这种显式声明呢?
class Hello
{
public:
    string world;
};
Hello* r(string world)
{
    Hello hello;
    hello.world = world;
    return &hello;
}
int main()
{
    Hello *hr = r("name");
    cout << "指针的值"<<hr->world<<endl; // 直接空指针了
}
不声明 new,出了作用域,对象马上灭亡,所以空指针。

指向数组

char x[] = {'a','b','c','\\0'};
cout<< *x; // 指向第 0 个元素,输入 a
一个 Array 类型的变量,其实本质上是一个指向数组第一个元素的指针。

© Frandy 2021