//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;
}