Skip to content

败犬日报 2025-02-20

1. 群友出的面试题

https://www.zhihu.com/question/451327108/answer/53592485454

对此群友讨论了下面的话题。

1.1. magic static

cpp
void swapTwoPoints(std::pair<double,double>& a, std::pair<double,double>& b) {
    static std::pair<double,double> tmp = a;
    a = b;
    b = tmp;
    return;
}

这个代码的 bug 是静态局部变量只有首次调用会初始化,第二次调用就寄了。

magic static 就是用运行时的值去初始化静态局部变量,效果是只会初始化一次,还能保证并发安全。

效果和 std::call_once 相同。

magic static 可以用来实现单例模式。

1.2. double 传值还是传常量引用

传值。

一般来说,double 只有 8 字节,传引用(在实现上是传递 double 变量的地址)也是 8 字节。但是传引用需要解引用,这是一笔开销。

这里不考虑内联,内联了就没区别。

24 字节以内结构体都推荐传值。

2. 拷贝构造的参数不加引用

cpp
struct T {
    T(const T rhs) { ... }
};

不行。因为 rhs 传参的时候,它需要被初始化。而“初始化”这个过程依赖构造函数,这样就会无限递归。

事实上编译器会报错 error: invalid constructor; you probably meant 'T (const T&)'

3. rust 引用计数溢出导致安全问题

https://internals.rust-lang.org/t/rc-is-unsafe-mostly-on-32-bit-targets-due-to-overflow/2120

有点离谱。

4. 为什么 L1 Cache 只有页内的位才能索引

缓存一致性问题。

虚拟地址分为页号(高位部分)和页内偏移(低位部分)。如果两个虚拟地址映射到同一物理地址时,这个物理地址就可以映射到不同的缓存组中。而虚拟 / 物理地址的页内偏移是一致的(翻译前后不变),就不会出现这个问题。

5. 加 prefetch 性能是否有提升

经常没有提升。

因为一般 CPU 的硬件预取能覆盖常见的访问模式,不用再做软件预取了,简单的 CPU 除外。另外一些算法的特殊访问模式也会对预取效果有影响。

所以满足上述条件的可以多花时间研究 prefetch。

6. float 转换为相同二进制表示的 int

标准做法是 std::bit_cast(C++20),旧版本用 memcpy。不用担心性能,memcpy 第三个参数是常量会被编译器优化掉,从而没有函数调用开销。

cpp
int convert(float x) {
    int y;
    memcpy(&y, &x, sizeof(int));
    return y;
}

不标准但可行的做法是搞个 unionhttps://zh.cppreference.com/w/cpp/language/union 有记载:

读取并非最近写入的联合体成员是未定义行为。许多编译器以非标准语言扩展实现读取联合体的不活跃成员的能力。

不要用 reinterpret_cast,这个是未定义行为,有类型别名问题。