【C++ 面试高频基础:指针、引用、const、static、new/delete 总结】

一、指针

指针是 C++ 中非常重要的概念。简单来说,指针就是用来保存地址的变量。

普通变量保存的是数据本身,而指针变量保存的是某个变量的内存地址。

1. 指针的基本使用

复制代码
#include <iostream>
using namespace std;

int main() {
    int a = 10;

    // p 是一个指针变量,用来保存 int 类型变量的地址
    int* p = &a;

    // 输出变量 a 的值
    cout << "a = " << a << endl;

    // 输出变量 a 的地址
    cout << "&a = " << &a << endl;

    // 输出指针 p 保存的地址
    cout << "p = " << p << endl;

    // 通过 *p 访问 p 指向的变量
    cout << "*p = " << *p << endl;

    return 0;
}

在这段代码中:

复制代码
int* p = &a;

表示定义一个指针变量 p,它保存变量 a 的地址。

复制代码
*p

表示访问指针 p 指向的那块内存中的值。

2. 指针的作用

指针常见作用有:

复制代码
1. 间接访问变量
2. 动态申请内存
3. 函数参数传递
4. 操作数组和字符串
5. 实现数据结构,例如链表、树等

3. 指针使用注意点

指针使用时要注意空指针和野指针。

复制代码
int* p = nullptr;  // 推荐使用 nullptr 表示空指针

如果一个指针没有初始化,里面可能是随机地址,这种指针叫野指针。使用野指针可能导致程序崩溃。

错误示例:

复制代码
int* p;       // 没有初始化,p 是野指针
*p = 10;      // 错误,可能访问非法内存

正确写法:

复制代码
int* p = nullptr;  // 初始化为空指针

int a = 10;
p = &a;            // 让 p 指向变量 a

cout << *p << endl;

4. 指针面试总结

面试时可以这样回答:

指针本质上是一个变量,用来保存内存地址。通过指针可以间接访问某个变量。指针使用灵活,但是也容易出错,例如空指针、野指针、悬空指针等问题。因此在使用指针时要注意初始化,动态申请内存后要及时释放。


二、引用

引用可以理解为变量的别名。给一个变量起了引用之后,引用和原变量操作的是同一块内存。

1. 引用的基本使用

复制代码
#include <iostream>
using namespace std;

int main() {
    int a = 10;

    // ref 是 a 的引用,也就是 a 的别名
    int& ref = a;

    cout << "a = " << a << endl;
    cout << "ref = " << ref << endl;

    // 修改 ref,相当于修改 a
    ref = 20;

    cout << "修改后 a = " << a << endl;
    cout << "修改后 ref = " << ref << endl;

    return 0;
}

输出结果中,aref 的值都会变成 20。

因为 ref 不是一个新的变量,而是 a 的别名。

2. 引用作为函数参数

引用最常见的作用之一是作为函数参数,避免拷贝,并且可以修改实参。

复制代码
#include <iostream>
using namespace std;

// 使用引用传参,可以直接修改外部变量
void changeValue(int& x) {
    x = 100;
}

int main() {
    int a = 10;

    changeValue(a);

    cout << "a = " << a << endl;  // 输出 100

    return 0;
}

如果不用引用,而是普通值传递:

复制代码
void changeValue(int x) {
    x = 100;
}

这样只是修改了函数内部的临时变量,不会影响外部的 a

3. 指针和引用的区别

对比项 指针 引用
本质 保存地址的变量 变量的别名
是否可以为空 可以为空 一般不能为空
是否需要初始化 可以不初始化,但不推荐 定义时必须初始化
是否可以改变指向 可以改变指向 初始化后不能再引用其他变量
使用方式 通过 *p 访问 直接使用
安全性 相对容易出错 相对更安全

示例代码:

复制代码
#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int b = 20;

    // 指针可以改变指向
    int* p = &a;
    p = &b;

    // 引用一旦绑定,就不能再换成其他变量
    int& ref = a;
    ref = b;  // 这里不是让 ref 引用 b,而是把 b 的值赋给 a

    cout << "a = " << a << endl;  // 输出 20

    return 0;
}

注意:

复制代码
ref = b;

不是让 ref 改为引用 b,而是把 b 的值赋给 ref 所引用的变量 a

4. 引用面试总结

