嵌入式软件工程师面试题——2025校招专题(三)

news2024/11/15 15:26:09

说明:

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

1.指针和引用的差别?

答案

  • 定义和声明:指针是一个变量,它存储了一个内存地址。可以通过使用星号(*)来声明一个指针变量。引用是一个别名,它是已存在的变量的别名,通过使用和定义原始变量相同的类型来声明引用。
  • 内存操作:指针可以通过解引用操作符(*)来访问和修改指向的内存地址中的值。可以对指针进行算术运算,如指针加法和指针减法。引用本身并不直接操作内存地址,它只是原始变量的别名,对引用的操作实际上是对原始变量的操作。
  • 空值:指针可以具有空值(NULL或nullptr),表示指针没有指向任何有效的内存地址。引用必须始终引用一个有效的对象,不能为引用赋予空值。
  • 重新赋值:指针可以被重新赋值,可以指向不同的内存地址。引用在声明后不能被重新赋值,它始终引用同一个对象。
  • 初始化:指针可以在声明时不进行初始化,也可以在稍后的时间点进行初始化。引用在声明时必须进行初始化,并且一旦初始化后,就不能再引用其他对象。
  • 对象类型:指针可以指向任何类型的对象,包括基本类型、自定义类型和指针类型。引用只能引用与其声明时相同或兼容的类型的对象。
  • 应用区别。总的来说,在以下情况下应该使用指针:一是考虑到存在不指向任何
    对象的可能(在这种情况下,能够设置指针为空),二是需要能够在不同的时刻指向不同的
    对象(在这种情况下,你能改变指针的指向)。如果总是指向一个对象并且一旦指向一个对
    象后就不会改变指向,那么应该使用引用。

2.下面5个函数哪个能够成功进行两个数的交换?

#include <iostream>
using namespace std;

void swap1(int p, int q)
{
    int temp;
    temp = p;
    p = q;
    q = temp;
}

void swap2(int *p, int *q)
{
    int *temp;
    *temp = *p;
    *p = *q;
    *q = *temp;
}

void swap3(int *p, int *q)
{
    int *temp;
    temp = p;
    p = q;
    q = temp;
}

void swap4(int *p, int *q)
{
    int temp;
    temp = *p;
    *p = *q;
    *q = temp;
}

void swap5(int &p, int &q)
{
    int temp;
    temp = p;
    p = q;
    q = temp;
}

int main()
{
    int a=1, b=2;
    // swap1(a,b); 
    // swap2(&a, &b); 
    // swap3(&a, &b); 
    // swap4(&a, &b);  
    // swap5(a,b); 

    cout << a << " " << b << endl;


    return 0;
}

解析
这道题考察函数参数传递、值传递、指针传递(地址传递)、引用传递。

  • swap1传的是值的副本,在函数体内被修改了形参p、q(实际参数a、b的一个拷贝),p、q的值确实交换了,但是它们是局部变量,不会影响到主函数中的a和b。当函数swap1生命周期结束时,p、q所在栈也就被删除了。
  • swap2传的是一个地址进去,在函数体内的形参*p、*q是指向实际参数a、b地址的两个
    指针。int *temp; *temp = *p;是不符合逻辑的一段代码,int *temp新建了一个指针(但没有分配内存)。 * temp= * p不是指向而是拷贝。把 * p所指向的内存里的值(也就是实参a的值)拷贝到 * temp所指向内存里了。但是int *temp不是不分配内存吗?的确不分配,于是系统在拷贝时临时给了一个随机地址,让它存值。分配的随机地址是个“意外”,且函数结束后不收回,造成内存泄露。那么swap2到底能否实现两数交换吗?这要视编译器而定,在vscode可以通过测试,但是在更加“严格”的编译器如vs2023,这段代码会报错。
  • swap3传的是一个地址进去,在函数体内的形参*p、*q是指向实际参数a、b地址的两个指针.int *temp; temp = p;int * temp新建了一个指针(但没有分配内存)。temp=p是指向而不是拷贝。temp指向了 * p所指向的地址(也就是a)。函数swap3不能实现两数的交换,这是因为函数体内只是指针的变化,而对地址中的值却没有改变。
  • 函数swap4可以实现两数的交换,因为它修改的是指针所指向地址中的值.
  • swap5函数与swap4类似,是一个引用传递,修改的结果直接影响实参。

答案:swap4函数和swap5函数

3.写出下面程序的运行结果

#include <iostream>
using namespace std;


