永清网站建设,家具品牌网站,片网站无法显示,wordpress 分享文章标题C的引用 前言一、C引用概念二、引用特性交换指针引用 三、常引用保证值不变权限的方法权限的放大权限的缩小权限的平移类型转换临时变量 四、引用的使用场景1. 做参数2. 做返回值 五、传值、传引用效率比较值和引用的作为返回值类型的性能比较 六、引用和指针的区别引用和指针的… C的引用 前言一、C引用概念二、引用特性交换指针引用 三、常引用保证值不变权限的方法权限的放大权限的缩小权限的平移类型转换临时变量 四、引用的使用场景1. 做参数2. 做返回值 五、传值、传引用效率比较值和引用的作为返回值类型的性能比较 六、引用和指针的区别引用和指针的注意点引用和指针的不同点 七、测试代码展示.cpp 前言
C的引用是别名它为已存在的对象提供了另一个名称。一旦引用被初始化指向一个对象它就不能再指向其他对象。引用必须在声明时初始化并且必须初始化为有效的对象或字面量。引用通常用于函数参数和返回值以实现按引用传递和返回。此外它们也常用于大型对象和数组以避免复制的开销。C11引入了右值引用和移动语义允许更高效的资源管理和性能优化。总的来说C的引用是一种强大的工具能够增强代码的可读性和性能。 一、C引用概念
引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。
比如李逵在家称为铁牛江湖上人称黑旋风。 在C中引用是一个别名用于已经存在的变量或对象。引用提供了对变量的间接访问通过引用可以通过不同的名称来访问同一变量。
类型 引用变量名(对象名) 引用实体
void TestRef()
{int a 10;int ra a;//定义引用类型printf(%p\n, a);printf(%p\n, ra);
}注意引用类型必须和引用实体是同种类型的
引用的定义使用符号如下所示
int x 10;
int ref x;在这个例子中ref是x的引用它是x的别名。现在ref和x可以互换使用任何对ref的更改将反映在x上反之亦然。
二、引用特性
void TestRef()
{int a 10;// int ra; // 该条语句编译时会出错int ra a;int rra a;printf(%p %p %p\n, a, ra, rra);
}引用有以下几个特点 引用必须在定义时进行初始化一旦初始化完成后就无法更改引用的绑定。 引用必须与其所引用的对象具有相同的类型。 引用可以作为函数的参数和返回值通过引用参数传递参数可以避免复制大型对象的开销。 一个变量可以有多个引用
引用与指针不同指针是一个对象可以指向任何其他对象而引用始终指向同一个对象。另外引用在使用时不需要解引用操作符(*)因为它本身就是对象的别名。
引用的使用可以简化代码并提高可读性它常用于函数参数传递、函数返回值、以及在循环中使用。
void increment(int i) {i;
}int main() {int x 10;increment(x);cout x; // 输出11return 0;
}在上面的例子中increment函数接受一个引用参数i对该引用进行递增操作。在main函数中将变量x传递给increment函数后x的值被递增为11。因为参数是引用类型所以对i的修改会直接影响到x。
交换
指针
void Swap(int* a, int* b)
{int tmp *a;*a *b;*b tmp;
}
int main{int x 0, y 10;cout x y endl;Swap(x, y);cout x y endl;return 0;}引用
void _Swap(int a, int b)
{int tmp a;a b;b tmp;
}
int main{int x 0, y 10;cout x y endl;_Swap(x, y);cout x y endl;return 0;}三、常引用
保证值不变
C中的常引用是指使用const关键字修饰的引用即被引用的对象不能被修改。常引用和普通引用的主要区别在于常引用所引用的对象在引用过程中不能被修改。
常引用的语法形式如下
const T ref;其中T是被引用对象的类型。常引用可以指向任何类型的对象包括基本类型、自定义类型、指针等。
常引用在函数参数传递中很常用可以用于避免拷贝大对象同时又不希望对对象进行修改。在函数定义时使用常引用作为参数可以防止函数对参数进行修改。
需要注意的是引用作为函数参数时函数内部对引用的修改也会反映到函数外部的变量上。
void print(const int i) {cout i;
}int main() {int x 10;print(x);return 0;
}在上述例子中print函数接受一个const引用参数i这意味着i不可修改。在main函数中将变量x传递给print函数后print函数无法修改x的值。这样做可以确保函数不会意外地修改传递给它的参数。
权限的方法
void TestConstRef()
{const int a 10;//int ra a; // 该语句编译时会出错a为常量const int ra a;// int b 10; // 该语句编译时会出错b为常量const int b 10;double d 12.34;//int rd d; // 该语句编译时会出错类型不同const int rd d;
}权限的放大
const int a 10;
int ra a; // 该语句编译时会出错a为常量int p a;这样是可取的因为这是赋值语句
如上const int a 10;按照语法来理解是可读但不可写我们使用一个可读可写的int类型来引用就会报错所以这种方法是不可取的
const int* a m;
int* ra a; 这也是权限的放大不可以
权限的缩小
int a 10;
const int ra a; 如上 int a 10;按照语法来理解是可读可写我们使用一个可读的const int类型来引用是没有问题的所以这种方法是可取的
int p a;//p ra;这样是可取的因为这是赋值语句
注意ara也会因为ra是a的别名但是ra不能因为ra是常量总结来说就是不能通过ra来修改 int* a m;
const int* ra a; 这也是权限的缩小可以
权限的平移 int a 10;int ra a;int rra a;
const int b 10;
const int rb b;如上 int a 10;按照语法来理解是可读可写我们使用一个可读可写的int类型来引用是没有问题的所以这种方法是可取的同理const的修饰也是一样的
类型转换 double d 12.34;//int rd d; // 该语句编译时会出错类型不同const int rd d;如上为什么int rd d;不行而 const int rd d;确可以是因为类型转换会生成临时变量类型转换是将一个数据类型的值转换为另一个数据类型的值而不是直接修改原始值。因此在执行类型转换时会创建一个新的变量来存储转换后的值并且可以在需要的地方使用。这时的临时变量是具有常性的使用int rd d; 就相当于权限的放大会报错需要使用const修饰
临时变量
int x 0,y 1;
//int p x y;是不可以的
const int p x y;//是可以的,和上面一样是临时变量的原因除了类型转换之外还有以下几种情况会生成临时变量 函数返回值当一个函数返回一个临时变量时编译器会在函数结束时生成一个临时变量并将其复制到函数返回的地方。 表达式计算在进行表达式计算时如果表达式中包含临时变量的创建和销毁编译器会在需要的地方生成临时变量。 函数调用当调用函数时会将实参传递给形参。如果实参的类型与形参的类型不匹配编译器可能会生成临时变量来进行类型转换。 对象初始化当创建对象时如果使用了拷贝构造函数编译器会生成一个临时变量来初始化新对象。 运算符重载当重载一个运算符时可能会生成临时变量来进行操作。
需要注意的是编译器为了优化性能可能会对临时变量进行优化比如使用编译器自动生成的构造函数、析构函数等。因此生成临时变量并不一定会带来显著的性能损耗。
四、引用的使用场景
1. 做参数
void Swap(int left, int right)
{int temp left;left right;right temp;
}2. 做返回值
int Count()
{static int n 0;n;// ...return n;
}下面代码输出什么结果为什么
int Add(int a, int b)
{int c a b;return c;
}
int main()
{int ret Add(1, 2);Add(3, 4);cout Add(1, 2) is : ret endl;return 0;
}注意如果函数返回时出了函数作用域如果返回对象还在(还没还给系统)则可以使用引用返回如果已经还给系统了则必须使用传值返回。
五、传值、传引用效率比较
以值作为参数或者返回值类型在传参和返回期间函数不会直接传递实参或者将变量本身直接返回而是传递实参或者返回变量的一份临时的拷贝因此用值作为参数或者返回值类型效率是非常低下的尤其是当参数或者返回值类型非常大时效率就更低。
#include time.h
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A a) {}
void TestRefAndValue()
{A a;// 以值作为函数参数size_t begin1 clock();for (size_t i 0; i 10000; i)TestFunc1(a);size_t end1 clock();// 以引用作为函数参数size_t begin2 clock();for (size_t i 0; i 10000; i)TestFunc2(a);size_t end2 clock();// 分别计算两个函数运行结束后的时间cout TestFunc1(A)-time: end1 - begin1 endl;cout TestFunc2(A)-time: end2 - begin2 endl;
}
int main()
{TestRefAndValue();return 0;
}值和引用的作为返回值类型的性能比较
#include time.h
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A TestFunc2() { return a; }
void TestReturnByRefOrValue()
{// 以值作为函数的返回值类型size_t begin1 clock();for (size_t i 0; i 100000; i)TestFunc1();size_t end1 clock();// 以引用作为函数的返回值类型size_t begin2 clock();for (size_t i 0; i 100000; i)TestFunc2();size_t end2 clock();// 计算两个函数运算完成之后的时间cout TestFunc1 time: end1 - begin1 endl;cout TestFunc2 time: end2 - begin2 endl;
}
int main()
{//TestRefAndValue();TestReturnByRefOrValue();return 0;
}通过上述代码的比较发现传值和指针在作为传参以及返回值类型上效率相差很大。
六、引用和指针的区别
引用和指针的注意点
在语法概念上引用就是一个别名没有独立空间和其引用实体共用同一块空间。
int main()
{int a 10;int ra a;couta aendl;coutra raendl;return 0;
}在底层实现上实际是有空间的因为引用是按照指针方式来实现的。
int main()
{int a 10;int ra a;ra 20;int* pa a;*pa 20;return 0;
}我们来看下引用和指针的汇编代码对比
引用和指针的不同点 引用概念上定义一个变量的别名指针存储一个变量地址。 引用在定义时必须初始化指针没有要求 引用在初始化时引用一个实体后就不能再引用其他实体而指针可以在任何时候指向任何一个同类型实体 没有NULL引用但有NULL指针 在sizeof中含义不同引用结果为引用类型的大小但指针始终是地址空间所占字节个数(32位平台下占4个字节) 引用自加即引用的实体增加1指针自加即指针向后偏移一个类型的大小 有多级指针但是没有多级引用 访问实体方式不同指针需要显式解引用引用编译器自己处理 引用比指针使用起来相对更安全
注意
int* ptr NULL;
int r ptr;这样编译器是不会报错的
int main()
{int* ptr NULL;int r *ptr;cout r endl;return 0;
}这样才会报错因为指针的解引用是在使用的阶段在底层是r存放的是ptr的地址
七、测试代码展示
.cpp
#include iostream
using namespace std;
//void TestRef()
//{
// int a 10;
// int ra a;//定义引用类型
// printf(%p\n, a);
// printf(%p\n, ra);
//}
void TestRef()
{int a 10;// int ra; // 该条语句编译时会出错int ra a;int rra a;printf(%p %p %p\n, a, ra, rra);
}
void increment(int i) {i;
}
void Swap(int* a, int* b)
{int tmp *a;*a *b;*b tmp;
}
void _Swap(int a, int b)
{int tmp a;a b;b tmp;
}
//int main()
//{
// //TestRef();
// //int x 10;
// //increment(x);
// //cout x; // 输出11
// int x 0, y 100;
// cout x y endl;
// //Swap(x, y);
// _Swap(x, y);
// cout x y endl;
// return 0;
//}
//void print(const int i) {
// cout i;
//}
//
//int main() {
// int x 10;
// /*int a 10;
// const int ra a;
// int p a;*/
// const int a 10;
// //int ra a; // 该语句编译时会出错a为常量
// int p a;
// print(p);
// return 0;
//}
//int Add(int a, int b)
//{
// int c a b;
// return c;
//}
//int main()
//{
// int ret Add(1, 2);
// Add(3, 4);
// cout Add(1, 2) is : ret endl;
// return 0;
//}
#include time.h
//struct A { int a[10000]; };
//void TestFunc1(A a) {}
//void TestFunc2(A a) {}
//void TestRefAndValue()
//{
// A a;
// // 以值作为函数参数
// size_t begin1 clock();
// for (size_t i 0; i 10000; i)
// TestFunc1(a);
// size_t end1 clock();
// // 以引用作为函数参数
// size_t begin2 clock();
// for (size_t i 0; i 10000; i)
// TestFunc2(a);
// size_t end2 clock();
// // 分别计算两个函数运行结束后的时间
// cout TestFunc1(A)-time: end1 - begin1 endl;
// cout TestFunc2(A)-time: end2 - begin2 endl;
//}
//struct A { int a[10000]; };
//A a;值返回
//A TestFunc1() { return a; }引用返回
//A TestFunc2() { return a; }
//void TestReturnByRefOrValue()
//{
// // 以值作为函数的返回值类型
// size_t begin1 clock();
// for (size_t i 0; i 100000; i)
// TestFunc1();
// size_t end1 clock();
// // 以引用作为函数的返回值类型
// size_t begin2 clock();
// for (size_t i 0; i 100000; i)
// TestFunc2();
// size_t end2 clock();
// // 计算两个函数运算完成之后的时间
// cout TestFunc1 time: end1 - begin1 endl;
// cout TestFunc2 time: end2 - begin2 endl;
//}
//int main()
//{
// //TestRefAndValue();
// TestReturnByRefOrValue();
// return 0;
//}
int main()
{int* ptr NULL;int r *ptr;cout r endl;return 0;
}