面试时可以这样回答:

引用是变量的别名,定义时必须初始化,初始化后不能再引用其他变量。引用通常用于函数参数传递,可以避免拷贝,提高效率,也可以让函数内部修改外部变量。相比指针,引用使用起来更简单,也相对更安全。


三、const

const 表示常量,用来修饰变量、指针、函数参数和成员函数。被 const 修饰的内容一般不能被修改。

1. const 修饰普通变量

复制代码
#include <iostream>
using namespace std;

int main() {
    const int a = 10;

    // a = 20;  // 错误,const 修饰的变量不能修改

    cout << a << endl;

    return 0;
}

const int a = 10; 表示 a 是一个常量,后面不能再修改。

2. const 修饰指针

const 和指针结合时,面试经常问。

常见有三种写法:

复制代码
const int* p1;        // 指向常量的指针
int* const p2 = &a;   // 指针常量
const int* const p3;  // 指向常量的指针常量

3. 指向常量的指针

复制代码
#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int b = 20;

    // p 指向的值不能通过 p 修改
    const int* p = &a;

    // *p = 30;  // 错误,不能通过 p 修改 a 的值

    // p 本身可以改变指向
    p = &b;

    cout << *p << endl;

    return 0;
}

const int* p 的意思是:不能通过 p 修改它指向的值,但是 p 可以指向其他变量。

4. 指针常量

复制代码
#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int b = 20;

    // p 是常量指针,必须初始化
    int* const p = &a;

    // 可以通过 p 修改 a 的值
    *p = 30;

    // p = &b;  // 错误,p 不能改变指向

    cout << a << endl;

    return 0;
}

int* const p 的意思是:指针 p 本身不能改变指向,但是可以通过 p 修改它指向的值。

5. const 修饰函数参数

如果函数参数不希望被修改,可以使用 const

复制代码
#include <iostream>
#include <string>
using namespace std;

// 使用 const 引用传参,既避免拷贝,又防止函数内部修改参数
void printName(const string& name) {
    cout << "name = " << name << endl;

    // name = "Tom";  // 错误,const 引用不能修改
}

int main() {
    string name = "Jack";

    printName(name);

    return 0;
}

这种写法在 C++ 项目中很常见:

复制代码
const string& name

它有两个好处:

复制代码
1. 使用引用,避免拷贝,提高效率。
2. 使用 const,防止函数内部修改数据。

6. const 面试总结

面试时可以这样回答:

const 用来表示不可修改。它可以修饰普通变量、指针、函数参数和成员函数。const 修饰变量时表示变量不能被修改;修饰指针时需要看 const 在 * 的左边还是右边,左边表示指向的值不能改,右边表示指针本身不能改。函数参数中常用 const 引用,既能避免拷贝,又能防止参数被修改。


四、static

static 在 C++ 中有多个作用,不同位置含义不同。面试中常考局部静态变量、静态全局变量、静态成员变量和静态成员函数。

1. static 修饰局部变量

普通局部变量在函数调用结束后就会销毁。

但是 static 局部变量只会初始化一次,并且生命周期一直到程序结束。

复制代码
#include <iostream>
using namespace std;

void test() {
    // static 局部变量只初始化一次
    static int count = 0;

    count++;

    cout << "count = " << count << endl;
}

int main() {
    test();  // 输出 1
    test();  // 输出 2
    test();  // 输出 3

    return 0;
}

如果没有 static,每次调用 test() 时,count 都会重新创建并初始化为 0。

2. static 修饰全局变量

复制代码
// static 修饰全局变量后,该变量只能在当前文件中使用
static int g_num = 10;

static 全局变量的作用域被限制在当前源文件中,其他源文件不能直接访问它。

这样可以减少命名冲突。

3. static 修饰类成员变量

静态成员变量属于整个类,而不是某一个对象。

复制代码
#include <iostream>
using namespace std;

class Student {
public:
    // 静态成员变量声明
    static int count;

    Student() {
        count++;
    }
};

// 静态成员变量需要在类外初始化
int Student::count = 0;

int main() {
    Student s1;
    Student s2;
    Student s3;

    cout << "学生对象数量:" << Student::count << endl;

    return 0;
}

这里 count 属于 Student 类,所有对象共享同一个 count

4. static 修饰类成员函数

