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

说明:

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

1.指针自增自减有什么不同?

分析:
指针的自增和自减是将指针的所指地址加1或者减1的操作,也有前置和后置的两种使用形式。
下面先来看一个指针自增的示例代码。

#include 
using namespace std;


int main()
{
    char str[] = "chinses people";
    char *p = &str[0];
    char *tem = &str[1];
    printf("p=%p\n",p);
    printf("*p=%c\n",*p);
    ++*p++;
    printf("p=%p\n",p);
    printf("*p=%c\n",*p);
    puts(str);

    return 0;
}

答案: 变量的自增自减是修改变量的值,指针的自增自减是修改指针的指向地址

2.什么是左值和右值

分析:

  • 左值可以出现在赋值语句的左边或右边,也就是说左值可以当右值使用。右值只能出 现在赋值的右边,不能出现在赋值语句的左边。左值表示程序中必须有一个特定的名字引用到这个值。右值表示程序中没有一个特定的名字引用到这个值。
  • 变量是左值,因此可以出现在赋值语句的左边。数字字面值是右值,因此不能被赋值。 实际上,左值是一个存储地址,也就是一块内存存储数据所要操作的地址。而右值是一个 具体的数据或者数值,也就是该内存存储的数据内容。只有左值和右值都是单一变量的时候二者才可以相互交换位置,因为变量具有固定的内存地址。

答案: 变量是左值,可以出现在赋值语句的左边。数字字面值是右值,不能被赋值。

3.变量默认初始化有什么规则

解析:

  • 当定义没有初始化表达式的变量时,系统有时候会自动帮编程者初始化变量。这时,系统会提供什么样的值取决于变量的类型和变量定义的位置。
  • 内置类型变量是否自动初始化取决于变量定义的位置。在函数体外定义的变量都初始 化为0,在函数体里定义的内置类型变量不进行自动初始化。除了用作赋值操作符的左操作数,未初始化变量用作任何其他用途都是没有定义的。
  • 类自身定义了该类型的对象可以怎样初始化。类通过定义一个或多个构造函数来控制 类对象的初始化。如果定义某个类的变量时没有提供初始化,这个类也可以定义初始化时的操作。它是通过定义一个特殊的构造函数即默认构造函数来实现的。

答案:
系统有时候会自动帮编程者初始化未初始化的变量。使用未初始化的变量是常见的程 序错误,但通常也是很难以发现的错误,有时候会导致编程者在程序运行时发生的问题原 因上出现判断错误。所以建议每个内置类型的对象都要初始化。虽然这样并不是完全必需的,但是会让程序容易和安全。

4.变量有哪几种存储类型

答案:

  • 自动类型变量 (auto)
    自动类型只能是局部类型的变量,属于动态存储类型。
  • 静态类型变量 (static)
    static, 即在程序运行的过程中静态变量始终是占用一个存储空间。静态变量只能在他 的作用范围内使用,使用局部静态变量是为了在下次调用该函数时,能使用上次调用后得到的该变量的值。
  • 寄存器类型变量 (register)
    属于动态存储类型,编译器不为寄存器类型的变量分配内存空间,而是直接使用CPU 的寄存器。以便提高对这类变量的存取速度。主要用于控制循环次数等不需要长期保存值得变量。
  • 外部类型变量 (extern)
    外部类型变量必须是全局变量,在C++ 中,有两种情况需要使用外部类型变量。 一种 是在同一源程序文件中,当在全局的定义之前使用该变量时,在使用前要对该变量进行外部类型变量声明。另一种是当程序有多个文件组成时,若在一个源文件中要引用在另一个源文件中定义的全局变量,则在引用前必须对所引用的变量进行外部声明。
    如果在某文件中定义的全局变量不想被其他文件所调用,则必须将该变量声明为静态全局变量,也就是说,静态全局变量只能供所在的文件使用。

5.C 与 C++ 有什么区别

答案:

  • 面向对象编程:C++引入了面向对象编程(OOP)的概念,允许使用类、继承、封装、多态等OOP特性。C语言则是一种结构化、过程式的编程语言,没有直接支持OOP。
  • 强类型语言:C++是一种强类型语言,对于变量的类型要求非常严格。在C++中,需要进行显式的类型转换,以确保类型安全。而C语言对类型转换较为宽松,可以进行隐式的类型转换。
  • 新增关键字和类型:C++引入了一些新的关键字和类型,如class、virtual、template等。此外,C++还引入了bool类型、引用(reference)类型、命名空间等概念,以增强语言的功能和表达能力。
  • 参数规定:C++支持默认参数和函数重载,这意味着函数可以有默认值的参数,并且可以在同一作用域内定义多个同名的函数,只要它们的参数列表不同。而C语言不支持这些特性,函数的参数列表必须完全匹配。
  • 参数格式和变量作用域:C和C++对于函数参数的格式要求略有不同。C语言的函数参数列表要求在函数声明中列出具体的参数名,而C++则只需要指定参数类型即可。此外,C和C++对于变量的作用域规则也有一些差异。
  • 引用(reference)的使用:C++引入了引用(reference)类型,它可以用来创建一个变量的别名。通过引用,可以实现对变量的原地修改,而不需要通过指针进行操作。C语言没有引用这个概念,只能通过指针来实现类似的功能。

