联想嵌入式软件开发 一面 面经(常规问题居多)

1.自我介绍

面试官好,我是[姓名],[学校][专业][年级]。我的技术方向是嵌入式开发,主要使用C++。

技术栈方面,我熟悉C++语言特性、STL容器、多线程编程,了解Linux系统和嵌入式开发。有实际的项目经验,做过智能系统开发,涉及到模型部署、性能优化等工作。

我对嵌入式开发很感兴趣,特别是资源受限环境下的软件优化。联想在PC和智能设备领域有很强的技术实力,希望能加入贵公司,在嵌入式方向深入发展。

2.说说C++中的多态,静态多态和动态多态有什么区别?

多态是面向对象的重要特性,指同一个接口可以有不同的实现方式。C++中有两种多态:静态多态和动态多态。

静态多态是编译期确定的,主要通过函数重载和模板实现。编译器根据参数类型或模板参数,在编译时就确定调用哪个函数,没有运行时开销。

// 函数重载实现静态多态
void print(int x) {
    cout << "int: " << x << endl;
}

void print(double x) {
    cout << "double: " << x << endl;
}

// 模板实现静态多态
template<typename T>
void print(T x) {
    cout << x << endl;
}

// 编译时确定调用哪个版本
print(10);      // 调用print(int)
print(3.14);    // 调用print(double)


动态多态是运行期确定的,通过虚函数和继承实现。基类指针或引用可以指向派生类对象,调用虚函数时,根据对象的实际类型决定调用哪个版本。这是通过虚函数表(vtable)实现的,有一定的运行时开销。

class Base {
public:
    virtual void show() {
        cout << "Base" << endl;
    }
};

class Derived : public Base {
public:
    void show() override {
        cout << "Derived" << endl;
    }
};

// 运行时确定调用哪个版本
Base* ptr = new Derived();
ptr->show();  // 输出"Derived",运行时根据实际类型决定


区别总结:

  • 静态多态在编译期确定,效率高,没有运行时开销
  • 动态多态在运行期确定,灵活性高,但有虚函数表查找的开销
  • 静态多态通过重载和模板,动态多态通过虚函数
  • 嵌入式开发中,如果追求性能,优先用静态多态

3.inline关键字的作用是什么?什么时候使用?

inline关键字建议编译器将函数内联展开,即在调用处直接插入函数代码,而不是通过函数调用。这样可以减少函数调用的开销(保存寄存器、跳转、返回等),提高性能。

inline int add(int a, int b) {
    return a + b;
}

int main() {
    int result = add(3, 5);  // 编译器可能展开为:int result = 3 + 5;
}


inline的特点:

  • 只是建议,编译器可以忽略。复杂的函数(如有循环、递归)通常不会内联
  • 内联函数的定义通常放在头文件中,因为编译器需要看到完整定义才能内联
  • 内联会增加代码体积,如果函数被多次调用,会有多份代码拷贝
  • 类内定义的成员函数默认是inline的

使用场景:

  • 函数体很小,只有几行代码
  • 函数被频繁调用,在性能关键路径上
  • 函数逻辑简单,没有循环和递归

不适合内联的场景:

  • 函数体很大,内联会导致代码膨胀
  • 函数有复杂逻辑,编译器可能拒绝内联
  • 虚函数通常不能内联(除非编译器能确定实际类型)

嵌入式开发中,代码空间有限,要谨慎使用inline,避免代码膨胀。

4.vector容器的扩容机制是什么?

vector是动态数组,当元素数量超过容量时,会自动扩容。扩容机制是:

分配新内存:通常是当前容量的1.5倍或2倍(不同编译器实现不同,GCC是2倍,MSVC是1.5倍)。

拷贝元素:将旧内存中的元素拷贝或移动到新内存。如果元素有移动构造函数,使用移动;否则使用拷贝。

释放旧内存:释放旧的内存空间。

vector<int> vec;
cout << "capacity: " << vec.capacity() << endl;  // 0

vec.push_back(1);
cout << "capacity: " << vec.capacity() << endl;  // 1

vec.push_back(2);
cout << "capacity: " << vec.capacity() << endl;  // 2

vec.push_back(3);
cout << "capacity: " << vec.capacity() << endl;  // 4(扩容为2倍)

