#C++ Day21 October 31 2025

//13 – 1 citation grammar

//引用的语法

#include <iostream>

using namespace std;

//quotation

//引用比指针更好理解

//指针:所以爱会消失吗。对嘛?

//引用:给指针取一个别名 

//& 

//数据类型& 变量名 = 变量;

//reference13-

void test() {

int a_very_very_very_very_very_very_very_very_long_array[8] = { 1,1 };

for (int i = 2; i < 8; ++i) {

a_very_very_very_very_very_very_very_very_long_array[i] = a_very_very_very_very_very_very_very_very_long_array[i – 1] * a_very_very_very_very_very_very_very_very_long_array[i – 1] + a_very_very_very_very_very_very_very_very_long_array[i – 2] * a_very_very_very_very_very_very_very_very_long_array[i – 2];

}

for (int i = 0; i < 8; ++i) {

cout << a_very_very_very_very_very_very_very_very_long_array[i] << ” “;

}

//太长了代码

cout << endl;

for (int i = 2; i < 8; ++i) {

a_very_very_very_very_very_very_very_very_long_array[i] = 0;

}

for (int i = 2; i < 8; ++i) {

int& pre1 = a_very_very_very_very_very_very_very_very_long_array[i – 1];

int& pre2 = a_very_very_very_very_very_very_very_very_long_array[i – 2];

int& now = a_very_very_very_very_very_very_very_very_long_array[i];

//referenced it

now = pre1 * pre1 + pre2 * pre2;

}

for (int i = 0; i < 8; ++i) {

cout << a_very_very_very_very_very_very_very_very_long_array[i] << ” “;

}

cout << endl;

}

int main() {

/*

int a = 1314;// 1. 定义int类型变量a,赋值为1314

int& b = a;// 2. 定义int类型的引用b,将b绑定到变量a上

b = 520;

cout << “a = ” << a << endl;

cout << “b = ” << b << endl;

// 3. 查看a和b的内存地址:地址完全相同(证明是同一块内存)

cout << “a的地址:” << &a << endl; 

cout << “b的地址:” << &b << endl;

*/

int a = 1314;// 1. 定义int类型变量a,赋值为1314

int* b = &a;// 

*b = 520;

cout << “a = ” << a << endl;

cout << “b = ” << *b << endl;//用指针写法需要多写三个*号 引用只需要写一个&

test();

return 0;

}

你写出的这两行代码涉及 C++ 中的引用(Reference) 特性,这是 C++ 区别于 C 语言的重要语法之一。下面我会逐行解析代码含义、引用的核心特性,以及实际使用中的注意事项,帮你彻底理解这段代码的本质。

一、逐行解析代码含义

cpp

int a = 1314;  // 1. 定义int类型变量a,赋值为1314

int& b = a;    // 2. 定义int类型的引用b,将b绑定到变量a上

关键概念:什么是 “引用”?

引用可以理解为变量的 “别名” —— 它不是一个新的变量(不占用额外内存),而是对已存在变量(这里的a)的 “另一个名字”。

就像生活中 “张三” 的别名是 “小张”,喊 “张三” 或 “小张” 都是指同一个人;代码中a和b也指向同一块内存空间,操作a或b会产生完全相同的效果。

二、代码的实际效果(通过示例验证)

我们可以通过一段完整代码,观察a和b的关联关系:

cpp

#include <iostream>

using namespace std;

int main() {

    int a = 1314;

    int& b = a;  // b是a的引用(别名)

    // 1. 输出a和b的值:结果相同(都指向同一块内存)

    cout << “初始值:a = ” << a << “, b = ” << b << endl;  // 输出:a = 1314, b = 1314

    // 2. 修改b的值:a的值也会同步变化

    b = 520;

    cout << “修改b后:a = ” << a << “, b = ” << b << endl;  // 输出:a = 520, b = 520

    // 3. 查看a和b的内存地址:地址完全相同(证明是同一块内存)

    cout << “a的地址:” << &a << endl;  // 例如输出:0x61fe1c

    cout << “b的地址:” << &b << endl;  // 输出与a完全相同:0x61fe1c

    return 0;

}

