嵌入式软件工程师面试题——2025校招社招通用(八)

说明:

  • 面试题来源于网络书籍,公司题目以及博主原创或修改(题目大部分来源于各种公司);
  • 文中很多题目,或许大家直接编译器写完,1分钟就出结果了。但在这里博主希望每一个题目,大家都要经过认真思考,答案不重要,重要的是通过题目理解所考知识点,好应对题目更多的变化;
  • 博主与大家一起学习,一起刷题,共同进步;
  • 写文不易,麻烦给个三连!!!

1.继承时访问级别如何变化?

分析:
在 C++ 中,继承是可以降低父类的访问级别的。
按照访问级别不同,继承可分为以下3种。

  • public (公用继承):基类成员保持自己的访问级别;
  • protect (受保护继承):基类的 public 和 protected成员在派生类中为 protected 成 员。基类的 private 成员保持为 private;
  • private (私有继承):基类的所有成员在派生类中为 private 成员。

如果没有自定义, C++ 将使用默认继承保护级别,使用class 保留字定义的派生类默认 具有 private 继承。使用 struct 保留字定义的类默认具有public 继承。友元关系是不可以继 承的。static 成员属于类,而非对象,在继承层次中只有一个这样的成员。既可以通过基类 访问 static 成员,也可以通过派生类访问。

答案: 在C++ 中有public、protect 和 private 等3种继承访问级别。子类继承的时候,通过声明为 protect 或者 private 可以降低父类的访问级别。

2.什么是浅复制和深复制?

答案:

  • 浅复制是指将一个对象的内容简单地拷贝到另一个对象中,包括成员变量的值和指针的地址。这意味着原始对象和副本对象共享相同的指针,它们指向相同的内存地址。如果对其中一个对象进行修改,会影响到另一个对象。这可能导致潜在的问题,特别是当对象销毁时,可能会出现重复释放内存或访问无效内存的情况。

  • 深复制是指创建一个新的对象,并将原始对象的内容完全复制到新对象中,包括成员变量的值和指针所指向的内容。这意味着原始对象和副本对象有各自独立的内存空间,彼此之间不会互相干扰。对任何一个对象的修改都不会影响到另一个对象。

为了实现深复制,通常需要重载拷贝构造函数和赋值运算符,确保在复制对象时,指针所指向的内容也得到复制。可以使用动态内存分配来创建新的对象,使得两个对象在内存中是彼此独立的。
下面是一个示例,展示了浅复制和深复制的区别:

#include 
#include 

class Person {
public:
    Person(const char* name) {
        size_t len = std::strlen(name) + 1;
        this->name = new char[len];
        std::strcpy(this->name, name);
    }

    // 浅复制
    Person(const Person& other) {
        this->name = other.name;  // 只是拷贝指针,共享相同的内存
    }

    // 深复制
    Person& operator=(const Person& other) {
        if (this != &other) {
            delete[] this->name;
            size_t len = std::strlen(other.name) + 1;
            this->name = new char[len];
            std::strcpy(this->name, other.name);
        }
        return *this;
    }

    ~Person() {
        delete[] name;
    }

    void PrintName() {
        std::cout << "Name: " << name << std::endl;
    }

private:
    char* name;
};

int main() {
    Person person1("Alice");
    Person person2 = person1;  // 浅复制
    person1.PrintName();  // 输出 "Name: Alice"
    person2.PrintName();  // 输出 "Name: Alice"

    person2 = Person("Bob");  // 深复制
    person1.PrintName();  // 输出 "Name: Alice"
    person2.PrintName();  // 输出 "Name: Bob"

    return 0;
}

3.C++ 支持参数个数不确定的函数吗

分析: 支持,可变参数模板(Variadic Templates)和va_list机制

  • 可变参数模板(Variadic Templates):可变参数模板是C++11引入的特性,它允许定义接受任意数量参数的函数模板。使用可变参数模板,可以在函数模板定义中使用…语法,表示参数的可变个数。通过递归展开模板参数包,可以访问和处理每个参数。
#include 
using namespace std;

// 可变参数模板的递归终止函数
void print() {
    cout << endl;
}

// 可变参数模板的递归展开函数
template<typename T, typename... Args>
void print(T first, Args... args) {
    cout << first << " ";
    print(args...);
}

int main()
{   
    print(1, 2, 3);
    print(1.0, 2.0);
    print(1, 2, 3, "hello", 4.5);

    return 0;
}

在上述示例中,print函数是一个可变参数模板函数。当递归展开时,每次都会打印第一个参数,并将剩余的参数传递给下一次递归调用,直到参数列表为空时终止。

  • va_list机制:va_list机制是C语言中的一个特性,C++也支持使用C头文件中的相关函数和宏来处理可变参数。通过使用va_list、va_start、va_arg和va_end等宏,可以在函数内部访问和处理可变数量的参数。