vec.push_back(4);
vec.push_back(5);
cout << "capacity: " << vec.capacity() << endl;  // 8(再次扩容)


扩容的性能影响:

  • 扩容需要分配内存、拷贝元素、释放内存,开销较大
  • 如果频繁扩容,性能会下降
  • 可以用reserve()预分配空间,避免频繁扩容
vector<int> vec;
vec.reserve(1000);  // 预分配1000个元素的空间
for (int i = 0; i < 1000; i++) {
    vec.push_back(i);  // 不会触发扩容
}


capacity和size的区别:

  • size是当前元素个数
  • capacity是已分配的空间大小
  • capacity >= size

嵌入式开发中,内存有限,要注意vector的内存占用。如果知道元素数量,最好用reserve预分配,避免扩容带来的内存碎片和性能损失。

5.STL中哪些容器是定长的,哪些是变长的?

变长容器(动态大小):

  • vector:动态数组,可以自动扩容
  • deque:双端队列,可以在两端高效插入删除
  • list:双向链表,可以在任意位置插入删除
  • map/set:红黑树实现,可以动态增删元素
  • unordered_map/unordered_set:哈希表实现,可以动态增删元素
  • string:本质是vector,可以动态增长

定长容器(固定大小):

  • array(C++11):固定大小的数组,大小在编译期确定
std::array<int, 10> arr;  // 固定10个元素,不能改变大小


半定长容器:

  • stack、queue、priority_queue:适配器,底层可以用vector或deque,大小可变但不直接控制

区别:

  • 变长容器可以动态增删元素,灵活但有内存管理开销
  • 定长容器大小固定,性能好但不灵活
  • 嵌入式开发中,如果元素数量确定,优先用array,避免动态内存分配
// 嵌入式中推荐的用法
std::array<int, 100> buffer;  // 栈上分配,不需要动态内存

// 如果必须用变长容器,预分配空间
std::vector<int> vec;
vec.reserve(100);  // 避免频繁扩容


6.static关键字有哪些作用?

static在不同上下文中有不同的作用。

修饰局部变量:变量的生命周期延长到程序结束,但作用域仍是局部的。只初始化一次,保持上次的值。

void func() {
    static int count = 0;  // 只初始化一次
    count++;
    cout << count << endl;
}

func();  // 输出1
func();  // 输出2
func();  // 输出3


修饰全局变量或函数:限制作用域在当前文件,其他文件不可见。用于避免命名冲突。

// file1.cpp
static int global_var = 10;  // 只在file1.cpp中可见

static void helper() {  // 只在file1.cpp中可见
    // ...
}


修饰类成员变量:所有对象共享同一个变量,属于类而不是对象。必须在类外初始化。

class MyClass {
public:
    static int count;  // 声明
};

int MyClass::count = 0;  // 定义和初始化

MyClass obj1, obj2;
obj1.count = 10;
cout << obj2.count;  // 输出10,共享同一个变量


修饰类成员函数:不依赖于对象,可以通过类名直接调用。不能访问非静态成员。

class MyClass {
public:
    static void func() {
        // 只能访问静态成员
    }
};

MyClass::func();  // 通过类名调用


嵌入式开发中的应用:

  • 用static局部变量保存状态,避免全局变量污染
  • 用static限制函数和变量的作用域,避免命名冲突
  • 用static成员实现单例模式

7.sizeof的作用是什么?sizeof一个指针结果是多少?

sizeof是编译期运算符,用于计算类型或变量的字节大小。

基本类型的大小:

sizeof(char);

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

嵌入式面试八股文全集 文章被收录于专栏

这是一个全面的嵌入式面试专栏。主要内容将包括:操作系统(进程管理、内存管理、文件系统等)、嵌入式系统(启动流程、驱动开发、中断管理等)、网络通信(TCP/IP协议栈、Socket编程等)、开发工具(交叉编译、调试工具等)以及实际项目经验分享。专栏将采用理论结合实践的方式,每个知识点都会附带相关的面试真题和答案解析。

全部评论

相关推荐

最喜欢秋天的火龙果很...:第一份工作一定要往大的去,工资低点没事。后面换工作会更好找,即使你去小公司,你也不可能不会换工作的。所以找大的去
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务