从运行结果能明确看到:

  • a和b的值始终同步(改一个,另一个也变);
  • a和b的内存地址完全一致(证明引用不占用额外内存,只是 “别名”)。

三、引用的核心语法规则(必须牢记)

这段代码看似简单,但背后隐藏着引用的不可违背的规则,一旦违反会直接编译报错:

  • 引用必须 “初始化”,且初始化后不能改绑
    • 错误写法 1:只定义引用不绑定变量(编译报错)
      int& b; // 错误!引用必须在定义时绑定一个已存在的变量
    • 错误写法 2:初始化后尝试绑定其他变量(编译报错)
      int c = 666;
      b = c; // 错误!不是“改绑c”,而是“把c的值赋给b(即a)”
  • 引用的类型必须与绑定的变量类型完全一致
    不能用int&绑定double变量,类型不匹配会编译报错:
    double d = 3.14;
    int& b = d; // 错误!引用类型(int&)与变量类型(double)不匹配
  • 不存在 “空引用”
    引用必须绑定到一个 “真实存在的变量” 上,不能像指针那样指向NULL(空地址):
    int& b = NULL; // 错误!引用无法绑定空地址

四、引用与指针的区别(为什么用引用?)

很多初学者会把引用和指针混淆,因为二者都能 “间接操作变量”,但本质差异很大。通过对比能更清晰理解引用的设计目的:

特性引用(Reference指针(Pointer
初始化必须在定义时绑定变量,不可改绑可定义后赋值,可指向不同变量
空值无空引用(必须绑定有效变量)有空指针(NULL/nullptr)
内存占用不占用额外内存(仅别名)占用内存(存储变量地址,如 4/8 字节)
语法复杂度直接使用(如b = 520),更简洁需解引用(如*p = 520),易出错
适用场景函数参数、返回值(避免拷贝)动态内存分配、数组遍历等

你的代码中,int& b = a的核心价值是:用更简洁的语法间接操作a,且避免指针的空值风险和语法复杂度

五、常见使用场景(代码示例)

除了直接绑定变量,引用更常用在函数参数和返回值中,核心目的是避免变量拷贝(提升效率) 和实现 “修改实参” 的效果

场景 1:函数参数用引用(修改实参)

cpp

// 用引用参数修改外部变量的值(无需指针,语法更简洁)

void addOne(int& x) {  // x是外部变量的引用

    x += 1;  // 修改x等同于修改外部变量

}

int main() {

    int a = 1314;

    addOne(a);  // 调用后,a的值变为1315

    cout << a;  // 输出1315

    return 0;

}

场景 2:函数返回引用(避免返回值拷贝)

cpp

// 返回数组的引用(避免拷贝整个数组,提升效率)

int& getArrayElement(int arr[], int index) {

    return arr[index];  // 返回arr[index]的引用

}

int main() {

    int arr[3] = {10, 20, 30};

    getArrayElement(arr, 1) = 200;  // 直接修改返回的引用(即arr[1])

    cout << arr[1];  // 输出200

    return 0;

}

总结

你写的int a = 1314; int& b = a;是 C++ 引用的基础用法,核心是:

  • b是a的别名,二者指向同一块内存;
  • 操作b等同于操作a,且引用不占用额外内存;
  • 引用必须初始化、不可改绑、类型匹配,这些规则是避免错误的关键。

理解引用后,你会发现它在函数设计、高效编程中非常实用,是 C++ 中简化代码、提升性能的重要工具。

你写出的 int* b = &a; 是 C/C++ 中指针(Pointer) 的基础用法,与上一轮提到的 “引用”(int& b = a)虽都能间接操作变量,但本质是完全不同的语法特性。下面将逐行解析代码含义、指针的核心逻辑、与引用的关键差异,以及实际使用中的注意事项,帮你彻底掌握指针的基础用法。

一、逐行解析代码含义(结合上一轮的 int a = 1314

先补充完整代码上下文,方便理解:

cpp

运行

int a = 1314;    // 1. 定义int类型变量a,赋值为1314(内存中会分配一块4字节空间存储1314)

int* b = &a;     // 2. 定义int类型的指针变量b,将a的内存地址赋值给b

关键概念:什么是 “指针”?

指针是一种专门存储 “内存地址” 的变量—— 它本身占用独立的内存空间,存储的内容不是具体的值(如 1314),而是另一个变量(如a)在内存中的 “位置编号”(即地址)。

可以类比生活中的 “快递柜”:

  • a 是快递柜里的 “包裹”(存储具体内容 1314);
  • a 的内存地址(比如0x61fe1c)是快递柜的 “柜门编号”;
  • b 是一张写着 “柜门编号” 的 “纸条”(本身是独立的载体,存储的是地址,而非包裹内容)。

二、代码的实际效果(通过示例验证指针的操作逻辑)

通过一段完整代码,观察指针如何通过 “地址” 间接操作变量a:

cpp

运行

#include <iostream>

using namespace std;

int main() {

    int a = 1314;    // 变量a:值=1314,地址=0x61fe1c(示例地址,实际由系统分配)

    int* b = &a;     // 指针b:存储a的地址(即b的值=0x61fe1c)

    // 1. 输出关键信息:区分“指针本身的值”“指针指向的值”“变量a的地址”

    cout << “a的值:” << a << endl;          // 输出a的具体值:1314

    cout << “a的地址(&a):” << &a << endl; // 输出a的内存地址:0x61fe1c

    cout << “指针b的值(存储的地址):” << b << endl; // 输出b存储的地址:0x61fe1c(与&a相同)

    cout << “指针b指向的值(*b):” << *b << endl; // 输出b指向的变量的值:1314(即a的值)

    // 2. 通过指针修改a的值:用“*b”(解引用)操作指向的变量

    *b = 520; // 含义:找到b存储的地址对应的变量(即a),将其值改为520

    cout << “\n修改*b后:” << endl;

    cout << “a的值:” << a << endl;          // 输出:520(a被间接修改)

    cout << “指针b指向的值(*b):” << *b << endl; // 输出:520

    // 3. 指针本身的地址(证明指针是独立变量,占用内存)

    cout << “指针b自身的地址(&b):” << &b << endl; // 输出:0x61fe18(与a的地址不同,证明b是独立变量)

    return 0;

}

运行结果会清晰体现指针的核心逻辑:

  • 指针b存储的是a的地址(b = &a);
  • 需通过解引用运算符* 才能访问指针指向的变量(*b等价于a);
  • 指针b本身是独立变量,有自己的内存地址(&b与&a不同),占用 4 字节(32 位系统)或 8 字节(64 位系统)内存。

三、指针的核心语法规则(必须牢记,避免编译错误)

int* b = &a; 看似简单,但指针的语法规则比引用更严格,违反会导致编译报错或运行时崩溃:

1. 指针的 “类型匹配” 规则

指针的类型必须与 “指向的变量类型” 完全一致(除非用void*万能指针,但需谨慎):

  • 正确:int* b = &a;(int*指针指向int变量a);
  • 错误:double* b = &a;(double*指针不能指向int变量,类型不匹配,编译报错)。

2. 指针的 “初始化” 与 “空指针”

  • 指针可以先定义后赋值(区别于引用必须初始化):cpp

    运行




    int* b;    // 允许先定义指针(未初始化时,值是随机的“野指针”,危险!)
  • int a = 1314;
  • b = &a;    // 后续赋值为a的地址(此时b是有效指针)




  • 避免 “野指针”:未指向有效变量时,建议赋值为空指针nullptr(C++11 后)或NULL(本质是 0):cpp

    运行




    int* b = nullptr; // 空指针:明确表示“暂时未指向任何有效变量”





  • ⚠️ 注意:不能对空指针使用*b(解引用空指针会导致程序崩溃)。

3. 指针的 “解引用” 与 “取地址”

  • 取地址运算符&:获取变量的内存地址(如&a即 “a 的地址”);
  • 解引用运算符*:通过指针存储的地址,访问对应的变量(如*b即 “b 指向的变量”);
  • 二者是 “逆操作”:*(&a) == a(先取 a 的地址,再解引用,结果还是 a)。

四、指针与引用的核心差异(避免混淆)

你之前写的 “引用”(int& b = a)和现在的 “指针”(int* b = &a)都能间接操作a,但本质是两种不同的语法,差异如下表(关键区分点):

对比维度指针(int* b = &a引用(int& b = a
本质独立变量,存储 “地址”变量的 “别名”,不存储地址(无独立内存)
初始化要求可先定义后赋值(允许int* b;)必须在定义时绑定变量(禁止int& b;)
指向变更可修改指向(如b = &c;,改为指向 c)一旦绑定,不可改绑其他变量
空值支持支持空指针(int* b = nullptr)无空引用(必须绑定有效变量)
操作语法需解引用(*b = 520)直接使用(b = 520),更简洁
内存占用占用内存(4/8 字节,取决于系统位数)不占用额外内存
安全性风险较高(野指针、空指针解引用会崩溃)安全性高(无野引用、空引用问题)

五、指针的常见使用场景(代码示例)

指针是 C/C++ 的核心特性,适用场景比引用更广泛,尤其是需要 “动态内存”“数组操作”“多态” 等场景:

场景 1:操作数组(数组名本质是 “指向首元素的指针”)

数组的内存是连续的,指针可以通过 “指针移动” 遍历数组:

cpp

运行

#include <iostream>

using namespace std;

int main() {

    int arr[3] = {10, 20, 30};

    int* p = arr; // 数组名arr等价于&arr[0](首元素地址),p指向arr[0]

    // 遍历数组:指针移动(p++即“指向当前元素的下一个元素”)

    for (int i = 0; i < 3; i++) {

        cout << “arr[” << i << “] = ” << *p << endl; // 解引用p,获取当前元素

        p++; // 指针移动(地址增加4字节,因int占4字节)

    }

    return 0;

}

// 输出:

// arr[0] = 10

// arr[1] = 20

// arr[2] = 30

场景 2:动态内存分配(用new/delete管理内存)

指针可以指向 “运行时才分配的内存”(动态内存),这是引用无法实现的:

cpp

运行

#include <iostream>

using namespace std;

int main() {

    // 动态分配1个int大小的内存,指针p指向这块内存

    int* p = new int; 

    *p = 1314; // 给动态内存赋值

    cout << “动态内存的值:” << *p << endl; // 输出:1314

    // 释放动态内存(必须手动释放,否则内存泄漏)

    delete p; 

    p = nullptr; // 释放后赋值为空指针,避免野指针

    return 0;

}

场景 3:函数参数传递(修改实参,或传递大型数据)

指针作为函数参数,可实现 “修改外部变量”(类似引用),且支持传递数组、动态内存等:

cpp

运行

#include <iostream>

using namespace std;

// 用指针参数修改外部变量

void multiplyBy2(int* x) {

    if (x != nullptr) { // 先判断指针非空,避免崩溃

        *x *= 2; // 解引用指针,修改外部变量

    }

}

int main() {

    int a = 1314;

    multiplyBy2(&a); // 传递a的地址

    cout << a << endl; // 输出:2628(a被修改)

    return 0;

}

总结

你写的 int* b = &a; 是指针的基础用法,核心要点如下:

  • b 是指针变量,存储的是a的内存地址(而非a的值);
  • 必须通过解引用*b 才能访问或修改a的值;
  • 指针是独立变量,有自己的内存地址,支持后续修改指向(如b = &c);
  • 与引用相比,指针更灵活(支持动态内存、数组操作),但安全性更低(需避免野指针、空指针解引用)。

指针是 C/C++ 的难点,但也是核心能力 —— 掌握指针后,才能理解动态内存、数据结构(如链表、树)等更复杂的编程概念。

Day22 November 3 2025

//13 – 2 reference characteristics

#include <iostream>

using namespace std;

//two charaters of reference:

//1.must initialization(必须初始化)

//2.Can’t be modified after initialization(初始化以后无法修改)

//跟指针定义的区别,指针不一定要初始化,定义后只要不是指针常量后续都可以修改

//因为引用一定要初始化,所以根本不会有空指针问题

//引用为后续STL源码打基础

int main() {

//int& a;//error:must have an equal symbol

int a = 3, c = 6;

int& b = a; 

b = c;//b still a reference to a,b=6

cout << a << b << c << endl; // 666

return 0;

}

//13 – 3 the essence of reference

//引用的本质

#include <iostream>

using namespace std;

//引用 解引用

//引用的底层特性 其实就是指针常量

int main() {

int a = 520;

//int& b = a;

/*

00007FF7ACEE1865  lea         rax,[a]  

00007FF7ACEE1869  mov         qword ptr [b],rax 

*/

//b = 1314;//引用后面赋值不需要写星号

/*

00007FF7ACEE186D  mov         rax,qword ptr [b]  

00007FF7ACEE1871  mov         dword ptr [rax],522h 

*/

//汇编代码一模一样,说明引用就是指针常量

//打断点可看窗口 反汇编 deassembly

int* const b = &a;//pointer constant  (指针常量) 的初始化

/*

00007FF64AF11865  lea         rax,[a]  

00007FF64AF11869  mov         qword ptr [b],rax  

*/

*b = 1314;//指针常量后面赋值需要写星号

//这里是解引用 dereference

/*

00007FF64AF1186D  mov         rax,qword ptr [b]  

00007FF64AF11871  mov         dword ptr [rax],522h  

*/

return 0;

}

//13 – 4 function passing a reference as a parameter

//引用作为函数传参

#include <iostream>

using namespace std;

int countAndSum(int arr[], int size, int target, int& count) {

int sum = 0;

cout << &count << endl;//打印count的地址

for (int i = 0; i < size; ++i) {

if (arr[i] == target) {

count++; //初始化时用引用 传进来的地址也一模一样

sum += arr[i];

}

}

return sum; //计算所有等于target的数的和

}

int countAndSum2(int arr[], int size, int target, int* count) {//使用指针

int sum = 0;

cout << count << endl;//打印count的地址

for (int i = 0; i < size; ++i) {

if (arr[i] == target) {

*count++; 

sum += arr[i];

}

}

return sum; //计算所有等于target的数的和

}

struct S {

int a, b, c, d, e, f, g;

};

//void printS(S s) {//传参是个结构体

// cout << &s << endl;//如果结构体不用引用,传进来前的地址输出出来,不是同一个地址

// //C++ 和其他语言区别 结构体 作为参数时 会拷贝一份新的数据出来

// //当结构体非常大 如有个非常大的数组a[1000000]时 拷贝就相当耗时了

//

// cout << s.a << s.b << s.c << s.d << s.e << s.f << s.g << endl;

//}

void printS2(S &s) {//传参是个结构体

cout << &s << endl;//传进来前的地址输出出来,不是同一个地址

//C++ 和其他语言区别 结构体 作为参数时 会拷贝一份新的数据出来

//当结构体非常大 如有个非常大的数组a[1000000]时 拷贝就相当耗时了

//所以加上引用就能避免拷贝 让传参和实际的参数同一个地址,这就是引用在函数传参时的作用

cout << s.a << s.b << s.c << s.d << s.e << s.f << s.g << endl;

}

int main() {

int arr[]{ 1,2,3,2,4,5,6,4,3,2 };//10 numbers

//返回统计数组中值为2的元素个数以及返回和

int c = 0;

cout << &c << endl;//打印c的地址

int sum = countAndSum(arr, 10, 2, c);

cout << sum << ” ” << c << endl;

S s = { 1,2,3,4,5,6,7 };

cout << &s << endl;

//如果结构体不用引用 传进来后的地址输出出来,不是同一个地址

printS2(s);

return 0;

}

//13 – 5 reference as a function return value

//引用作为函数返回值

#include <iostream>

using namespace std;

int getArrayValue(int arr[], int index) {

return arr[index];//上面不加引用这里返回的是一个值

}

int &getArrayValue2(int arr2[], int index) {

return arr2[index];//上面加了引用这里返回一个别名

}

//STL底层源码里面有很多 面向对象 中括号运算符重载也有这种

int main() {

int a[] = { 8,7,6,5,4,3 };

//我想打印出第四个元素5

cout << getArrayValue(a, 3) << endl;

//想把下标3这个值改掉 但是不想访问数组 需要加上引用

getArrayValue2(a, 3) = 999; //直接对函数赋值也没有报错,这里相当于a[3]=999,然后函数就可以作为左值来赋值了

cout << getArrayValue2(a, 3) << endl;

return 0;

}

//13 – 6 constant reference

//常量引用

//常量引用非常广泛 尤其是在STL实现上

#include <iostream>

#include <vector>

using namespace std;

struct S {

int a, b, c, d, e, f;

};

//加引用少一次拷贝 让它更加高效

void printS(S& s) {

s.b = 520; //加const 在引用前变 常量引用 防止有人不小心把这个值改了

cout << s.a << s.b << s.c << s.d << s.e << s.f << endl;

}

void printS2(const S& s) {

//同一个对象很难避免写函数的人不去修改它 为了避免别人修改 用const关键词修饰

// 在很多STL底层源码有

//s.b = 520; //加const 在引用前变 常量引用 防止有人不小心把这个值改了

cout << s.a << s.b << s.c << s.d << s.e << s.f << endl;

}

int main() {

int a;

const int& b = a;

//引用 =指针常量

//常量引用 = 常量指针常量

S s = { 1,2,3,4,5,6 };

printS2(s);

vector <int>a;//F12查看源代码

return 0;

}

//13 – 7 pointer reference

//指针引用 

//*&

#include <iostream>

using namespace std;

void allocMemory1(char * ptr,int bytes) {//传进去一个char类型的指针变量 和一个 字节数bytes

//这个函数作用 传入一个指针 和 字节数,然后从堆heap上 申请对应字节数的内存 并且把地址赋值给ptr

ptr = new char[bytes];

cout << “ptr 的 地址:” << &ptr << endl;//ptr虽然本身也是一个地址,但我们想要ptr它的地址

}

void test1() {

//test1这个函数 首先定义了一个初始化的指针 并且把它初始化为NULL

char* p = NULL;

allocMemory1(p, 5); //然后调用allocMemory1这个函数 期望是这个函数申请的那块内存的首地址

cout << (void*)p << endl;//然后把首地址转换为void*以后把它打印出来(转换成一个通用的指针类型)

//为什么要转换  原因是不转换的话 C++会认为它是一个C风格的字符串 字符串如果为空 直接输出程序会导致崩溃

cout << “p 的 地址:” << &p << endl;

//这里的p是实参 ptr是形参

}

void allocMemory2(char*& ptr, int bytes) {//在*后加上引用&  引用的是一个指针变量

//上面加上引用后 ptr就变成p的别名了

//传进去一个char类型的指针变量 和一个 字节数bytes

//这个函数作用 传入一个指针 和 字节数,然后从堆heap上 申请对应字节数的内存 并且把地址赋值给ptr

ptr = new char[bytes];

cout << “ptr 的 地址:” << &ptr << endl;//ptr虽然本身也是一个地址,但我们想要ptr它的地址

}

void test2() {

//test1这个函数 首先定义了一个初始化的指针 并且把它初始化为NULL

char* p = NULL;

allocMemory2(p, 5); //然后调用allocMemory1这个函数 期望是这个函数申请的那块内存的首地址

//上面加上引用后 ptr就变成p的别名了 所以两个是同一个变量

//实参 p  和形参 ptr是同一块地址了 打印的地址就不再是空了 这就是指针引用的作用

cout << (void*)p << endl;//然后把首地址转换为void*以后把它打印出来(转换成一个通用的指针类型)

//为什么要转换  原因是不转换的话 C++会认为它是一个C风格的字符串 字符串如果为空 直接输出程序会导致崩溃

cout << “p 的 地址:” << &p << endl;

//这里的p是实参 ptr是形参

}

int main() {

//test1();

test2();

return 0;

}