静态成员函数也属于类,而不是某个对象。

复制代码
#include <iostream>
using namespace std;

class Student {
public:
    static int count;

    Student() {
        count++;
    }

    // 静态成员函数
    static void printCount() {
        cout << "学生数量:" << count << endl;

        // 静态成员函数中不能直接访问普通成员变量
        // 因为它没有 this 指针
    }
};

int Student::count = 0;

int main() {
    Student s1;
    Student s2;

    // 可以通过类名直接调用静态成员函数
    Student::printCount();

    return 0;
}

静态成员函数没有 this 指针,所以不能直接访问普通成员变量和普通成员函数。

5. static 面试总结

面试时可以这样回答:

static 在不同位置有不同含义。修饰局部变量时,变量只初始化一次,生命周期到程序结束;修饰全局变量或函数时,可以限制它们只在当前文件中使用;修饰类成员变量时,该变量属于整个类,所有对象共享;修饰类成员函数时,可以通过类名直接调用,但静态成员函数没有 this 指针,不能直接访问普通成员变量。


五、new/delete

在 C++ 中,newdelete 用于动态内存管理。

new 用来在堆区申请内存,并调用构造函数。

delete 用来释放内存,并调用析构函数。

1. new/delete 的基本使用

复制代码
#include <iostream>
using namespace std;

int main() {
    // 在堆区申请一个 int 类型空间,并初始化为 10
    int* p = new int(10);

    cout << "*p = " << *p << endl;

    // 释放堆区内存
    delete p;

    // 释放后把指针置空,避免悬空指针
    p = nullptr;

    return 0;
}

注意:

复制代码
delete p;
p = nullptr;

释放内存后,指针本身还保存着原来的地址,这种指针叫悬空指针。为了安全,通常会把它置为 nullptr

2. new\[\] 和 delete\[\]

如果申请的是数组,要使用 new[]delete[]

复制代码
#include <iostream>
using namespace std;

int main() {
    // 动态申请一个长度为 5 的 int 数组
    int* arr = new int[5];

    // 给数组赋值
    for (int i = 0; i < 5; i++) {
        arr[i] = i + 1;
    }

    // 输出数组内容
    for (int i = 0; i < 5; i++) {
        cout << arr[i] << " ";
    }

    // 释放数组内存,必须使用 delete[]
    delete[] arr;

    arr = nullptr;

    return 0;
}

一定要注意:

复制代码
new      对应 delete
new[]    对应 delete[]

不能混用。

3. new/delete 和 malloc/free 的区别

对比项 new/delete malloc/free
所属语言 C++ C 语言
是否调用构造函数 new 会调用构造函数 malloc 不会
是否调用析构函数 delete 会调用析构函数 free 不会
返回值类型 返回具体类型指针 返回 void*
失败处理 new 失败通常抛异常 malloc 失败返回 NULL
使用对象 更适合 C++ 对象 只负责申请原始内存

示例代码:

复制代码
#include <iostream>
#include <cstdlib>
using namespace std;

class Student {
public:
    Student() {
        cout << "调用构造函数" << endl;
    }

    ~Student() {
        cout << "调用析构函数" << endl;
    }
};

int main() {
    // new 会申请内存,并调用构造函数
    Student* s1 = new Student();

    // delete 会调用析构函数,并释放内存
    delete s1;

    // malloc 只申请内存,不会调用构造函数
    Student* s2 = (Student*)malloc(sizeof(Student));

    // free 只释放内存,不会调用析构函数
    free(s2);

    return 0;
}

这段代码说明:

复制代码
new/delete 更适合 C++ 对象。
malloc/free 只负责内存申请和释放,不负责对象构造和析构。

4. new/delete 面试总结

面试时可以这样回答:

new 和 delete 是 C++ 中用于动态内存管理的操作符。new 不仅会申请内存,还会调用对象的构造函数;delete 不仅会释放内存,还会调用对象的析构函数。malloc 和 free 是 C 语言中的库函数,只负责申请和释放原始内存,不会调用构造函数和析构函数。因此在 C++ 中管理对象时,通常使用 new/delete,或者更推荐使用智能指针来减少内存泄漏风险。


六、面试高频问题整理

1. 指针和引用有什么区别?