6.文件输入输出有哪几种方式

答案:
fstream 头文件定义了3种支持文件I/O的类型,如下所示

  • ifstream: 由 istream 派生而来,提供读文件的功能。
  • ofstream: 由 ostream 派生而来,提供写文件的功能。
  • fstream: 由 iostream派生而来,提供读写同一个文件的功能。
    这些类型都由相应的 iostream 类型派生而来,这个事实意味着编程者已经知道使用 fstream类型需要了解的大部分内容了。特别是,可使用I/O 操作符(<<和>>)在文件 上实现格式化的I/O, 而且在前面章节介绍的条件状态也同样适用于fstream 对象。 fstream 类型除了继承下来的行为外,还定义了两个自己的新操作 open 和 close, 以及形参为要打 开的文件名的构造函数。fstream、ifstream 或ofstream 对象可调用这些操作,而其他的I/O 类型则不能调用。
    一个文件写入的示例代码如下:
#include 
using namespace std;

int a(void)
{
    return 110;
}


int main(void)
{
    ofstream oftest;
    char filename[16] = "c:\\a.txt";
    oftest.open(filename, ostream::app);
    oftest.close();
    oftest.clear();
    return 0;
}

7.宏定义与操作符的区别

分析:

  • 宏定义是C 语言开始提供的3种预处理功能的其中一种。这3种预处理分别是:宏定义、文件包含和条件编译。
  • 宏定义的语法格式为:#define<标识符><字符串>,其中的标识符就是所谓的符号常量,也称为“宏名”。预处理(预编译)工作也叫做宏展开,即将宏名替换为字符串。
  • 单的宏定义将一个标识符定义为一个字符串,源程序中的该标识符均以指定的字符串 来代替。预处理命令后通常不加分号。这并不是说所有的预处理命令后都不能有分号出现。 由于宏定义只是用宏名对一个字符串进行简单的替换,因此如果在宏定义命令后加了分号,将会连同分号一起进行置换。
  • 带参数的宏定义格式为:#define <宏名>(<参数表>)<宏体>。其中,<宏名>是一个 标识符,<参数表>中的参数可以是一个,也可以是多个,视具体情况而定,当有多个参数 的时候,每个参数之间用逗号分隔。<宏体>是被替换用的字符串,宏体中的字符串是由参数表中的各个参数组成的表达式。
  • 宏定义与操作符的区别:宏定义是替换,不做计算,也不做表达式求解。另外宏定义的替换在编译前进行,所以它不占用内存。宏的展开不占运行时间,只占编译时间,而操作符则占运行时间。

答案: 宏定义是C++ 的预处理命令之 一 ,它是 一个替换操作,不做计算和表达式求解,不占
用内存和编译时间。

8.包含头文件时,如何查找?

答案:
在C++中,包含头文件是一种常见的技术,用于向程序中添加预定义的代码和声明。当编译器遇到#include指令时,会查找相应的头文件并将其包含到源代码中。
通常情况下,编译器查找头文件的路径由以下几个因素决定:

  1. 系统默认路径:编译器通常会在系统默认的头文件路径中查找所需的头文件。这些路径可能包括操作系统的标准库、编译器安装目录等。
  2. 环境变量:有些编译器可能会从环境变量中获取额外的头文件搜索路径。例如,在Linux系统中,可以通过设置$CPLUS_INCLUDE_PATH环境变量来添加额外的头文件路径。
  3. 编译器选项:有些编译器提供了选项来指定额外的头文件路径。例如,在GCC中,可以使用-I选项来添加额外的头文件搜索路径。
    在实际编程中,如果需要包含一个头文件,但编译器无法找到该头文件,就会发生编译错误。为了避免这种情况,可以采取以下措施:
  4. 明确指定头文件路径:如果头文件不在默认的搜索路径中,可以使用编译器提供的选项或者环境变量来指定额外的搜索路径。
  5. 将头文件放在当前目录或项目目录:将头文件直接放在当前目录或项目目录下,编译器会自动搜索当前目录和项目目录。
  6. 使用相对路径或绝对路径:可以使用相对路径或绝对路径来包含头文件。相对路径是相对于当前源代码文件的路径,而绝对路径是从根目录开始的完整路径。

9.虚函数与纯虚函数的区别

答案:
在C++中,虚函数和纯虚函数都是用于实现多态性的重要机制。它们都需要通过继承来使用,并且都可以被子类重写。
虚函数是一种在基类中声明的函数,在派生类中可以进行重写。当通过基类指针或引用调用虚函数时,实际执行的是派生类中的版本。虚函数可以有实现,如果在基类中定义了虚函数的实现,那么派生类可以选择保留该实现或进行修改。虚函数可以通过关键字virtual来声明,并且可以通过关键字override来显式地重写。
纯虚函数是一种在基类中声明但没有实现的虚函数。它需要在派生类中进行具体的实现,否则派生类也将成为抽象类。纯虚函数可以通过在函数声明后加上=0来声明,例如:

class Base {
public:
    virtual void foo() = 0; // 纯虚函数
};

需要注意的是,包含纯虚函数的类通常被称为抽象类,它无法被直接实例化。如果想要使用这个类,必须先创建一个派生类并实现所有的纯虚函数。此外,派生类也可以选择重写其父类的非纯虚函数,以满足自己的需求。

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