近期的笔试和面试的复盘

  1. C++中static关键字的作用,什么时候构造,什么时候析构。
    static变量的用法有五种:全局变量、局部变量、类的变量,函数、类的函数。
    他的作用是将当前变量/函数的生命周期限制到当前文件/当前类/当前函数。
    当一个static变量被定义在类内时,他并没有初始化。因此,如下所示的代码会报错:
// MyClass.h
class MyClass {
public:
    static int sharedVar; // 声明(未定义)
};

// main.cpp

int main() {
    MyClass::sharedVar = 10; // ❌ 错误:未定义的引用(undefined reference)
    return 0;
}

报错如下:

/usr/bin/ld: /tmp/cc6ZWKp0.o: in function `main':
main.cpp:(.text+0x6): undefined reference to `MyClass::sharedVar'
collect2: error: ld returned 1 exit status

解决上面的问题有两种方法:
如果是C++ 14及以前,可以通过类外定义的方式实现:

// MyClass.h
class MyClass {
public:
    static int sharedVar; // 声明
};

// MyClass.cpp
int MyClass::sharedVar = 0; // ✅ 定义(分配内存)

// main.cpp
#include "MyClass.h"
int main() {
    MyClass::sharedVar = 10; // ✅ 正常使用
    return 0;
}

对于C++17及以后,可以通过inline的方式直接在类内定义同时初始化。

// MyClass.h
class MyClass {
public:
    inline static int sharedVar = 0; // ✅ C++17:声明 + 定义
};

// main.cpp
#include "MyClass.h"
int main() {
    MyClass::sharedVar = 10; // ✅ 正常使用
    return 0;
}

对于类内的静态变量,其初始化实际发生在程序运行时,main函数执行前。且初始化顺序与析构顺序相反。在同一文件中按照从上到下的顺序构造,在不同文件中构造顺序不确定(也即无法保证明确的构造顺序,除非通过一些手段显式的修复这些问题,例如:懒加载,显式初始化等)

  1. Cmake里使用target_link链接时,指定的private、public、interface等关键字的作用是什么
    这里指的是target_link_libraries,那么三个关键字指的都是,当前库对于被链接到的对象的行为
    比如target_link_libraries(A PRIVATE B),那么如果C链接了A,关键字就会影响B对C的可见性,其中比较奇怪的是interface,描述指的是C会链接A,但是B不链接A这种情况。

  2. inline在.so文件和.a文件中,如何判断内联行为是否发生
    so文件是shared object的意思,在编译时需要添加 -shared -fPIC命令,让生成的代码可以在内存中的任意位置加载并运行,而无需修改代码中的地址引用。
    对so文件,可以通过nm -C查看当前文件的符号表中是否有对应的函数名。
    对a文件,可以通过objdump -tT 查看。
    对于so文件,也可以通过反汇编实现。

  3. CPU中如何操作NPU的存储空间
    目前在AscendC中应该还没有类似的操作。

  4. 如何用GDB调试AscendC算子
    https://www.hiascend.com/document/detail/zh/canncommercial/82RC1/opdevg/Ascendcopdevg/atlas_ascendc_10_0073.html
    原来昇腾也支持用GDB调试,因为算子支持仿真运行的方式。

  5. GDB里的si什么意思,如何打印一个数组的每个元素的值和地址空间
    si是单步执行一条汇编指令的意思。
    对于编译期知道长度的数组,直接打印会成功。
    对于编译期不知道长度的数组,可以使用print *array_ptr@length,指定长度的方式执行。例如 print *((int*)0x7fffffffdc50)@5
    另外可以使用如下的命令:
    x/[数量][格式][单位] 地址
    例如:x/10dw array_ptr

  6. 线程如何通信
    线程间通信(ITC)​​
    ​​共享内存​​:直接读写全局变量或堆内存(需同步)。
    ​​互斥锁(Mutex)​​:保护共享资源。
    ​​条件变量(Condition Variable)​​:线程间事件通知。
    ​​信号量(Semaphore)​​:控制资源访问。
    ​​原子操作​​:无锁编程(如 atomic_int)。
    ​​线程局部存储(TLS)​​:避免共享数据。
    ​​进程间通信(IPC)
    ​​管道(Pipe)​​:单向字节流(如 pipe())。
    ​​命名管道(FIFO)​​:支持无亲缘关系的进程通信。
    ​​消息队列(Message Queue)​​:结构化消息传递(如 mq_open)。
    ​​共享内存(Shared Memory)​​:映射同一物理内存(如 shmget)。
    ​​信号(Signal)​​:异步事件通知(如 kill)。
    ​​套接字(Socket)​​:跨网络通信(如 TCP/UDP)。
    ​​文件​​:通过磁盘文件交换数据(需同步)。

  7. 筛选法建堆的第一个元素会选择哪个
    应该是(n - 1) / 2,或者更准确的说是第一个非叶子结点的子节点,在笔试中直接画图更方便。

  8. 类必须包含一个成员吗?
    这种说法是错误的,类可以是空的, 或者包含非成员函数,例如友元、静态断言、类型声明等

  9. const对象相关的概念
    如果对一个类构造const类型的对象,那么其初始化只支持使用初始化列表的方式,而不能使用普通的带参数输入的初始化方法。且对于这种const的对象,只能调用const修饰的函数。

  10. 赋值语句进行cout会发生什么?
    赋值语句的结果是左值的引用。

  11. 一个基本有序的数组进行排序时,需要用什么排序算法。
    答案是插入排序或者堆排序,因为基本有序可以等价于k比较小,而这两种算法的事件负责度分别为:
    插入排序:O(NK)
    堆排序: O(N logK)

为什么基本有序可以等价于K比较小?
因为K的含义是所有元素距离其最终位置的差值。
13. 有一个基类和派生类,其中,派生类没有构造函数,基类的构造函数调用的Init()函数,在基类和派生类中都是虚函数,此时创建一个派生类的对象,会调用哪个函数?

  • 首先这种情况下,应该调用哪个构造函数?
    构造派生类对象时,​​先调用基类的构造函数​​,再调用派生类的构造函数(如果派生类有显式定义的话)。
    由于派生类没有显式定义构造函数,编译器会生成一个​​默认构造函数​​,它只会调用基类的构造函数(如果基类有无参构造函数)或默认初始化成员变量。
    也就是说调用了基类的构造函数,然后构造了派生类的构造函数,虽然这个函数没有什么作用。

  • 虚函数如何发挥作用?
    在基类构造时,由于派生类的构造还没完成,因此派生类的虚函数表不存在,此时调用虚函数并不能顺利找到派生类的函数,因此执行的是基类的虚函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值