int main()
{
    int a[3];
    a[0] = 0;
    a[1] = 1;
    a[2] = 2;

    int *p, *q;
    p = a;
    q = &a[2];

    cout << a[q - p] << '\n';

    return 0;
}

解析
首先,定义了一个整型数组a,它有3个元素。然后,将数组a的第一个元素赋值为0,第二个元素赋值为1,第三个元素赋值为2。

接下来,定义了两个整型指针变量p和q。将指针变量p指向数组a的首地址,即a[0]的位置。将指针变量q指向数组a的第三个元素的地址,即a[2]的位置。

然后,使用cout语句输出a[q - p]的值。这里的[q - p]表示指针q和指针p之间的偏移量,即q指向的元素在数组a中的位置与p指向的元素在数组a中的位置之间的差值。由于q指向a[2],p指向a[0],所以[q - p]的值为2-0=2。因此,输出a[2]的值,即2
答案:2

4.下列程序的输出结果是什么?

#include <iostream>
using namespace std;


class A {
public:
    int _a;
    A()
    {
        _a = 1;
    }
    void print()
    {
        printf("%d", _a);
    }
};

class B :public A {
public:
    int _a;
    B()
    {
        _a = 2;
    }
};

int main()
{
    B b;
    b.print();

    printf("%d", b._a);

    return 0;
}

A.22
B.11
C.12
D.21

解析
B类中的_a把A类中_a的覆盖掉了。在构造B类时,先调用A类的构造函数。所以A类的_a是1,而B类的_a是2。
答案:C

5.以下描述正确的是( )

A.函数的形参在函数未调用时预分配存储空间
B.若函数的定义出现在主函数之前,则可以不必再说明
C.若一个函数没有return语句,则什么值都不返回
D.一般来说,函数的形参和实参的类型应该一致

解析
A:错误的,调用到实参才会分配空间。
B:函数需要在它被调用之前被声明,这个跟main()函数无关。
C:错误的,在主函数main中可以不写return语句,因为编译器会隐式返回0;但是在一
般函数中没return语句是不行的。
D:正确的。

答案:D

6.写出函数指针、函数返回指针、const指针、指向const的指针、指向const的const指针

答案

void (*f)()         // 函数指针
void * f()          // 函数返回指针
const int *         // const指针
int * const         // 指向const的指针
const int * const   // 指向const的const指针

7.下面的数据声明都代表什么?

(1)float(*def)[10];
(2)double
(gh)[10];
(3)double(f[10])();
(4)int
((b)[10]);
(5)Long (
fun)(int)
(6)Int (
(*F)(int,int))(int)

答案
(1)float(* * def)[10];
def是一个二级指针,它指向的是一个一维数组的指针,数组的元素都是float。
(2)double*(*gh)[10];
gh是一个指针,它指向一个一维数组,数组元素都是double *。
(3)double(*f[10])();
f是一个数组,f有10个元素,元素都是函数的指针,指向的函数类型是没有参数且返回double的函数。
(4)int * (( * b)[10]);
就跟“int * (b)[10]”是一样的,是一维数组的指针。
(5)Long (
fun)(int)
函数指针。
(6)Int ( * (*F)(int,int))(int)
F是一个函数的指针,指向的函数的类型是有两个int参数并且返回一个函数指针的函
数,返回的函数指针指向有一个int参数且返回int的函数。

8.以下程序的输出是( )

#include <stdio.h>
#include <iostream>
using namespace std;


