C/C++陷阱——临时变量的产生和特性

简介: C/C++陷阱——临时变量的产生和特性

C/C++陷阱——临时变量的产生和特性

在学习C++常引用时,有这样一段代码引起了我的注意:

int a = 1;
double& b = a;

当我编译这段代码时,竟然报错了:

按理来说,初始化引用时不能涉及权限的放大(如用const int初始化int&),但是这里只是权限的平移,为什么会错误呢?

我们可以看到报错信息里有这样一句话:非常量限定,这指的又是什么呢?

这就是本次要讨论的重点——临时变量的产生和特性

在C和C++中,临时变量通常指的是临时创建并存储数据的变量,它们在表达式求值或函数调用中起到临时存储值的作用。这些临时变量通常是由编译器自动生成的,无需程序员显式声明或管理

临时变量的产生

临时变量主要在下面这两种情况下产生:

  1. 类型转换

当需要将一个数据类型转换为另一个数据类型时,通常会生成临时变量来保存转换后的值

例如:

int a = 10;
double b = a;

我们都知道,将整型变量a赋值给双精度浮点型变量b时,涉及到了隐式类型转换,但我们可能大多数都忽略了这个过程中就产生了一个临时变量来存储转换后的值,最后变量b接收的就是这个临时变量。因为只有这样,我们才能在类型转换的过程中确保变量a的数据是完整的、合规的、安全的。

又例如:

int a = 1;
int* pt = (int*)a;

我们总不能说,将整型变量a强制转换为整型指针后,整型变量a就变成指针了吧。

  1. 函数调用

当一个函数返回一个值时,通常会生成临时变量来存储函数的返回结果。这个临时变量可以被赋给其他变量或用于进一步的操作。

int Add(int a, int b)
{
  int sum = a + b;
  return sum;
}
int main()
{
  int ret = Add(1, 2);
  return 0;
}

Add函数就是一个简单的两数相加的函数。那么小伙伴觉得这个函数的返回值是什么?是变量sum吗?当然不是,应该清楚局部变量出了其所在函数的作用域后就会被销毁,因此当Add函数被调用完后,sum所代表的值就是一个随机数了。因此实际上,编译器一般都会生成一个临时变量来存储函数返回的结果,最后ret接受的也是这个临时变量。

除了上面两种情况外,还有其他的一些情况也会有临时变量的产生,大家了解即可:

  1. 表达式求值:当需要计算一个表达式,特别是包含多个操作数和操作符的复杂表达式时,编程语言通常会生成临时变量来保存中间结果。
  2. 中间计算:在执行复杂计算时,可以使用临时变量来存储中间计算结果,以避免重复计算相同的值。
  3. 循环迭代:在循环结构中,迭代计数器通常被视为临时变量,因为它们在每次迭代中都会被更新。
  4. 条件语句:在条件语句中,如果需要根据条件执行不同的操作,临时变量可能会用于存储条件的结果或中间值。
  5. 数组和容器操作:在对数组、向量、列表等数据结构进行操作时,可能会生成临时变量来存储临时元素或中间结果。
  6. 错误处理:在处理异常或错误时,临时变量可以用于存储错误信息或状态。

临时变量的常性

临时变量有一个很重要的特性:常量性

这一个特性确保了临时变量是不可以被修改,这其中也就包括了权限不能被放大

例如:

int Add(int a, int b)
{
  int sum = a + b;
  return sum;
}
int main()
{
  Add(1, 2)++;
  return 0;
}

就会报错:

这里解释一下左值和右值的概念:

  1. 左值(L-value):左值是可以出现在赋值操作符(例如=)的一侧的表达式,表示一个可以被赋值的内存位置通常是一个变量。左值表示一个标识符或一个引用,它指向内存中的某个位置。例如,如果你有一个变量x,那么x就是一个左值,因为你可以将一个值赋给它,如x = 10。
  2. 右值(R-value):右值是一个表达式的结果值,它可以出现在赋值操作符的右侧。右值通常是计算的结果,它可以是常数、临时变量或函数的返回值。例如,如果你有一个表达式x + y,它的结果是一个右值,因为它代表一个值,但你不能将一个值赋给它。

报错信息显示,函数的返回值不是一个左值,也就是说返回的临时变量是不可被修改的。这也从侧面反映了临时变量的常性。

