突破编程_C++_面试(变量与常量)

面试题 1 : C++ 中的变量存储类别有哪些,并简要描述它们的特点?

在C++中,变量的存储类别决定了变量的生命周期和可见性。以下是C++中的几种变量存储类别及其特点:

自动存储期

  • 也称为局部存储类别。
  • 这类变量在函数或代码块内部声明,并且在函数或代码块执行时创建,在函数或代码块退出时销毁。
  • 它们的生命周期是局部的,只存在于声明它们的函数或代码块执行期间。
  • 每次进入函数或代码块时,都会为这些变量分配新的存储空间,退出时释放这些空间。

静态存储期

  • 这类变量在程序开始执行时就创建,并在程序结束时销毁。
  • 它们可以在全局作用域、命名空间作用域或函数作用域中声明。
  • 在全局或命名空间作用域中声明的静态变量在整个程序执行期间都存在。
  • 在函数作用域中声明的静态变量(也称为静态局部变量)只在该函数被调用的期间存在,但其生命周期跨越了函数调用,即它们的值在函数调用之间是保持的。

线程存储期

  • 这类变量在线程开始执行时创建,并在线程结束时销毁。
  • 它们通常用于多线程编程,以确保每个线程都有自己的变量副本。
  • 在 C++11 及以后的版本中,可以使用 thread_local 关键字来声明线程局部存储期的变量。

动态存储期

  • 这类变量由 new 运算符创建,并由 delete 运算符销毁。
  • 它们的生命周期由程序员控制,直到显式删除它们,否则它们会一直存在。
  • 动态分配的变量在堆上分配空间,不受函数或代码块生命周期的限制。

每种存储类别都有其特定的用途和优势。例如,自动存储期的变量适用于需要临时存储数据的场景,而静态存储期的变量则适用于需要在程序执行期间持续存在的数据。线程存储期的变量则适用于多线程环境中,每个线程需要独立的数据副本时。动态存储期的变量则提供了更大的灵活性和控制力,但也需要程序员手动管理内存。

面试题 2 : const 和 constexpr 的区别是什么?它们各自在哪些场景下更有用?

在 C++ 中, const 和 constexpr 都是用于定义常量的关键字,但它们在用途和语义上存在一些区别。
const 关键字:
(1)可以用于声明一个变量为常量,这意味着一旦变量被初始化,其值就不能被修改。
(2)可以在运行时被赋值,并且可以在程序的不同部分共享相同的内存地址。
(3)可以在任何函数内部或全局作用域中定义。
(4)主要用于确保变量在程序执行期间保持不变,提高代码的可读性和可维护性。
constexpr 关键字:
(1)用于声明一个常量表达式,该表达式必须在编译时就能计算出其值。
(2)必须在编译时初始化,并且其值必须是常量表达式。
(3)该关键字在编译时被存储在程序的只读数据段中,因此它们的地址可以在程序的多个实例之间共享。
(4)主要用于编译时常量、数组大小、模板参数等需要在编译时知道值的情况。
const 和 constexpr 各自的使用场景:
const 的使用场景:
(1)当需要定义一个在程序执行期间保持不变的值时,可以使用 const 。
(2)当需要在函数内部定义一个只在该函数内部有效的常量时,可以使用 const 。
(3)当需要防止变量被意外修改时,可以使用 const 。
constexpr 的使用场景:
(1)当需要定义一个编译时常量时,可以使用 constexpr 。
(2)当需要定义一个作为数组大小的常量时,可以使用 constexpr 。
(3)当需要定义一个作为模板参数的常量时,可以使用 constexpr 。
(4)当需要优化性能,避免在运行时计算常量表达式的值时,可以使用 constexpr 。
总结来说, const 用于在运行时定义不可变的变量,而 constexpr 用于在编译时定义常量表达式。选择使用哪个关键字取决于你的具体需求,以及是否需要在编译时还是运行时确定常量的值。

面试题 3 :什么是内部链接和外部链接?如何在 C++ 中使用 static 和 extern 关键字来控制链接?

在C++中,链接( Linking )是编译器将不同的编译单元(通常是源文件)组合成一个可执行程序或库文件的过程。在这个过程中,编译器需要解决函数或变量的引用和定义之间的关联。
内部链接和外部链接是 C++ 中符号链接性的两种类型,它们决定了符号的作用域和可见性。
内部链接
当一个函数或变量被声明为 static 时,它具有内部链接。这意味着该符号只在定义它的编译单元内可见,其他编译单元无法访问它。在 C++ 中,静态成员变量和静态成员函数具有内部链接。
外部链接
默认情况下,函数或变量具有外部链接。这意味着函数或变量可以在多个编译单元之间共享,只要它在链接时是可访问的。使用 extern 关键字可以声明一个函数或变量,而不在当前编译单元中定义它,从而在其他编译单元中定义该符号。
使用 static 和 extern 关键字来控制链接:
使用 static 关键字:
在函数或变量声明前加上 static 关键字,可以使其具有内部链接。这样,该函数或变量只在其定义的编译单元内可见。
例如,在头文件中声明一个静态变量:

// header.h
static int s_val = 0;

这样, s_val 只会在包含该头文件的源文件中可见,而不会在其他源文件中共享。
使用 extern 关键字:
extern 关键字用于声明一个在其他编译单元中定义的符号。它告诉编译器该符号在链接时将被解析,而不是在当前编译单元中定义。
例如,在一个源文件中定义一个函数,并在另一个源文件中声明并使用该函数:

// file1.cpp
#include "header.h"

void func() 
{
    // ... 函数实现 ...
}

// file2.cpp
#include "header.h"

extern void func(); // 声明在其他编译单元中定义的函数

int main() {
    func(); // 调用在其他编译单元中定义的函数
    return 0;
}

在这个例子中, func 在 file1.cpp 中定义,并在 file2.cpp 中通过 extern 关键字声明。这样, main 函数可以调用 func ,即使它没有在 file2.cpp 中定义。
注意:在 C++ 中,通常建议将变量声明为 extern ,并在一个源文件中定义它,以避免多重定义错误。对于函数,可以在头文件中声明原型,并在一个源文件中定义实现。这样,其他源文件可以通过包含该头文件来使用该函数。

面试题 4 :什么是 mutable 关键字,它有什么用处?

在 C++ 中, mutable 关键字用于在类的 const 成员函数中修改类的某个成员变量。通常, const 成员函数是不能修改它所属对象的任何成员变量的,但是,如果需要某些成员变量能够在 const 成员函数中被修改,就可以将这些成员变量声明为 mutable 。
mutable 关键字允许在 const 成员函数中修改这些成员变量,而不会影响对象的常量性。这对于某些需要在逻辑上保持常量性,但实际上需要修改内部状态的场景非常有用。
如下为样例代码:

#include 

class Counter
{
public:
	Counter() : m_count(0), m_mutableVal(0) {}

	// 增加计数
	void increment()
	{
		m_count++;
	}

	// 在const成员函数中修改 m_mutableVal
	void setMutableVal(int val) const
	{
		m_mutableVal = val;
	}

	// 获取MutableVal的值
	int getMutableVal() const
	{
		return m_mutableVal;
	}

	// 获取计数
	int getCount() const
	{
		return m_count;
	}

private:
	int m_count; // 普通成员变量
	mutable int m_mutableVal; // mutable成员变量
};

int main()
{
	const Counter counter; // 创建一个const对象

	// 调用const成员函数设置 m_mutableVal 的值
	counter.setMutableVal(1);
	printf("counter.getMutableVal() : %d\n",counter.getMutableVal());
	
	// 尝试调用非const成员函数会编译失败,因为 counter 是 const 对象
	// counter.increment(); // 错误:不能在const对象上调用非const成员函数

	return 0;
}

上面代码的输出为

counter.getMutableVal() : 1

在上面代码中, Counter 类有一个普通的成员变量 m_count 和一个被声明为 mutable 的成员变量 m_mutableVal 。在 const 成员函数 setMutableVal 中,可以修改 m_mutableVal 的值,而不会影响 m_count 的值或对象的常量性。当尝试在 const 对象 counter 上调用非 const 成员函数 increment 时,编译器会报错,因为这样做会违反 const 对象的常量性保证。

面试题 5 : volatile 关键字的作用是什么?

在 C++ 中, volatile 关键字用于告诉编译器对象的值可能会在编译器无法检测到的情况下被改变。通常有如下几种应用场景:
硬件访问
当程序直接与硬件通信时,硬件的状态可能会在任何时候改变,而这些改变不是通过 C++ 代码直接进行的。例如,一个嵌入式系统中的某个寄存器可能在外部中断或 DMA 传输完成后被更新。
多线程编程
在多线程环境中,一个线程可能会修改一个变量,而另一个线程正在读取或写入同一个变量。即使使用了互斥锁或其他同步机制, volatile 也可能被用来确保编译器不会对这个变量的访问进行优化,从而导致不正确的行为。然而,需要注意的是, volatile 本身并不提供线程间的同步或互斥;它仅仅防止编译器优化。
信号处理
在信号处理程序中,如果一个变量可能在信号处理程序之外被异步地修改,那么这个变量应该被声明为 volatile 。
编译器优化
编译器通常会对代码进行优化,假设变量的值在没有被显式修改的情况下是不会改变的。但是,如果变量可能被外部因素(如上面提到的情况)修改,那么这种优化可能是错误的。 volatile 关键字告诉编译器不要做出这样的假设。
注意: volatile 并不保证原子性。即使在 volatile 修饰的变量上执行的操作也可能不是原子的,特别是当操作涉及多个步骤(如递增一个值)时。在多线程环境中确保数据的一致性和同步通常需要使用更高级的同步原语,如互斥锁或原子操作。
此外,过度使用 volatile 可能会导致性能下降,因为编译器无法对涉及 volatile 变量的代码进行有效优化。因此,应该只在确实需要的情况下使用它。

你可能感兴趣的:(突破编程_C++_面试,面试,c++)