int main()
{
   
    int v[2][10] = {
        {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
        {11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
    };

    int (*a)[10] = v; // 数组指针
    
    cout << **a << endl;
    cout << **(a+1) << endl;
    cout << *(*a+1) << endl;
    cout << *(a[0]+1) << endl;
    cout << *(a[1]) << endl;

    return 0;
}

解析
本题定义一个指针指向一个10个int元素的数组。a+1表明a指针向后移动
1*sizeof(数组大小);a+1后共向后移动40个字节。*a+1仅针对这一行向后移动4个字节,如下
图所示。
在这里插入图片描述

答案:1 11 2 2 11

9.什么是数组指针?指针数组呢?

答案
数组指针和指针数组都涉及到指向数组的指针,但它们的概念和用法略有不同。
1.数组指针
数组指针是指向数组的指针,它可以看作是一个一维数组,每个元素是指向数组中元素的指针。例如,int (*p)[3]就是一个指向包含3个整数的数组的指针。使用数组指针时,可以通过解引用指针来访问数组元素,也可以使用指针算术运算来访问数组元素。

示例代码:

int arr[2][3] = {{1,2,3},{4,5,6}};
int (*p)[3] = arr; // p指向arr数组
cout << **p << endl; // 输出1
cout << *(*p + 1) << endl; // 输出

2.指针数组
指针数组是一个数组,其中每个元素都是一个指针。例如,int *arr[3]就是一个包含3个指向整数的指针的数组。使用指针数组时,可以通过下标访问数组元素,也可以使用指针解引用来访问指针指向的值。

示例代码:

int a = 1, b = 2, c = 3;
int *arr[3] = {&a, &b, &c}; // arr数组包含3个指向整数的指针
cout << *arr[0] << endl; // 输出1
cout << *(arr[1] + 1) << endl; // 错误,arr[1]是一个指针,不能进行指针算术运算

**总结:**一句话(不够准确)来说就是,数组指针是指针指向了数组,指针数组是数组里面存指针

10.写出如下程序片段的输出

#include <iostream>
using namespace std;


int main()
{
    int a[] = {1, 2, 3, 4, 5};
    int *ptr = (int*)(&a + 1);


    printf("%d %d", *(a+1), *(ptr - 1));

    return 0;
}

解析
第一个结果好理解,是正常的指针运算。
第二个的确是5。首先a表示一个1行5列数组,在内存中表示为一个5个元素的序列。int* ptr=(int*)(&a+1)的意思是,指向a数组的第6个元素(尽管这个元素不存在)。那么显然,(ptr-1)所指向的数据就是a数组的第5个元素——5。

答案:2 5

11.空指针和迷途指针的区别是什么?

答案

  • 当delete一个指针的时候,实际上仅是让编译器释放内存,但指针本身依然存在。这时它就是一个迷途指针。
  • 空指针(Null Pointer): 空指针是指没有指向任何有效对象或函数的指针。在C和C++中,可以使用特殊的宏常量NULL或者nullptr来表示空指针。空指针是一个有效的指针值,但它并不指向任何有效的内存地址。当我们声明一个指针变量但没有给它赋初值时,默认情况下它的值是空指针。

通常,如果在删除一个指针后又把它删除了一次,程序就会变得非常不稳定,任何情况都有可能发生。但是如果你只是删除了一个空指针,则什么事都不会发生,这样做非常安全。
使用迷途指针或空指针(如MyPtr=0)是非法的,而且有可能造成程序崩溃。如果指针是空指针,尽管同样是崩溃,但它同迷途指针造成的崩溃相比是一种可预料的崩溃。这样调试起来会方便得多。

12.C++中有了malloc/free,为什么还需要new/delete?

答案
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。然而,new和delete提供了更多的功能和便利性,优势如下:

  • 类型安全性:new和delete是类型安全的,它们会自动根据指定的类型进行内存分配和释放。在使用new时,不需要显式地指定分配的内存大小,编译器会根据类型自动计算所需的内存大小。而malloc()需要手动计算所需的内存大小,并进行强制类型转换。
  • 构造和析构函数的调用:new运算符在分配内存后,会自动调用对象的构造函数进行初始化。而malloc()只是简单地分配内存空间,不会调用构造函数。同样,delete运算符在释放内存前,会自动调用对象的析构函数进行清理工作。而free()只是简单地释放内存空间,不会调用析构函数。
  • 重载支持:new和delete运算符可以被重载,允许用户自定义内存分配和释放的行为。这对于实现自定义的内存管理策略、跟踪内存分配情况等非常有用。
  • 对象数组的处理:new运算符可以方便地分配对象数组,并在释放时自动调用每个对象的析构函数。而malloc()和free()无法正确处理对象数组的构造和析构。

13.说一下C++的this指针?

答案:
(1)This指针本质是一个函数参数,只是编译器隐藏起形式的,语法层面上的参数。this只能在成员函数中使用,全局函数、静态函数都不能使用this。实际上,成员函数默认第一个参数为T* const this。如:

class A {
public:
    int func(int p) { }
};

其中,func的原型在编译器看来应该是:

int func(A* const this, int p);

(2)this在成员函数的开始前构造,在成员的结束后清除。这个生命周期同任何一个函数的参数是一样的,没有任何区别。当调用一个类的成员函数时,编译器将类的指针作为函数的this参数传递进去。如:

A a;
a.func(10);

此处,编译器将会编译成:

A::func(&a, 10);

看起来和静态函数没差别,不过,区别还是有的。编译器通常会对this指针做一些优化,因此,this指针的传递效率比较高,如VC通常是通过ecx寄存器传递this参数的。
(3)this指针并不占用对象的空间。
this相当于非静态成员函数的一个隐函的参数,不占对象的空间。它跟对象之间没有包含关系,只是当前调用函数的对象被它指向而已。
所有成员函数的参数,不管是不是隐含的,都不会占用对象的空间,只会占用参数传递时的栈空间,或者直接占用一个寄存器。
(4)this指针是什么时候创建的?
this在成员函数的开始执行前构造,在成员的执行结束后清除。
但是如果class或者struct里面没有方法的话,它们是没有构造函数的,只能当做C的struct使用。采用TYPE xx的方式定义的话,在栈里分配内存,这时候this指针的值就是这块内存的地址。采用new方式创建对象的话,在堆里分配内存,new操作符通过eax返回分配的地址,然后设置给指针变量。之后去调用构造函数(如果有构造函数的话),这时将这个内存块的地址传给ecx。
(5)this指针存放在何处?堆、栈、还是其他?
this指针会因编译器不同而有不同的放置位置。可能是堆、栈,也可能是寄存器。
(6)this指针是如何传递给类中的函数的?绑定?还是在函数参数的首参数就是this指针?那么,this指针又是如何找到“类实例后函数”的?
大多数编译器通过ecx寄存器传递this指针。事实上,这也是一个潜规则。一般来说,不
同编译器都会遵从一致的传参规则,否则不同编译器产生的obj就无法匹配了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1133996.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu 安装 docker-compose

在Ubuntu上安装Docker Compose&#xff0c;可以按照以下步骤进行操作&#xff1a; 下载 Docker Compose 二进制文件 sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker…

【static + 代码块+toString打印对象】

文章目录 static成员static修饰成员变量static成员变量初始化代码块 对象的打印写show方法打印对象调用toString打印对象 总结 static成员 举例&#xff1a;一个班的学生&#xff0c;在实例化每个人的名字&#xff0c;年龄&#xff0c;学号等学员信息时都不一样&#xff0c;但…

Redis单线程还是多线程?

&#x1f603; 个人学习笔记&#xff0c;不喜勿喷&#xff0c;望大佬指正&#xff01; 目录 一、Redis为什么选择单线程1. 是什么&#xff1f;2. why&#xff0c;为什么之前选择单线程&#xff1f;3. 作者原话使用单线程原因&#xff0c;官网证据 二、 为什么逐渐加入多线程特性…

网络安全,下一个十年饭碗稳了!

“战役”阶段&#xff0c;多个行业线下转为线上求生&#xff0c;云买菜、云蹦迪、云相亲、云聚会悄然兴起&#xff0c;未来属于云时代似乎成为互联网的共识&#xff0c;而与互联网息息相关的网络安全领域&#xff0c;已经站上了了蓬勃发展的“风口” 如今&#xff0c;网络安全前…

如何用Postman做接口自动化测试?一文5个步骤带你成功实现!

什么是自动化测试 把人对软件的测试行为转化为由机器执行测试行为的一种实践。 例如GUI自动化测试&#xff0c;模拟人去操作软件界面&#xff0c;把人从简单重复的劳动中解放出来 本质是用代码去测试另一段代码&#xff0c;属于一种软件开发工作&#xff0c;已经开发完成的用例…

Python超入门(6)__迅速上手操作掌握Python

# 26.函数和参数 # 注意&#xff1a;有参函数和无参函数的名字要不同 def user(): # 无参函数print("hello world!")def user1(my_id): # 有参函数print(my_id)def user2(first_name, last_name): # 有参函数print(fMy name is {first_name} {last_name})print(&…

商超便利店经营小程序商城的作用是什么

店铺流量越来越低&#xff0c;线上平台入驻费用也不低&#xff0c;营销方式单一&#xff0c;店铺内经营数据无法统计&#xff0c;客户进店难&#xff0c;消费难&#xff0c;复购更难&#xff0c;越来越多的商超便利商家开始通过数字化商城解决问题。 通过【雨科】平台搭建商超便…

HackTheBox - Starting Point -- Tier 0 --- Dancing

文章目录 一 题目二 实验过程 一 题目 Tags Network、Protocols、SMB、Reconnaissance、Anonymous/Guest Access译文&#xff1a;网络、协议、SMB、侦察、匿名/访客访问Connect To attack the target machine, you must be on the same network.Connect to the Starting Poi…

基于springboot实现篮球竞赛预约平台管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现篮球竞赛预约平台管理系统演示 摘要 随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;篮球竞赛预约平台也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;同时信息量也越来越庞大&…

统计学习方法 支持向量机(下)

文章目录 统计学习方法 支持向量机&#xff08;下&#xff09;非线性支持向量机与和核函数核技巧正定核常用核函数非线性 SVM 序列最小最优化算法两个变量二次规划的求解方法变量的选择方法SMO 算法 统计学习方法 支持向量机&#xff08;下&#xff09; 学习李航的《统计学习方…

SpringBoot 源码分析(二) 自动装配过程分析

一、自动装配原理前置知识 自动装配就是自动将bean装载到Ioc容器中。实际上在spring 3.x版本中&#xff0c;Enable模块驱动注解的出现&#xff0c;已经有了一定的自动装配的雏形&#xff0c;而真正能够实现这一机制&#xff0c;还是在spirng 4.x版本中&#xff0c;conditional条…

微信小程序实现网页详情

方案一、使用微信小程序官方提供的webview 前提已经在微信公众平台开发管理配置好了安全域名即&#xff1a; 方案二、把网页转成pdf直接展示 前提已经在微信公众平台开发管理配置好了安全域名即&#xff1a; 实现思路是发起网络请求拿到pdf下载地址&#xff0c;然后wx.download…

MySQL的优化利器:索引条件下推,千万数据下性能提升273%

MySQL的优化利器&#xff1a;索引条件下推&#xff0c;千万数据下性能提升273%&#x1f680; 前言 上个阶段&#xff0c;我们聊过MySQL中字段类型的选择&#xff0c;感叹不同类型在千万数据下的性能差异 时间类型&#xff1a;MySQL字段的时间类型该如何选择&#xff1f;千万…

JAVA毕业设计104—基于Java+Springboot+Vue的医院预约挂号小程序(源码+数据库)

基于JavaSpringbootVue的医院预约挂号小程序(源码数据库)104 一、系统介绍 本系统前后端分离带小程序 小程序&#xff08;用户端&#xff09;&#xff0c;后台管理系统&#xff08;管理员&#xff0c;医生&#xff09; 小程序&#xff1a; 预约挂号&#xff0c;就诊充值&…

GWAS软件包:GAPIT3它来啦

GAPIT是一款非常老的而且非常流行的软件包&#xff0c;傻瓜式操作&#xff0c;一键出图出结果&#xff0c;一篮子的解决方案&#xff0c;是我最经常使用的GWAS分析软件包。 最近&#xff0c;GAPIT现在的版本是GAPIT3&#xff0c;速度比第二版有较大的提升&#xff1a; 更大的变…

快速解决安装ps打开找不到MSVCP140.dll问题,教你5个解决方法,

如果你在安装 Photoshop 时遇到找不到MSVCP140.dll的问题&#xff0c;MSVCP140.dll是Microsoft Visual C 2015 Redistributable的一个组件&#xff0c;它提供了许多常用的C函数库&#xff0c;用于支持各种软件的正常运行。当安装或运行某些软件时&#xff0c;如果系统中MSVCP14…

鱼眼图像去畸变python / c++

#鱼眼模型参考链接 本文假设去畸变后的图像与原图大小一样大。由于去畸变后的图像符合针孔投影模型&#xff0c;因此不同的去畸变焦距得到不同的视场大小&#xff0c;且物体的分辨率也不同。可以见上图&#xff0c;当焦距缩小为一半时&#xff0c;相同大小的图像&#xff08;横…

【办公常用软件分享】

在平时的工作生活中&#xff0c;经常会遇到各种各样的需求&#xff0c;没有合适的工具&#xff0c;不仅会降低效率&#xff0c;还会影响结果&#xff0c;有些工具的功能虽然能够满足&#xff0c;但是需要付费&#xff0c;偶尔用一次总显得不划算&#xff0c;所以今天就分享几个…

Modbus转Profinet网关连接三菱变频器博图快速配置案例

本案例将分享如何使用兴达易控的modbus转profinet网关&#xff08;XD-MDPN100&#xff09;来连接西门子1200系列plc&#xff0c;并实现三菱变频器的485通讯兼容转modbusTCP通信。通过在博图中进行配置&#xff0c;我们可以实现设备之间的连接和通信。 首先&#xff0c;我们需…

通信基础(三):多路复用技术

一、时分复用 时分复用造成线路资源的浪费: 使用时分复用系统传送计算机数据时&#xff0c;由于计算机数据的突发性质&#xff0c;用户对分配到的子信道的利用率一般是不高的。 二、 统计时分复用 STDM(Statistic TDM)