除此之外,临时变量还有其他一些特性,大家仅作了解即可:

  1. 短暂寿命:临时变量通常在其创建点的作用域内存在,一旦超出该作用域,它们就会被销毁。这使它们成为一种短暂的存储设备。
  2. 匿名:通常,临时变量没有显式的名称,因为它们是在表达式求值或函数调用期间自动创建的。它们只是在内部存储中的值。
  3. 用于中间计算:临时变量通常用于存储中间计算结果或值的转换。它们帮助管理复杂的表达式,确保正确的计算顺序。
  4. 可被编译器优化:现代编译器通常会进行优化,以最小化临时变量的使用,以提高性能。它们可以消除不必要的临时变量,以减少内存开销。
  5. 值语义:临时变量通常采用值语义,这意味着它们存储的是具体的值,而不是引用或指向其他变量。这有助于避免共享状态和副作用。
  6. 用于函数返回:在函数返回值的情况下,临时变量通常用于存储函数的结果,以便将其传递给调用方。
  7. 隐式创建和销毁:编程语言和编译器通常会自动创建和销毁临时变量,程序员无需显式管理它们的生命周期。
  8. 类型与原始值相关:临时变量的类型通常与它们所包含的值的类型相关,以确保类型的一致性。

总结

通过对临时变量的了解,我们就可以解释最开始提到的问题了:

int a = 1;
double& b = a;

当用整形变量a初始化浮点型引用b时,涉及到了隐式类型转换,那么中间就会产生一个double临时变量来临时存储a的值,但由于临时变量具有常性,其权限不能被放大,因此double& b = a;这句就是错误的。我们应该改为**const double& b = a;确保权限不变**。


本篇完。

相关文章
|
18天前
|
C语言 C++ 开发者
深入探索C++:特性、代码实践及流程图解析
深入探索C++:特性、代码实践及流程图解析
|
1月前
|
存储 IDE 编译器
深入探索C++中的变量世界:理论与实践
【4月更文挑战第5天】本文介绍了C++变量的基础知识,包括声明、数据类型、const和volatile限定符。通过示例展示了变量在用户输入、计算、控制流程和函数参数中的应用,并列举了常见错误及避免方法,如未声明、作用域混淆、类型不匹配、未初始化和拼写错误。最后提出了变量命名、避免冗余、适时复用、注释说明和利用现代C++特性的最佳实践。
28 0
|
2月前
|
存储 算法 程序员
【C++20 新特性 】模板参数包展开与Lambda初始化捕获详解
【C++20 新特性 】模板参数包展开与Lambda初始化捕获详解
79 3
|
2月前
|
算法 数据处理 C++
【C++ 20 新特性 算法和迭代器库的扩展和泛化 Ranges】深入浅出C++ Ranges库 (Exploring the C++ Ranges Library)
【C++ 20 新特性 算法和迭代器库的扩展和泛化 Ranges】深入浅出C++ Ranges库 (Exploring the C++ Ranges Library)
111 1
|
2月前
|
存储 安全 API
C++ 17 新特性 C++ String View:了解C++ 17 std::string_view的使用场景
C++ 17 新特性 C++ String View:了解C++ 17 std::string_view的使用场景
93 2
|
2月前
|
编译器 程序员 API
C++ 14 17 新特性:[[fallthrough]], [[nodiscard]], [[maybe_unused]], 和 [[deprecated]] 新属性的使用...
C++ 14 17 新特性:[[fallthrough]], [[nodiscard]], [[maybe_unused]], 和 [[deprecated]] 新属性的使用...
20 3
|
5天前
|
自然语言处理 编译器 C语言
【C++】C++ 入门 — 命名空间,输入输出,函数新特性
本文章是我对C++学习的开始,很荣幸与大家一同进步。 首先我先介绍一下C++,C++是上个世纪为了解决软件危机所创立 的一项面向对象的编程语言(OOP思想)。
31 1
【C++】C++ 入门 — 命名空间,输入输出,函数新特性
|
6天前
|
算法 C++
【C++入门到精通】condition_variable(条件变量)C++11 [ C++入门 ]
【C++入门到精通】condition_variable(条件变量)C++11 [ C++入门 ]
10 0
|
12天前
|
编译器 C++
【C++】【C++的常变量取地址问题(对比C的不同)】const修饰的常变量&volatile修饰用法详解(代码演示)
【C++】【C++的常变量取地址问题(对比C的不同)】const修饰的常变量&volatile修饰用法详解(代码演示)
|
14天前
|
存储 程序员 C语言
深入理解C++:从语言特性到实践应用
深入理解C++:从语言特性到实践应用
24 3