指针是保存地址的变量,引用是变量的别名。指针可以为空,也可以改变指向;引用定义时必须初始化,并且初始化后不能再引用其他变量。指针使用时需要通过 * 解引用,引用可以直接使用。一般来说,引用更安全,指针更灵活。


2. const 修饰指针时怎么理解?

主要看 const* 的左边还是右边。

复制代码
const int* p;        // 指向的值不能通过 p 修改
int* const p = &a;   // 指针本身不能改变指向
const int* const p;  // 指向的值不能改,指针本身也不能改

简单记忆:

复制代码
const 在 * 左边:值不能改。
const 在 * 右边:指针不能改。

3. static 有哪些作用?

static 修饰局部变量时,变量只初始化一次,生命周期到程序结束。

static 修饰全局变量或函数时,可以限制作用域,让它们只能在当前文件使用。

static 修饰类成员变量时,变量属于整个类,所有对象共享。

static 修饰类成员函数时,可以通过类名调用,但不能直接访问普通成员变量,因为静态成员函数没有 this 指针。


4. new/delete 和 malloc/free 有什么区别?

new/delete 是 C++ 的操作符,malloc/free 是 C 语言的库函数。

new 会申请内存并调用构造函数,delete 会调用析构函数并释放内存。

malloc 只申请内存,不会调用构造函数,free 只释放内存,不会调用析构函数。

所以在 C++ 中创建对象时,通常使用 new/delete,而不是 malloc/free。


5. delete 后为什么要把指针置为 nullptr?

delete 释放的是指针指向的内存,但是指针变量本身还保存着原来的地址。如果继续使用这个指针,就可能访问已经释放的内存,形成悬空指针。

所以释放后通常写成:

复制代码
delete p;
p = nullptr;

这样后面再判断:

复制代码
if (p != nullptr) {
    // 使用 p
}

可以提高程序安全性。


七、总结

本文主要整理了 C++ 面试中非常高频的几个基础知识点:指针、引用、const、static 和 new/delete。

指针本质上是保存地址的变量,使用灵活,但容易出现空指针、野指针和悬空指针等问题。

引用是变量的别名,定义时必须初始化,使用起来比指针更简单,常用于函数参数传递,可以避免拷贝,提高效率。

const 用来表示不可修改,常用于修饰变量、指针和函数参数。面试中重点掌握 const 指针和指针常量的区别。

static 在不同位置有不同含义,可以修饰局部变量、全局变量、类成员变量和类成员函数。面试中要重点理解 static 局部变量只初始化一次,以及 static 成员属于类而不是对象。

new/delete 用于 C++ 动态内存管理。new 会申请内存并调用构造函数,delete 会调用析构函数并释放内存。new 和 delete 要配对使用,new\[\] 和 delete\[\] 也要配对使用。

简单记忆:

复制代码
指针:保存地址,灵活但容易出错。
引用:变量别名,必须初始化,更安全。
const:限制修改,提高代码安全性。
static:改变生命周期或作用域,也可表示类共享成员。
new/delete:动态申请和释放内存,会调用构造和析构函数。

面试中回答这些基础问题时,不要只背概念,最好能结合代码说明它们的使用场景和区别。

0voice · GitHub

相关推荐
Yeats_Liao6 小时前
Feed流系统设计(三):数据模型与存储设计,从表结构到Redis收件箱
java·javascript·redis
JiaHao汤6 小时前
分布式事务方案全景:从理论到 Seata 落地
java·分布式·spring·spring cloud
2601_961875246 小时前
法考考试时间安排及科目|时间表|资料已整理
开发语言·c#·inverted-index·suffix-tree·sstable·r-tree·lsm-tree
AI科技星6 小时前
数术工坊第八卷:算力革命
c语言·开发语言·网络·量子计算·agi
geovindu6 小时前
go: Generators Pattern
开发语言·后端·设计模式·golang·生成器模式
色空大师7 小时前
【debug调试详解-idea】
java·ide·intellij-idea·调试·远程调试
程序猿阿越7 小时前
AutoMQ源码(一)读、写、Compaction
java·后端·源码
ywl4708120877 小时前
jwt生产token,简单版helloworld
java·数据库·spring
未若君雅裁7 小时前
生产问题排查与性能瓶颈定位:日志、监控、链路追踪、压测与Arthas
java·web安全