
06_C指针与引用6.1 指针6.1.1 指针的概念指针是C中的一种变量类型用于存储另一个变量的内存地址。指针变量通过解引用操作符*可以访问或修改其所指向的变量的值。指针的使用可以提高程序的效率尤其是在处理大型数据结构时。指针和普通变量的区别普通变量直接存储数据值而指针变量存储的是另一个变量的地址。指针变量需要使用地址操作符来获取变量的地址。指针变量可以通过 * 操作符操作指针变量指向的内存空间这个过程称为解引用6.1.2 指针的定义和使用指针的定义语法格式数据类型*指针变量名;示例#includeiostreamusingnamespacestd;intmain(){intvar42;// 定义一个整数变量int*ptrvar;// 定义一个指针变量并初始化为var的地址cout变量var的值: varendl;// 输出变量var的值cout指针ptr指向的值: *ptrendl;// 通过指针访问var的值*ptr100;// 通过指针修改var的值cout修改后变量var的值: varendl;// 输出修改后的var的值return0;}6.1.3 指针所占用的内存空间指针变量本身也占用内存空间通常在32位系统中占4字节在64位系统中占8字节。示例#includeiostreamusingnamespacestd;intmain(){intvar42;int*ptrvar;cout变量var的地址: varendl;// 输出变量var的地址cout指针ptr的值var的地址: ptrendl;// 输出指针ptr的值cout指针ptr所占用的内存大小: sizeof(ptr) 字节endl;// 输出指针大小return0;}6.1.4 空指针和野指针空指针nullptr是指不指向任何有效内存地址的指针通常为0使用空指针可以避免意外访问无效内存。野指针是指指向已经释放或未分配内存的指针使用野指针会导致未定义行为。空指针示例#includeiostreamusingnamespacestd;intmain(){int*ptrnullptr;// 定义一个空指针if(ptrnullptr){cout指针ptr是空指针endl;}// 注意不要解引用空指针或野指针否则会导致程序崩溃return0;}野指针示例#includeiostreamusingnamespacestd;intmain(){int*ptr(int*)0x1234;// 未初始化的指针可能是野指针// 注意不要解引用野指针否则会导致程序崩溃// cout *ptr endl; // 这行代码是危险的return0;}6.1.5 const修饰指针const修饰指针有三种情况指向常量的指针const int* ptr不能通过指针修改所指向的值但可以改变指针本身的地址。常量指针int* const ptr指针本身的地址不能改变但可以通过指针修改所指向的值。指向常量的常量指针const int* const ptr既不能改变指针本身的地址也不能通过指针修改所指向的值。示例#includeiostreamusingnamespacestd;intmain(){intvar42;constint*ptr1var;// 指向常量的指针// *ptr1 100; // 错误不能通过ptr1修改var的值intnewVar100;ptr1newVar;// 可以改变ptr1的地址int*constptr2var;// 常量指针*ptr2100;// 可以通过ptr2修改var的值// ptr2 newVar; // 错误不能改变ptr2的地址constint*constptr3var;// 指向常量的常量指针// *ptr3 200; // 错误不能通过ptr3修改var的值// ptr3 newVar; // 错误不能改变ptr3的地址coutvar的值: varendl;// 输出var的值: 100return0;}技巧就近原则即const修饰符靠近它所修饰的对象。6.1.6 指针和数组指针和数组有密切的关系数组名实际上是一个指向数组首元素的指针常量。可以通过指针来遍历和操作数组元素。示例#includeiostreamusingnamespacestd;intmain(){intarr[]{10,20,30,40,50};int*ptrarr;// 数组名作为指向首元素的指针for(inti0;i5;i){coutarr[i] *(ptri)endl;// 通过指针访问数组元素}return0;}6.1.7 指针运算指针可以进行算术运算如加法、减法、递增和递减。指针运算会根据指针所指向的数据类型自动调整步长。示例#includeiostreamusingnamespacestd;intmain(){intarr[]{10,20,30,40,50};int*ptrarr;// 指向数组首元素cout指针初始值: ptr, 指向的值: *ptrendl;ptr;// 指针递增指向下一个元素cout指针递增后: ptr, 指向的值: *ptrendl;ptr2;// 指针加2跳过一个元素cout指针加2后: ptr, 指向的值: *ptrendl;ptr--;// 指针递减指向上一个元素cout指针递减后: ptr, 指向的值: *ptrendl;return0;}6.2 引用6.2.1 引用的基本使用引用是C中的一种别名机制用于为变量创建一个新的名字。引用必须在定义时初始化并且一旦绑定到一个变量就不能再绑定到其他变量。示例#includeiostreamusingnamespacestd;intmain(){intvar42;intrefvar;// 定义引用ref绑定到varcout变量var的值: varendl;// 输出变量var的值cout引用ref的值: refendl;// 输出引用ref的值ref100;// 通过引用修改var的值cout修改后变量var的值: varendl;// 输出修改后的var的值return0;}6.2.2 常量引用常量引用const引用是指引用所绑定的变量不能通过该引用进行修改。常量引用可以绑定到临时变量或不同类型的变量常用于函数参数传递。示例#includeiostreamusingnamespacestd;voidprintValue(constintref){cout引用的值: refendl;// ref 100; // 错误不能通过常量引用修改值}intmain(){intvar42;printValue(var);// 传递变量var的引用printValue(100);// 传递临时变量的引用return0;}6.2.3 引用的本质引用在底层实现上通常被编译器处理为指针常量但引用的语法更简洁使用起来更安全。引用不能为null必须绑定到一个有效的变量。示例//发现是引用转换为 int* const ref a; void func(int ref){ ref 100; // ref是引用转换为*ref 100 } int main(){ int a 10; //自动转换为 int* const ref a; 指针常量是指针指向不可改也说明为什么引用不可更改 int ref a; ref 20; //内部发现ref是引用自动帮我们转换为: *ref 20; cout a: a endl; cout ref: ref endl; func(a); return 0; }6.3 指针与引用的比较6.3.1 相同点都可以间接访问同一个变量或对象支持通过“别名”来修改原始数据。都可以作为函数参数将实参传入函数体内并允许在函数中修改调用者的数据所谓“按地址/引用传递”。在底层实现上引用通常会被编译器转换为指针来处理因此两者在生成的机器代码层面很多时候非常接近。6.3.2 主要区别是否必须初始化指针可以先定义再赋值也可以为nullptr。引用定义时必须立刻绑定到一个有效对象上不能“悬空”。是否允许指向/绑定为空指针可以指向nullptr用来表示“当前不指向任何对象”。引用语义上不允许为空尽管通过极端手段可以制造“空引用”但会导致未定义行为一般认为引用一定代表一个有效对象。是否允许改变指向/绑定的目标指针可以在生命周期内多次改变指向的地址。引用一旦绑定后就不能再“改绑”整个生命周期内都代表同一个对象。使用语法是否需要解引用指针访问所指向对象要使用解引用操作符*或-。引用当作普通变量名使用即可不需要额外的解引用语法。内存占用与本质指针是一个独立的变量占用一个指针大小的内存32位一般为4字节64位一般为8字节。引用从语法层面看不占用独立内存是被引用对象的“别名”实际编译器实现时通常会用“指向该对象的指针常量”来完成。使用场景建议指针适合需要“可为空”、需要动态改变指向、需要进行指针运算如遍历数组、手动管理动态内存的场合。引用适合用来做函数参数和返回值更符合直观语义代码更简洁安全。6.3.3 简单示例对比#includeiostreamusingnamespacestd;voidaddByPointer(int*p){// 使用指针if(p){*p10;// 需要解引用}}voidaddByReference(intr){// 使用引用r10;// 直接当变量用}intmain(){inta10;int*pa;// 指针可以为nullptr也可以随时改指向addByPointer(p);cout通过指针修改后的 a aendl;intrefa;// 引用必须初始化并且终身绑定 aaddByReference(ref);cout通过引用修改后的 a aendl;return0;}