答案: 是的,C++支持参数个数不确定的函数。C++中有两种主要的方法来实现参数个数不确定的函数:可变参数模板(Variadic Templates)和va_list机制。

#include 
#include 
using namespace std;

// 使用va_list机制的可变参数函数
void print(int count, ...) {
    va_list args;
    va_start(args, count);
    for (int i = 0; i < count; ++i) {
        int value = va_arg(args, int);
        cout << value << " ";
    }
    va_end(args);
    cout << endl;
}


int main() {
    print(5, 1, 2, 3, 4, 5);
    return 0;
}

在上述示例中,print函数接受一个整数参数count和可变数量的整数参数。使用va_list、va_start、va_arg和va_end宏,可以在函数内部遍历并访问可变数量的参数。

4.指针形参与引用形参有什么区别

分析:
函数的形参可以是指针,此时将复制实参指针。与其他非引用类型的形参一样,该类 形参的任何改变也仅作用于局部副本。如果函数将新指针赋给形参,主调函数使用的实参指针的值没有改变。
被复制的指针只影响对指针的赋值。如果函数形参是非 const 类型的指针,则函数可通过指针实现赋值,修改指针所指向对象的值。

#include 
using namespace std;

void reset(int *p)
{
    *p = 0;
    p = 0;
}

int main()
{   
    int i = 42;
    int *p = &i;
    cout << "i: " << *p << '\n';    // 打印 42 
    cout << p << endl;              // 0x61ff08
    reset(p);
    cout << "i: " << *p << endl;    // 打印 0
    cout << p << endl;              // 0x61ff08

    return 0;
}

答案: 指针形参是指函数的参数是指针,它不会像引用形参那样通过函数调用影响实参的值,但
是调用后它会修改实参的对象。程序中建议尽量少使用指针形参,这样会使程序的可读性下降。

5.静态函数能访问类的私有成员

分析:
类的静态成员函数可以访问类的私有成员,但是静态成员函数只能直接访问类的静态 私有成员,因为静态成员函数是不可以直接访问非静态的成员的。但是静态成员函数可以借助对象名和指针来访问类的非静态私有成员。

静态函数是不可以直接访问类的私有变量的。但可以通过宏定义替换 private为 public等方法来实现对类的私有成员的访问。

答案: 静态函数只能直接访问类的静态私有成员,静态函数不可以直接访问类的非静态私有
成员,但是可以通过自定义的一些特殊方法比如宏替换访问类的非静态私有成员。

6.什么是函数模板?什么是类模板?

答案:
函数模板技术定义了参数化的非成员函数,这使得程序能够使用不同的参数类型调用 相同的函数。而至于使用何种类型,则是由编译器确定并从模板中生成相应类型的代码。 编译器确定了模板函数的实际类型参数,称之为模板的实例化。

T Add(T a, Tb)
{
	T res;
	res = a + b;
	return res;
}

类模板描述了能够管理其他数据类型的通用数据类型。类模板技术通常用于建立包含 其他类型的容器类,例如,队列、链表和堆栈等。对于这些容器类来说,无论哪种数据类 型,其操作方式是相同的,但是对具体的数据类型却又是专用的。标准模板库STL 就大量使用了这种技术。

template<class T>
class TemplateSample
{
private:
    T& eentity;
public:
    void F(T& arg);
};

7.变量的声明和定义有什么区别

答案:
为变量分配地址和存储空间的称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,但是只在一个地方定义。加入 extern 修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。

说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。

8.C 语言的关键字 static 和 C++ 的关键字 static 有什么区别

答案:
在 C 中 static 用来修饰局部静态变量和外部静态变量、函数。而 C++中除了上述功能外,还用来定
义类的成员变量和函数。即静态成员和静态成员函数。

注意: 编程时 static 的记忆性,和全局性的特点可以让在不同时期调用的函数进行通信,传递信息,而 C++的静态成员则可以在多个对象实例间进行通信,传递信息。

9.一个指针可以是 volatile 吗

答案:
可以,因为指针和普通变量一样,有时也有变化程序的不可控性。常见例:子中断服务子程序修改
一个指向一个 buffer 的指针时,必须用 volatile 来修饰这个指针。

**说明:**指针是一种普通的变量,从访问上没有什么不同于其他变量的特性。其保存的数值是个整型数据,和整型变量不同的是,这个整型数据指向的是一段内存地址。

你可能感兴趣的:(嵌入式面试题,前端,嵌入式,面试,算法,校招)