大疆嵌入式笔试试题整理最全

大疆嵌入式笔试试题整理最全

加粗内容:需要补习的

主要整理大疆的嵌入式笔试,题目来自:https://blog.csdn.net/m0_46132568/article/details/126223090

大疆嵌入式笔试(试题1)

  1. 缓存和寄存器那个比较快?
    CPU-寄存器-缓存-内存

  2. 波特率是什么单位。比特率是什么单位

    1波特即每秒传输1 个码元符号(通过不同的调制方式,可以在一个码元上负载多个bit位信息)。1比特每秒是指每秒传输1 比特(bit)

    实际是哦那个中,常用串口通信协议举例,条件是:1个起始位,8个数据位,0个校验位,1个停止位,也就是说,8,N,1;8个数据位,一个停止位,无校验位。

    这个条件分析一下就是,如果我要传输一个字节(8 位)的数据,则我应该在总线上产生 10 个电平变换,也就是串行总线上,需要 10 位,才能发送 1 个字节数据。

    1秒可以发送9600位,那么用9600/10,就是1秒可以发送 960 个字节数据,则每发送一个字节需要的时间就是:1/960 ~= 0.00104166…s ~= 1.0416667 ms。

    此时就可以得出一个结论,在 9600 波特率下,大概 1ms 发送 1 个字节的数据。

  3. 对ARM处理器异常的理解

    外部中断,内部异常,系统调用都属于

  4. Cotex_M基础知识的掌握

    LDR R0,=0x12345678是直接将值赋给R0吗
    就是把0x12345678这个地址中的值存放到r0中

  5. IO密集型类知识

  6. 支持优先级继承调度器会动态改变任务优先级

  7. git命令相关知识

  8. )哪些不是实时操作系统

    要注意的是带有RT的基本上都是实时性操作系统
    WIN,Linux都是分时操作系统

  9. ARM_V8支持64位?

    大致来说,ARMv8架构跟AMD和Intel的做法一样,采取64位兼容32位的方式。应用程序可以以32位模式执行,也可以以64位模式执行

  10. )NEON,PMIPB等操作

  11. SPI有几种工作模式
    SPI总线有四种工作方式(SP0, SP1, SP2, SP3),其中使用的最为广泛的是SPI0和SPI3方式。

  12. 栈空间大小会不会影响编译出来的bin固件大小
    这个。。暂时还没听说过

  13. 00编译优化是不是默认不优化

  14. inline内联基础知识及其作用

    在 c/c++ 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。

  15. 给图片大小和码率还是啥,求带宽多少Gbps

  16. VTOR作用,普通中断是不是只把R0-R3数据压栈,PRIMASK作用

VTOR中断向量表偏移寄存器

  1. cortex-M3 中断调用过程
    入栈

中断发生后,中断服务函数运行前,会先把xPSR, PC, LR, R12以及R3‐R0总共8个寄存器由硬件自动压入适当的堆栈中(中断前使用的MSP就压入MSP,中断前使用的是PSP就压入PSP

  1. 内联函数的作用
    避免函数调用的开销
  2. ARM Cotex -M 都是哈佛体系?-A冯诺依曼体系?
    有一些ARM(Cortex-M系列)是哈佛结构,而另一些ARM(Cortex-A)是冯诺依曼结构(或者更准确说是混合结构)
  3. I2S总线相关知识

2S特点
①支持全双工和半双工通信。(单工数据传输只支持数据在一个方向上传输;半双工数据传输允许数据在两个方向上传输,但是在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;全双工数据通信允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。
②支持主/从模式。(主模式:就是主CPU作为主机,向从机(挂载器件)发送接收数据。从模式:就是主CPU作为从机,接收和发送主机(挂载器件)数据。而主从机的分别其实是一个触发的作用,主机主动触发,从机只能被动响应触发。)

I2S总线拥有三条数据信号线:
1、SCK: (continuous serial clock) 串行时钟
对应数字音频的每一位数据,SCK都有1个脉冲。SCK的频率=2×采样频率×采样位数。
2、WS: (word select) 字段(声道)选择
用于切换左右声道的数据。WS的频率=采样频率。
命令选择线表明了正在被传输的声道。
WS为“1”表示正在传输的是左声道的数据。
WS为“0”表示正在传输的是右声道的数据。
WS可以在串行时钟的上升沿或者下降沿发生改变,并且WS信号不需要一定是对称的。在从属装置端,WS在时钟信号的上升沿发生改变。WS总是在最高位传输前的一个时钟周期发生改变,这样可以使从属装置得到与被传输的串行数据同步的时间,并且使接收端存储当前的命令以及为下次的命令清除空间。
3、SD: (serial data) 串行数据
用二进制补码表示的音频数据。 I2S格式的信号无论有多少位有效数据,数据的最高位总是被最先传输(在WS变化(也就是一帧开始)后的第2个SCK脉冲处),因此最高位拥有固定的位置,而最低位的位置则是依赖于数据的有效位数。也就使得接收端与发送端的有效位数可以不同。如果接收端能处理的有效位数少于发送端,可以放弃数据帧中多余的低位数据;如果接收端能处理的有效位数多于发送端,可以自行补足剩余的位(常补足为零)。这种同步机制使得数字音频设备的互连更加方便,而且不会造成数据错位。为了保证数字音频信号的正确传输,发送端和接收端应该采用相同的数据格式和长度。当然,对I2S格式来说数据长度可以不同。

  1. I2C主机发送__作为初始信号

    起始信号:SCL为高电平期间,SDA线由高电平向低电平的变化

  2. 栈空间大小多大,往一个数组写4K btye数据栈会不会溢出?如果会还有哪些情况会溢
    出?如果不会溢出会发生什么问题?
    自行测试linux的栈的默认空间在7~8Mbyte之间接近8M,通过ulimit -s可以知道理论值是8M
    但是栈的空间是不固定的,我们可以用ulimit -s 10240 更改其栈空间,单位KByte
    当然不同系统的默认栈值可能不一样,比如win系统的栈不到1M

    对于堆内存来说,可以申请的内存非常大,几乎可以申请超万个G我也不知道为什么。
    当一个进程退出后,不管是正常退出或者是异常退出,操作系统都会释放这个进程的资源。包括这个进程分配的内存。

    1. 写一个宏定义,给一个结构体成员地址,返回结构体的首地址

(1)给一个正整数n,求从1-n这些数字总共出现’1’的次数(编程题目)
(2)求圆周率的N位精度,这个N可能非常非常大(编程题目)

试题2

image-20220814112028130

参考链接

https://blog.csdn.net/u013193026/article/details/39367825

https://blog.csdn.net/qq_42957923/article/details/88052888

image-20220814114353260

Linux系统中内核线程和普通线程的区别

普通线程和内核线程

https://www.cnblogs.com/alantu2018/p/8526916.html

内核线程和用户线程

https://blog.csdn.net/dongyanxia1000/article/details/68947620)

试题3

题目:https://blog.csdn.net/weixin_43166958/article/details/98477895

选择题
  1. 求a的值

    经过表达式a = 5 ? 0 : 1的运算,变量a的最终值是     0
    
    

boolean虚拟机取值时候是掩码去掉前七位之后取末尾判断,0是false,1是true,而5对应的是00001001,所以这块表示的是1,也就是true,所以对应的是三目运算里面的结果是 0

  1. 数组int a[3][4]中的a[2][1]用其他形式表示:

    *(*(a+2)+1)
     *(a[2] + 1)
     int *p = &a[0][0] ;//p[9], 表示 a[2][1];
    
  2. 在soc中常常用做对外设寄存器配置总线的是

    i2c,spI

  3. 哈佛结构和冯诺依曼结构的区别:

    要理解哈弗结构和冯诺依曼结构的区别,首先要知道我们在编写程序的时候其实可以对程序的代码划分为两个部分,一部分是程序编写完成后就不再需要对其进行修改了的(也就是逻辑代码部分)另一部分就是在程序编写完毕后其内容会随着程序的运行而不断变化的部分(也就是定义变量)。而哈佛结构和冯诺依曼结构就是对于这个两部分代码的存储方式的区别。
    哈佛结构(Harvard architecture)是一种将程序指令储存和数据储存分开的存储器结构。

    中央处理器首先到程序指令储存器中读取程序指令内容,解码后得到数据地址,再到相应的数据储存器中读取数据,并进行下一步的操作(通常是执行)。程序指令储存和数据储存分开,数据和指令的储存可以同时进行,可以使指令和数据有不同的数据宽度。

哈佛结构的微处理器通常具有较高的执行效率。其程序指令和数据指令分开组织和储存的,执行时可以预先读取下一条指令。
大多数ARM、DSP是哈佛结构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

冯.诺伊曼结构(von Neumann architecture)是一种将程序指令存储器和数据存储器合并在一起的存储器结构。

大多数CPU和GPU是冯诺依曼结构的。

image-20220814133238983

诺依曼结构则是将逻辑代码段和变量统一都存储在内存当中,他们之间一般是按照代码的执行顺序依次存储。这样就会导致一个问题,如果当程序出现BUG的时候,由于程序没有对逻辑代码段的读写限定,因此,他将拥有和普通变量一样的读写操作权限。于是就会很容易的死机,一旦他的逻辑执行出现一点该变就会出现非常严重的错误。但是,冯诺依曼结构的好处是可以充分利用有限的内存空间,并且会使CPU对程序的执行十分的方便,不用来回跑。

  1. 联合体和结构体

结构体(struct)各成员各自拥有自己的内存,各自使用互不干涉,同时存在的,遵循内存对齐原则。一个struct变量的总长度等于所有成员的长度之和。

而联合体(union)各成员共用一块内存空间,并且同时只有一个成员可以得到这块内存的使用权(对该内存的读写),各变量共用一个内存首地址。因而,联合体比结构体更节约内存。一个union变量的总长度至少能容纳最大的成员变量,而且要满足是所有成员变量类型大小的整数倍。

#include<stdio.h>
//结构体
struct u  //u表示结构体类型名
{
    char a;     //a表示结构体成员名
    int b;
    short c;
}U1;      
//U1表示结构体变量名  
//访问该结构体内部成员时可以采用U1.a=1;其中"点"表示结构体成员运算符
 
//联合体
union u1  //u1表示联合体类型名
{
    char a;    //a表示联合体成员名
    int b;
    short c;
}U2;
//U2表示联合体变量名  
//访问该联合体内部成员时可以采用U2.a=1;其中"点"表示联合体成员运算符
 
 
//主函数
int main(){
    printf("%d\n",sizeof(U1));
    printf("%d\n",sizeof(U2));
    return 0;
}
 
/*程序运行结果是:
12
4*/

填空题4道

大小端问题

A=0x12345678存入地址1000H~10003H中,

小端模式:1000H=78 1001H=56 1002H=34 1003H=12

大端模式:1000H=12 1001H=34 1002H=56 1003H=78

宏定义计算问题

#define  PRODUCT (x) (x*x)

int main()
{
	int a,b=3;
	a=PRODUCT(b+2);
}

求a值:

b+2*b+2=3+2*3+2=11

b+2b+2=3+23+2=11

void foo(void)
{
    unsigned int a = 6;
    int b = -20;
    int c;
    (a+b > 6) ? (c=1) : (c=0);
}

这个问题测试C语言中的整数自动转换原则,这无符号整型问题的答案是c=1。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果c=1。

简答题3道
  1. 简述任务/线程之间的同步方式

    https://www.cnblogs.com/love-lzb/p/6859072.html?utm_source=itdadao&utm_medium=referral

    互斥锁:锁机制是同一时刻只允许一个线程执行一个关键部分的代码。

    条件变量:条件变量是利用线程间共享全局变量进行同步的一种机制。条件变量上的基本操作有:触发条件(当条件变为 true 时),等待条件,挂起线程直到其他线程触发条件。

    信号量:为控制一个具有有限数量用户资源而设计。

    事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。

  2. 简述可执行程序的内存布局

    image-20220814134146390
  3. 设计机制保证锁可以按照先到先得的方式被任务获取,并且占用内存空间最小(题目没有说全,记不清,考察操作系统和队列的应用)

编程题2道

1、比较输入字符串s1和s2前n个字符,忽略大小写,如果字符串s1和s2相同则返回0,不同则返回第一个不同字符的差值。

tolower是一种函数,功能是把字母字符转换成小写,非字母字符不做出处理。

#include <stdio.h>  
#include <string.h>  
#include <ctype.h>  
  
int strcmpx(const char *s1, const char *s2, unsigned int n)  
{  
    int c1, c2;  
    do {  
        c1 = tolower(*s1++);  
        c2 = tolower(*s2++);  
    } while((--n > 0) && c1 == c2 && c1 != 0);  
    return c1 - c2;  
}  
int main(void)  
{  
    int n = 4;  
    char str3[] = "ABCf";  
    char str4[] = "abcd";  
    printf("strcmpx(str3, str4, n) = %d", strcmpx(str3, str4, n));  
    return 0;  
}  

2、N X N数组,输出行中最小,列中最大的数的位置,比如:

1 2 3
4 5 6
7 8 9

输出:row=2,col=0

分析:

在矩阵中,一个数在所在行中是最大值,在所在列中是最小值,则被称为鞍点。

鞍点C++实现

https://blog.csdn.net/Sherry_Yue/article/details/83833516)

//C语言输出矩阵马鞍点

#include<stdio.h>  

void Input_Matrix(int m,int n,int a[100][100])              //输入矩阵元素
{

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            scanf("%d", &a[i][j]);
        }
    }
}

void Output_Matrix(int m, int n, int a[100][100])    //输出矩阵元素
{

    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            printf("%-5d", a[i][j]);
        }
        printf("\n");
    }
}

void Matrix_Mn(int m, int n, int a[100][100])     //输出矩阵马鞍点
{
    int flag = 0;
    int min, max, k, l;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            min = a[i][j];
            for (k = 0; k < m; k++)
            {
                if (min < a[i][k])
                    break;
            }
            if (k == m)
            {
                max = a[i][j];
                for (l = 0; l < n; l++)
                {
                    if (max > a[l][j])
                        break;
                }
                if (l == n)
                {
                    printf("%-5d%-5d\n", i, j);
                    printf("矩阵元素为:a[%d][%d]=%d\n",i,j, a[i][j]);
                    flag = 1;
                }
            }
        }
    }
    if (flag == 0)
    {
        printf("该矩阵没有马鞍点!\n");
    }
}
int main(void)
{
    int m, n;
    int a[100][100];

    for (;;)
    {

        printf("请输入矩阵的行数和列数:\n");
        scanf("%d %d", &n, &m);
        printf("请输入矩阵中的元素:\n");
        Input_Matrix(m, n, a);
        printf("矩阵输出为:\n");
        Output_Matrix(m, n, a);
        printf("马鞍点输出(该点所在的行数和列数):\n");
        Matrix_Mn(m, n, a);
    }
    return 0;
}

题目要求是一个数在所在行中是最小值,在所在列中是最大值,不确定还能不能称为鞍点,但是算法思路相似的。

思路:

  • 先找第i行上最小的元素t,以及所在列minj
  • 判断t是否为第minj列的最大值,如果不是则在minj列中继续寻找最大值并输出,如果是则输出
#include <stdio.h>
#define N 3
int a[N][N]={1,2,3,4,5,6,7,8,9};
int main()
{
    int i,j,t,minj;
    for(i=0;i<N;i++)
    {
        t=a[i][0];
        minj=0;
        for(j=1;j<N;j++)//行上最小
        {
            if(a[i][j]<t)
            {
                t=a[i][j];
                minj=j;//找到了行上最小的数所在的列
            }
        }

        int k;
        for(k=0;k<N;k++)
            if(a[k][minj]>t)//判断是否列上最大
                break;
        if(k<N) continue;//接着查找下一行
        printf("所求点是:a[%d][%d]:%d\n",i,minj,t);
    }
    return 0;
}


相关基础知识:

break:

(1).结束当前整个循环,执行当前循环下边的语句。忽略循环体中任何其它语句和循环条件测试。
(2).只能跳出一层循环,如果你的循环是嵌套循环,那么你需要按照你嵌套的层次,逐步使用break来跳出。

continue:

(1).终止本次循环的执行,即跳过当前这次循环中continue语句后尚未执行的语句,接着进行下一次循环条件的判断。
(2).结束当前循环,进行下一次的循环判断。
(3).终止当前的循环过程,但他并不跳出循环,而是继续往下判断循环条件执行语句.他只能结束循环中的一次过程,但不能终止循环继续进行。

题目四

https://blog.csdn.net/qq_38410730/article/details/80951443

填空选择题

1、ARM指令和Thumb指令。

https://www.cnblogs.com/yygsj/p/5428500.html

解答:在ARM的体系结构中,可以工作在三种不同的状态,一是ARM状态,二是Thumb状态及Thumb-2状态,三是调试状态。而ARM状态和Thumb状态可以直接通过某些指令直接切换,都是在运行程序,只不过指令长度不一样而已。

ARM状态:arm处理器工作于32位指令的状态,所有指令均为32位;
Thumb状态:arm执行16位指令的状态,即16位状态;
thumb-2状态:这个状态是ARM7版本的ARM处理器所具有的新的状态,新的thumb-2内核技术兼有16位及32位指令,实现了更高的性能,更有效的功耗及更少地占用内存。总的来说,感觉这个状态除了兼有arm和thumb的优点外,还在这两种状态上有所提升,优化;
调试状态:处理器停机时进入调试状态。

关于这个知识点还有几个注意点:

ARM处理器复位后开始执行代码时总是只处于ARM状态;
Cortex-M3只有Thumb-2状态和调试状态;
由于Thumb-2具有16位/32位指令功能,因此有了thumb-2就无需Thumb了。
另外,具有Thumb-2技术的ARM处理器也无需再ARM状态和Thumb-2状态间进行切换了,因为thumb-2具有32位指令功能。

2、哪种总线方式是全双工类型、哪种总线方式传输的距离最短?(选择题)

解答:几种总线接口的通信方式的总结如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3、TCP与UDP的区别。(选择题)

解答:TCP和UDP的区别总结如下图所示:

https://blog.csdn.net/u011957758/article/details/72353485

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4、Linux的用户态与内核态的转换方法。(选择题)

解答:Linux下内核空间与用户空间进行通信的方式主要有syscall(system call)、procfs、ioctl和netlink等。

syscall:一般情况下,用户进程是不能访问内核的。它既不能访问内核所在的内存空间,也不能调用内核中的函数。Linux内核中设置了一组用于实现各种系统功能的子程序,用户可以通过调用他们访问linux内核的数据和函数,这些系统调用接口(SCI)称为系统调用;
procfs:是一种特殊的伪文件系统 ,是Linux内核信息的抽象文件接口,大量内核中的信息以及可调参数都被作为常规文件映射到一个目录树中,这样我们就可以简单直接的通过echo或cat这样的文件操作命令对系统信息进行查取;
netlink:用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能;
ioctl:函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数控制设备的I/O通道。
5、linux目录结构,选项是/usr、/tmp、/etc目录的作用。(选择题)

image-20220814144116423

/usr:不是user的缩写,其实usr是Unix Software Resource的缩写, 也就是Unix操作系统软件资源所放置的目录,而不是用户的数据啦。这点要注意。 FHS建议所有软件开发者,应该将他们的数据合理的分别放置到这个目录下的次目录,而不要自行建立该软件自己独立的目录;

  • **/tmp:这是让一般使用者或者是正在执行的程序暂时放置档案的地方。**这个目录是任何人都能够存取的,所以你需要定期的清理一下。当然,重要资料不可放置在此目录啊。 因为FHS甚至建议在开机时,应该要将/tmp下的资料都删除;
  • /etc:系统主要的设定档几乎都放置在这个目录内,例如人员的帐号密码档、各种服务的启始档等等。 一般来说,这个目录下的各档案属性是可以让一般使用者查阅的,但是只有root有权力修改。 FHS建议不要放置可执行档(binary)在这个目录中。 比较重要的档案有:/etc/inittab, /etc/init.d/, /etc/modprobe.conf, /etc/X11/, /etc/fstab, /etc/sysconfig/等等。

6、下面这段程序的运行结果?(选择题)

int main(){
        const int x=5;
        const int *ptr;
        ptr=&x;
        *ptr=10;
        printf("%d\n",x);
 
        return 0;
}

解答:编译出错。

这道题主要是讲解const与指针的问题:

const int a;
int const a;
const int *a;
int * const a;
const int * const a;
int const * const a;

前两个的作用是一样,a是一个常整型数;
第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以);
第四个意思a是一个指向整型 数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的);
最后两个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数 是不可修改的,同时指针也是不可修改的)。
也就是说:本题x是一个常量,不能改变;ptr是一个指向常整型数的指针。而当*ptr=10;的时候,直接违反了这一点。同时要记得一点,const是通过编译器在编译的时候执行检查来确保实现的。

7、在32位系统中,有如下结构体,那么sizeof(fun)的数值是()

#pragma pack(1)
 
struct fun{
	int i;
	double d;
	char c;
};

解答:13。

可能是一般的内存对齐做习惯了,如果本题采用内存对齐的话,结果就是24(int 4 double char 7)。但是#pragma pack(1)让编译器将结构体数据强制按1来对齐。

每个特定平台上的编译器都有自己的默认“对齐系数”(32位机一般为4,64位机一般为8)。我们可以通过预编译命令#pragma pack(k),k=1,2,4,8,16来改变这个系数,其中k就是需要指定的“对齐系数”。

只需牢记:

8、Linux中的文件/目录权限设置命令是什么?(选择题)

解答:chmod

9、下面四个选项是四个整数在内存中的存储情况,请选择其中最大的一个。(选择题)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

解答:大端小端问题:

所谓的大端模式(BE big-endian),是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中(低对高,高对高);
所谓的小端模式(LE little-endian),是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中(低对低,高对高)。

那么A:12345678、B:56781234、C:12785634、D:56341278。

10、C语言的各种变量的存取区域,给你一段小程序,让你分析各个变量的存储区域(填空题)

解答:具体的题目内容忘了,但是大体上给出各个变量可能的存储区域:

堆:堆允许程序在运行时动态地申请某个大小的内存。一般由程序员分配释放;
栈:由编译器自动分配释放,存放函数的参数值,局部变量等值;
静态存储区:一定会存在且不会消失,这样的数据包括常量、常变量(const 变量)、静态变量、全局变量等;
常量存储区:常量占用内存,只读状态,决不可修改,常量字符串就是放在这里的。

11、下面这段程序的运行结果?(填空题)

int main() {
	int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
	memcpy(a + 3, a, 5);
	for (int i = 0; i<10; i++){
		printf("%d ", a[i]);
	}
	return 0;
}

解答:0 1 2 0 1 5 6 7 8 9

首先看一下内存复制函数memcpy()函数的定义:

void * memcpy ( void * destination, const void * source, size_t num );

将source指向的地址处的 num 个字节 拷贝到 destination 指向的地址处。注意,是字节。

因为memcpy的最后一个参数是需要拷贝的字节的数目!一个int类型占据4个字节!这样的话,本题5字节,实际上只能移动2个数字(往大的去)。如果要想达到将a地址开始的5个元素拷贝到a+3地址处,需要这么写:

memcpy(a + 3, a, 5*sizeof(int));

https://blog.csdn.net/baidu_35679960/article/details/80953973

12、C语言编译过程中,volatile关键字和extern关键字分别在哪个阶段起作用?(填空题)

解答:volatile应该是在编译阶段,extern在链接阶段。

volatile关键字的作用是防止变量被编译器优化,而优化是处于编译阶段,所以volatile关键字是在编译阶段起作用。

参考文章:C语言文件的编译与执行的四个阶段并分别描述volatile为什么要修饰中断里的变量

https://blog.csdn.net/yimingsilence/article/details/52800987

https://blog.csdn.net/fengyunjh6/article/details/9055359

13、linux系统打开设备文件,进程可能处于三种基本状态,如果多次打开设备文件,驱动程序应该实现什么?(填空题)

简答题

1、简述实时操作系统和非实时操作系统特点和区别。

解答:实时操作系统是保证在一定时间限制内完成特定功能的操作系统。实时操作系统有硬实时和软实时之分,硬实时要求在规定的时间内必须完成操作,这是在操作系统设计时保证的;软实时则只要按照任务的优先级,尽可能快地完成操作即可。

实时性最主要的含义是:任务的最迟完成时间是可确认预知的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2、简述static关键字对于工程模块化的作用。

解答:在C语言中,static有下3个作用:

函数体内的static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,以为其值在下次调用时仍维持上次的值(该变量存放在静态变量区);
在模块内static全局变量可以被模块内所有函数访问,但不能被模块外其他函数访问。(注意,只有在定义了变量后才能使用。如果变量定义在使用之后,要用extern 声明。所以,一般全部变量都会在文件的最开始处定义。);
在模块内的static函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内。
在嵌入式系统中,要时刻懂得移植的重要性,程序可能是很多程序员共同协作同时完成,在定义变量及函数的过程,可能会重名,这给系统的集成带来麻烦,因此保证不冲突的办法是显示的表示此变量或者函数是本地的,static即可。在Linux的模块编程中,这一条很明显,所有的函数和全局变量都要用static关键字声明,将其作用域限制在本模块内部,与其他模块共享的函数或者变量要EXPORT到内核中。

3、无锁可以提高整个程序的性能,但是CPU需要对此提供支持,请以x86/ARM为例简述。

解答:无锁编程具体使用和考虑到的技术方法包括:原子操作(atomic operations), 内存栅栏(memory barriers), 内存顺序冲突(memory order), 指令序列一致性(sequential consistency)和顺ABA现象等等。

在这其中最基础最重要的是操作的原子性或说原子操作。原子操作可以理解为在执行完毕之前不会被任何其它任务或事件中断的一系列操作。原子操作是非阻塞编程最核心基本的部分,没有原子操作的话,操作会因为中断异常等各种原因引起数据状态的不一致从而影响到程序的正确。

对于原子操作的实现机制,在硬件层面上CPU处理器会默认保证基本的内存操作的原子性,CPU保证从系统内存当中读取或者写入一个字节的行为肯定是原子的,当一个处理器读取一个字节时,其他CPU处理器不能访问这个字节的内存地址。但是对于复杂的内存操作CPU处理器不能自动保证其原子性,比如跨总线宽度或者跨多个缓存行(Cache Line),跨页表的访问等。这个时候就需要用到CPU指令集中设计的原子操作指令,现在大部分CPU指令集都会支持一系列的原子操作。

而在无锁编程中经常用到的原子操作是Read-Modify-Write (RMW)这种类型的,这其中最常用的原子操作又是 COMPARE AND SWAP(CAS),几乎所有的CPU指令集都支持CAS的原子操作,比如X86平台下中的是 CMPXCHG(Compare Are Exchange)。

继续说一下CAS,CAS操作行为是比较某个内存地址处的内容是否和期望值一致,如果一致则将该地址处的数值替换为一个新值。CAS操作具体的实现原理主要是两种方式:总线锁定和缓存锁定。所谓总线锁定,就是CPU执行某条指令的时候先锁住数据总线的, 使用同一条数据总线的CPU就无法访问内存了,在指令执行完成后再释放锁住的数据总线。锁住数据总线的方式系统开销很大,限制了访问内存的效率,所以又有了基于CPU缓存一致性来保持操作原子性作的方法作为补充,简单来说就是用CPU的缓存一致性的机制来防止内存区域的数据被两个以上的处理器修改。

最后这里随便说一下CAS操作的ABA的问题,所谓的ABA的问题简要的说就是,线程a先读取了要对比的值v后,被线程b抢占了,线程b对v进行了修改后又改会v原来的值,线程1继续运行执行CAS操作的时候,无法判断出v的值被改过又改回来。

解决ABA的问题的一种方法是,一次用CAS检查双倍长度的值,前半部是指针,后半部分是一个计数器;或者对CAS的数值加上版本号。

https://blog.csdn.net/liusuper2088/article/details/41047501

编程题

1、已知循环缓冲区是一个可以无限循环读写的缓冲区,当缓冲区满了还继续写的话就会覆盖我们还没读取到的数据。下面定义了一个循环缓冲区并初始化,请编写它的Write函数:

typedef struct RingBuf {
	char *Buf;
	unsigned int Size;
	unsigned int RdId;
	unsigned int WrId;
}RingBuf;
 
void Init(RingBuf *ringBuf, char *buf, unsigned int size) {
	memset(ringBuf, 0, sizeof(RingBuf));
	ringBuf->Buf = buf;
	ringBuf->Size = size;
	ringBuf->RdId = 0;
	ringBuf->WrId = 0;
}

解答:实际上我觉得提供的初始化代码部分,对WrId的初始化有点问题,Write()函数的完整代码如下:

typedef struct RingBuf {
	char *Buf;
	unsigned int Size;
	unsigned int RdId;
	unsigned int WrId;
}RingBuf;
 
void Init(RingBuf *ringBuf, char *buf, unsigned int size) {
	memset(ringBuf, 0, sizeof(RingBuf));
	ringBuf->Buf = buf;
	ringBuf->Size = size;
	ringBuf->RdId = 0;
	ringBuf->WrId = strlen(buf);
}
 
void Write(RingBuf *ringBuf, char *buf, unsigned int len) {
	unsigned int pos = ringBuf->WrId;
 
	while (pos + len > ringBuf->Size) {
		memcpy(ringBuf->Buf + pos, buf, ringBuf->Size - pos);
		buf += ringBuf->Size - pos;
		len -= ringBuf->Size - pos;
		pos = 0;
	}
 
	memcpy(ringBuf->Buf + pos, buf, len);
	ringBuf->WrId = pos + len;
}
 
void Print(RingBuf *ringBuf) {
	for (int i = 0; i < ringBuf->Size; i++) {
		cout << ringBuf->Buf[i];
	}
	cout << endl;
}
 
int main()
{
	RingBuf *rb = (RingBuf *)malloc(sizeof(RingBuf));
	char init_str[] = "ABC";
	int size = 6;
	Init(rb, init_str, size);
 
	char p[] = "1234567";
	Write(rb, p, 7);
	Print(rb);
 
	return 0;
}

2、已知两个已经按从小到大排列的数组,将它们中的所有数字组合成一个新的数组,要求新数组也是按照从小到大的顺序。请按照上述描述完成函数:

int merge(int *array1, int len1, int *array2, int len2, int *array3);

解答:这道题本质上就是一道合并排序,网上一大堆的程序案例,就不多介绍了。下面这段程序是我自己写的,并不是从网上贴的,如果有一些BUG,还请指出。

int merge(int *array1, int len1, int *array2, int len2, int *array3) {
	int retn = len1 + len2;
	if ((*array1 < *array2 || len2 == 0) && len1 > 0) {
		*array3 = *array1;
		merge(++array1, --len1, array2, len2, ++array3);
	}
	if ((*array1 >= *array2 || len1 == 0)  && len2 > 0) {
		*array3 = *array2;
		merge(array1, len1, ++array2, --len2, ++array3);
	}
	return retn;
}

题目五

选择题目

1、3个进程,需要的资源数依次为4,5,6,为了防止死锁,所需的最少资源数为(B)

A、12 B、13 C、14 D、15

解析:最差情况各进程占用3,4,5,再有一个资源时候,其中一个进程完成释放资源,所以3+4+5+1=13

2、Thumb指令集支持16位、32位。

3、类似宏定义计算问题(64位系统,char **a[5][6],sizeof(a))

#define  PRODUCT (x) (x*x)
 
int main()
{
	int a,b=3;
	a=PRODUCT(b+2);
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

b+2*b+2=3+2*3+2=11

4、嵌入式系统的特点:

  • 专用型
  • 隐蔽型
  • 资源受限
  • 高可靠性
  • 软件固化
  • 实时性

5、mov寻址方式

6、MMU的特点

多选题
  1. IIC的启动结束电平的状态

初始化:IIC的初始化为SDA和SCL均为高。

开始信号:处理器让SCL时钟保持高电平,然后让SDA数据信号由高变低就表示一个开始信号。同时IIC总线上的设备检测到这个开始信号它就知道处理器要发送数据了。

停止信号:处理器让SCL时钟保持高电平,然后让SDA数据信号由低变高就表示一个停止信号。同时IIC总线上的设备检测到这个停止信号它就知道处理器已经结束了数据传输,我们就可以各忙各个的了,如休眠等。

问答题

1、内联函数的使用和限制

2、TCP建立连接的过程

3、CPU处理中断的过程和具体操作

题目六

非常模糊

https://blog.csdn.net/zh_666888/article/details/111060297

一、选择 10道 2*10

以Linux和C为主,还有操作系统,内存带宽等等,题目不是很难,但是一定一定要沉下心来做,争取做一个对一个。

题目七

https://blog.csdn.net/weixin_42060900/article/details/99229518

关键字volatile

表示一个变量也许会被后台程序改变,关键字 volatile 是与 const 绝对对立的。它指示一个变量也许会被某种方式修改,这种方式按照正常程序流程分析是无法预知的(例如,一个变量也许会被一个中断服务程序所修改)。这个关键字使用下列语法定义:volatile data-definition。

变量如果加了 volatile 修饰,则会从内存重新装载内容,而不是直接从寄存器拷贝内容。 volatile 的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。

volatile应用比较多的场合,在中断服务程序和cpu相关寄存器的定义。

//示例一
#include <stdio.h>
int main (void)
{
	int i = 10;
	int a = i; //优化
	int b = i;
 
	printf ("i = %d\n", b);
	return 0;
}
//示例二
#include <stdio.h>
int main (void)
{
	volatile int i = 10;
	int a = i; //未优化
	int b = i;
 
	printf ("i = %d\n", b);
	return 0;
}


使用 volatile 的代码编译未优化。volatile 指出 i 是随时可能发生变化的,每次使用它的时候必须从 i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在 b 中。而优化做法是,由于编译器发现两次从 i读数据的代码之间的代码没有对 i 进行过操作,它会自动把上次读的数据放在 b 中。而不是重新从 i 里面读。这样以来,如果 i是一个寄存器变量或者表示一个端口数据就容易出错,所以说 volatile 可以保证对特殊地址的稳定访问。
volatile 使用:1.并行设备的硬件寄存器(如:状态寄存器);2.一个中断服务子程序中会访问到的非自动变量(Non-automatic variables);3.多线程应用中被几个任务共享的变量。
关键字 inline

大多数的机器上,调用函数都要做很多工作:调用前要先保存寄存器,并在返回时恢复,复制实参,程序还必须转向一个新位置执行C++中支持内联函数,其目的是为了提高函数的执行效率,用关键字 inline 放在函数定义(注意是定义而非声明,下文继续讲到)的前面即可将函数指定为内联函数,内联函数通常就是将它在程序中的每个调用点上“内联地”展开。
关键字 inline参考链接
C语言编译过程中,volatile关键字和extern关键字分别在哪个阶段起作用

volatile应该是在编译阶段,extern在链接阶段。
volatile关键字的作用是防止变量被编译器优化,而优化是处于编译阶段,所以volatile关键字是在编译阶段起作用。

请你说一下源码到可执行文件的过程

对于C++源文件,从文本到可执行文件一般需要四个过程:
**预处理阶段:**对源代码文件中文件包含关系(头文件)、预编译语句(宏定义)进行分析和替换,生成预编译文件。
**编译阶段:**将经过预处理后的预编译文件转换成特定汇编代码,生成汇编文件
**汇编阶段:**将编译阶段生成的汇编文件转化成机器码,生成可重定位目标文件
**链接阶段:**将多个目标文件及所需要的库连接成最终的可执行目标文件
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1)预编译
主要处理源代码文件中的以“#”开头的预编译指令。处理规则见下
1、删除所有的#define,展开所有的宏定义。
2、处理所有的条件预编译指令,如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”。
3、处理“#include”预编译指令,将文件内容替换到它的位置,这个过程是递归进行的,文件中包含其他文件。
4、删除所有的注释,“//”和“/**/”。
5、保留所有的#pragma 编译器指令,编译器需要用到他们,如:#pragma once 是为了防止有文件被重复引用。
6、添加行号和文件标识,便于编译时编译器产生调试用的行号信息,和编译时产生编译错误或警告是能够显示行号。
编译
把预编译之后生成的xxx.i或xxx.ii文件,进行一系列词法分析、语法分析、语义分析及优化后,生成相应的汇编代码文件。
1、词法分析:利用类似于“有限状态机”的算法,将源代码程序输入到扫描机中,将其中的字符序列分割成一系列的记号。
2、语法分析:语法分析器对由扫描器产生的记号,进行语法分析,产生语法树。由语法分析器输出的语法树是一种以表达式为节点的树。
3、语义分析:语法分析器只是完成了对表达式语法层面的分析,语义分析器则对表达式是否有意义进行判断,其分析的语义是静态语义——在编译期能分期的语义,相对应的动态语义是在运行期才能确定的语义。
4、优化:源代码级别的一个优化过程。
5、目标代码生成:由代码生成器将中间代码转换成目标机器代码,生成一系列的代码序列——汇编语言表示。
6、目标代码优化:目标代码优化器对上述的目标机器代码进行优化:寻找合适的寻址方式、使用位移来替代乘法运算、删除多余的指令等。
3)汇编
将汇编代码转变成机器可以执行的指令(机器码文件)。 汇编器的汇编过程相对于编译器来说更简单,没有复杂的语法,也没有语义,更不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译过来,汇编过程有汇编器as完成。经汇编之后,产生目标文件(与可执行文件格式几乎一样)xxx.o(Windows下)、xxx.obj(Linux下)。
4)链接

将不同的源文件产生的目标文件进行链接,从而形成一个可以执行的程序。链接分为静态链接和动态链接:
1、静态链接:
函数和数据被编译进一个二进制文件。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件。
空间浪费:因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,会出现同一个目标文件都在内存存在多个副本;
更新困难:每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。
运行速度快:但是静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。
2、动态链接:
动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。
共享库:就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分,副本,而是这多个程序在执行时共享同一份副本;
更新方便:更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标。
性能损耗:因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损失。

sizeof
1.如果是数组

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

https://blog.csdn.net/qq_37887537/article/details/76468112

  • struct与union数据内存对齐,内存对齐的作用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

   结构体struct内存对齐的3大规则:

1.对于结构体的各个成员,第一个成员的偏移量是0,排列在后面的成员其当前偏移量必须是当前成员类型的整数倍;
2.结构体内所有数据成员各自内存对齐后,结构体本身还要进行一次内存对齐,保证整个结构体占用内存大小是结构体内最大数据成员的最小整数倍;
3.如程序中有#pragma pack(n)预编译指令,则所有成员对齐以n字节为准(即偏移量是n的整数倍),不再考虑当前类型以及最大结构体内类型。

#pragma pack(1)
 
struct fun{
	int i;
	double d;
	char c;
};

sizeof(fun) = 13

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

//64位
struct C 
{ 
	double t;   //8   1111 1111
	char b;  //1      1
	int a;   //4      0001111  
	short c;  //2     11000000
};  
 sizeof(C) = 24;  //注意:1 4 2 不能拼在一起

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

联合体union内存对齐的2大规则:

1.找到占用字节最多的成员;
2.union的字节数必须是占用字节最多的成员的字节的倍数,而且需要能够容纳其他的成员.

//x64
typedef union {
    long i;
    int k[5];
    char c;
}D

要计算union的大小,首先要找到占用字节最多的成员,本例中是long,占用8个字节,int k[5]中都是int类型,仍然是占用4个字节的,然后union的字节数必须是占用字节最多的成员的字节的倍数,而且需要能够容纳其他的成员,为了要容纳k(20个字节),就必须要保证是8的倍数的同时还要大于20个字节,所以是24个字节。
内存对齐作用:

1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

请简述linux或RTOS中,栈空间最大使用率和栈溢出检测方法。

方法一:在任务切换时检测任务指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出的钩子函数。
方法二:任务创建的时候将任务栈所有的数据初始化为0xa5,任务切换时进行任务栈检测的时候会检测末尾的16个字节是否都是0xa5,通过这种方式来检测任务栈是否溢出了。

抢占式内核中,任务调度实际有哪些

一个程序中含有以下代码块,请说明其中各个变量的生命周期,作用域和存储位置(提示:存储位置可以选泽 ‘text’ ‘data’ ‘bss’ ‘heap’ ‘stack’)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个程序本质上都是由BSS段、data段、text段(代码区)三个组成的。可以看到一个可执行程序在存储(没有调入内存)时分为代码段、数据区和未初始化数据区三部分。
BSS段(未初始化数据区):通常用来存放程序中未初始化的全局变量和静态变量的一块内存区域。BSS段属于静态分配,程序结束后静态变量资源由系统自动释放。
数据段:数据段也属于静态内存分配。该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量)。
代码段:存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域属于只读。在代码段中,也有可能包含一些只读的常数变量
text段和data段在编译时已经分配了空间,而BSS段并不占用可执行文件的大小,它是由链接器来获取内存的。
bss段(未进行初始化的数据)的内容并不存放在磁盘上的程序文件中。其原因是内核在程序开始运行前将它们设置为0。需要存放在程序文件中的只有正文段和初始化数据段。
data段(已经初始化的数据)则为数据分配空间,数据保存到目标文件中。
数据段包含经过初始化的全局变量以及它们的值。BSS段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段的后面。当这个内存进入程序的地址空间后全部清零。包含数据段和BSS段的整个区段此时通常称为数据区。

可执行程序在运行时又多出两个区域:栈区和堆区。

栈区:由编译器自动释放,存放函数的参数值、局部变量等。每当一个函数被调用时,该函数的返回类型和一些调用的信息被存放到栈中。然后这个被调用的函数再为他的自动变量和临时变量在栈上分配空间。每调用一个函数一个新的栈就会被使用。栈区是从高地址位向低地址位增长的,是一块连续的内存区域,最大容量是由系统预先定义好的,申请的栈空间超过这个界限时会提示溢出,用户能从栈中获取的空间较小。
堆区:用于动态分配内存,位于BSS和栈中间的地址区域。由程序员申请分配和释放。堆是从低地址位向高地址位增长,采用链式存储结构。频繁的malloc/free造成内存空间的不连续,产生碎片。当申请堆空间时库函数是按照一定的算法搜索可用的足够大的空间。因此堆的效率比栈要低的多。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

int a0=1;
static int a1;
const static a2=0;
extern int a3;

void fun(void)
{
	int a4;
	volatile int a5;
	return;
}

a0 :全局初始化变量;生命周期为整个程序运行期间;作用域为所有文件;存储位置为data段。
a1 :全局静态未初始化变量;生命周期为整个程序运行期间;作用域为当前文件;储存位置为BSS段。
a2 :全局静态变量
a3 :全局初始化变量;其他同a0。
a4 :局部变量;生命周期为fun函数运行期间;作用域为fun函数内部;储存位置为栈。
a5 :局部易变变量;生命周期为
C语言 内存管理:https://blog.csdn.net/u011616739/article/details/61621815

变量的类型、作用域、存储空间、生命周期:https://blog.csdn.net/weixin_42528287/article/details/85783662

static extern 局部变量 全局变量 生命周期 作用域:https://www.jianshu.com/p/ce19ec49511c

题目八(不会-1、2、)

https://blog.csdn.net/weixin_43873379/article/details/98725095

  1. 写宏定义

    1)x对a向下取整数倍的宏定义ALIGN_DOWN(x, a) 例子(65,3)->63
    (2)x对a向上取整数倍的宏定义ALIGN_UP(x, a) 例子(65,3)->66
    其实这个就是个取余的问题,向下就是减掉余数,向上加上a-余数,注意宏定义细节,括号都加上加好,防止出现输入64+1或者其他的情况,造成计算错误

    #define ALIGN_DOWN(x, a) ((x) - (x) % (a))
    #define ALIGN_UP(x, a) ((x) + a - (x) % (a))
    
    

    (3)x对a向下取整数倍的宏定义ALIGN_2N_DOWN(x, a) ,a是2的n次幂例子(65,4)->64
    (4)x对a向上取整数倍的宏定义ALIGN_2N_UP(x, a) 例子,a是2的n次幂例子(65,4)->68
    注意要用2的n次幂这个特性进行加速
    笔者做的时候知道这个是利用位运算,然而在笔试时紧张第二个写错了,现在想想真的二啊我。。。。
    首先2的n次幂有个特性,减一之后低于n的位全部变为1,举例4d,0100b,减一变为0011,取反1100,利用这个特性便可以做出第三四来(好气啊,笔试紧张,向上的我写错了!!!)

    #define ALIGN_2N_DOWN(x, a) ((x)&~(a - 1))
    #define ALIGN_2N_UP(x, a) ((x + a - 1)&~(a - 1))
    
    

    其实最后一个向上2n次幂向上取整是在linux内核中定义的,哎多看源码,多积累把~~~
    linux原始定义如下,和我们写的一样,注意(),我上面没有写。
    #define ALIGN(x,a) (((x)+(a)-1)&~(a-1))

2、第二考察特别细节,

硬件数据寄存器地址0x8000_0000
状态寄存器0x8000_0004
状态寄存器31位为1时数据有效读出来,并清除,有效返0,无效返回负数
注意volatile、const、assert、等等吧
具体见下面代码,笔试时重定位写错了,哎,注意细节!!!!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

题目九(不会)

第一题

求出数组中两个不重叠的连续子数组的最大和。

输入:

10
1 -1 2 2 3 -3 4 -4 5 -5

输出:
13
子串为:[2 2 3 -3 4]、[5]

求最大子序和的升级版,实质上就是将数组分成两个数组,分别求出最大子序和,之后再求和,求出最大值.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdbool.h>
#include <limits.h>
 
int max(int a,int b)
{
    return a>b?a:b;
}
//用两个dp数组,一个正方向,一个逆方向,并把每个下标对应的最大子序和存储在对应的maxNum数组中
int solve(int* data,int N)
{
    int dp[N];
    int Inv_dp[N];
    dp[0]=data[0];
    int maxNum[N];
    int Inv_maxNum[N];
    maxNum[0]=data[0];
    //dp[i]:以第 i 个数结尾的「连续子数组的最大和」
    //maxNum[i]:前i个数的最大子序和
    for(int i=1;i<N;i++)
    {
        dp[i]=max(dp[i-1]+data[i],data[i]);
        maxNum[i]=max(dp[i],maxNum[i-1]);
    }
    //Inv_dp[i]:逆序,倒着看,以第i个数为结尾的最大和,
    //Inv_maxNum[i]:逆序的最大子序和
    Inv_dp[N-1]=data[N-1];
    Inv_maxNum[N-1]=data[N-1];
    for(int i=N-2;i>=0;i--)
    {
        Inv_dp[i]=max(Inv_dp[i+1]+data[i],data[i]);
        Inv_maxNum[i]=max(Inv_dp[i],Inv_maxNum[i+1]);
 
    }
    int res=INT_MIN;
    //求出最大值
    for(int i=1;i<=N-1;i++)
    {
        int left=maxNum[i-1];
        int right=Inv_maxNum[i];
        res=max(res,left+right);
    }
    return res;
}
 
int main()
{
    int data[10]={-1 ,-1 ,-1 ,-1 ,-1, -1, -1, -1 ,-1 ,-1};
    //int data[5]={-5, 9 ,-5, 11 ,20};
    printf("%d\n",solve(data,10));
    return 0;
 
}

第二题

现有一个收费服务方案:在时间1<=t<6内,每单位时间收费10元;6<=t<10内,收费5元,10<=t,收2元。同一时间只能为一位服务,
有一批用户要该服务,选出若干客户服务并使收费最大。记得不是很清楚,

样例:

1 1 4, 2 2 6, 3 3 12, 4 6 10

输出:

60, 2 4

逗号之后有空格,每三个数表示,id号,起始时间和终止时间,可以求出所需要的金额。

1 1 4

id,起始时间,停止时间。

输出是最大金额,之后是被服务的用户序列

思路:

首先对数据进行处理,求出每个用户收费的时间,之后构造图,求出入度为0到出度为0的每条路径,之后求出路径收费的最大值。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdbool.h>
#include <ctype.h>
 
struct Node
{
    int id;
    int start;
    int end;
    int money;
};
 
int compute(int start, int end)
{
    if (start < 6)
    {
        if (end < 6)
        {
            return (end - start) * 10;
        }
        else if (end < 10)
        {
            return (6 - start) * 10 + (end - 6) * 5;
        }
        else
        {
            return (end - 10) * 2 + 20 + (6 - start) * 10;
        }
    }
    else if (start < 10)
    {
        if (end < 10)
        {
            return (end - start) * 5;
        }
        else
        {
            return (end - 10) * 2 + (10 - start) * 5;
        }
    }
    else
    {
        return (end - start) * 2;
    }
}
 
int res = 0;
//存储所有路径
int road[1000][100];
int myindex;
//用栈存储路径
int stack[100];
int top = -1;
//邻接矩阵图
int G[1000][1000];
//入度数组和出度数组
int indegree[1000];
int outdegree[1000];
int num;
struct Node user[1000];
void dfs(int start)
{
    if(outdegree[start]==0)
    {
        //路径数组中,第0个元素,为收费总和,第1个元素为栈内元素的个数
        road[myindex][1]=top+1;
        int sum=0;
        for(int i=0,j=2;i<=top;i++,j++)
        {
            sum+=user[stack[i]].money;
            road[myindex][j]=stack[i];
        }
        road[myindex][0]=sum;
        myindex++;
        return;
    }
    for(int i=1;i<num;i++)
    {
        if(G[start][i]==1)
        {
            stack[++top]=i;
            dfs(i);
            top--;
        }
    }
}
 
int main()
{
    char data[1000];
    gets(data);
     num= 1; //id从1开始
    int pos = 0;
    int len = strlen(data);
    while (pos < len)
    {
        int tmp = 0;
        while (isdigit(data[pos]))
        {
            tmp = 10 * tmp + data[pos] - '0';
            pos++;
        }
        user[num].id = tmp;
        pos++;
        tmp = 0;
        while (isdigit(data[pos]))
        {
            tmp = 10 * tmp + data[pos] - '0';
            pos++;
        }
        user[num].start = tmp;
        pos++;
        tmp = 0;
        while (isdigit(data[pos]))
        {
            tmp = 10 * tmp + data[pos] - '0';
            pos++;
        }
        user[num].end = tmp;
        pos++;
        pos++;
        user[num].money = compute(user[num].start, user[num].end);
        num++;
    }
    //一共有num-1个节点
    memset(G, 0, sizeof(G));
    memset(indegree, 0, sizeof(indegree));
    memset(outdegree, 0, sizeof(outdegree));
    for (int i = 1; i < num; i++)
    {
        for (int j = 1; j < num; j++)
        {
            if (user[j].start >= user[i].end)
            {
                G[i][j] = 1;
                indegree[j]++;
                outdegree[i]++;
            }
        }
    }
 
    myindex=0;
    for (int i = 1; i < num; i++)
    {
        if (indegree[i] == 0)
        {
            stack[++top] = i;
            dfs(i);
            top--;
        }
    }
    int max=0;
    for(int i=0;i<myindex;i++)
    {
        if(road[i][0]>max)
        {
            max=road[i][0];
            res=i;
        }
    }
    printf("%d,",road[res][0]);
    for(int i=0;i<road[res][1];i++)
    {
        printf(" %d",road[res][i+2]);
    }
    return 0;
}

题目十

https://blog.csdn.net/qq_38410730/article/details/80895986

(2014大疆嵌入式笔试题试题)

有如下CAT_s机构体定义,回答:

1)在一台64位的机器上,使用32位编译,Garfield变量占用多少内存空间?64位编译又是如何?(总分5分)

2)使用32位编译情况下,给出一种判断所使用机器大小端的方法。(总分5分)

struct CAT_s
{
    int ld;
    char Color;
    unsigned short Age;
    char *Name;
    void(*Jump)(void);
}Garfield;

我回答

1.外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 还没记住
  • 所谓的大端模式(BE big-endian),是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中(低对高,高对低);

  • 所谓的小端模式(LE little-endian),是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中(低对低,高对高)。

    为什么要有大小端区分呢?

    这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,刚好相反。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。
    首先给出一个方法(Linux操作系统的源码中的判断):

    static union { 
        char c[4]; 
        unsigned long mylong; 
    } endian_test = {{ 'l', '?', '?', 'b' } };
    #define ENDIANNESS ((char)endian_test.mylong)
    

    https://bbs.csdn.net/topics/390898826

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    这是利用:联合体union的存放顺序是所有成员都从低地址开始存放的特性。由于联合体的各个成员共用内存,并应该同时只能有一个成员得到这块内存的使用权(即对内存的读写)。如果是“l”(小端)、“b”(大端)。

    除了这种方法之外,也可以利用数据类型转换的截断特性:

    void Judge_duan()
    {
    	int a = 1;  //定义为1是为了方便 如果你喜欢你可以随意,
    	            //只要你能搞清楚 例如:0x11223344;
     
    	char *p = (char *)&a;//在这里将整形a的地址转化为char*;
    	                     //方便后面取一个字节内容
     
    	if(*p == 1)//在这里解引用了p的一个字节的内容与1进行比较;
    		printf("小端\n");
    	else
    		printf("大端\n");
     
    }
    

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

参考:关于大小端的判定:https://blog.csdn.net/bitboss/article/details/51247869

关于截断、整形提升;https://blog.csdn.net/weixin_50886514/article/details/111478367

顺便提一下,大小端之间的转化,以小端转化为大端为例:

int endian_convert(int t)    
{    
       int result=0;    
       int i;     
    
       for (i = 0; i < sizeof(t); i++) {    
              result <<= 8;    
              result |= (t & 0xFF);    
              t >>= 8;    
       }    
       return result;    
}

2、描述下面XXX这个宏的作用。(总分10分)

#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
#define XXX(ptr,type,member)({\
    const typeof(((type*)0)->member) *__mptr=(ptr);\
    (type*)((char*)__mptr – offsetof(type,member));})

3、简述C函数:

1)参数如何传递(__cdecl调用方式);

2)返回值如何传递;

3)调用后如何返回到调用前的下一条指令执行。(总分10分)

4、在一个多任务嵌入式系统中,有一个CPU可直接寻址的32位寄存器REGn,地址为0x1F000010,编写一个安全的函数,将寄存器REGn的指定位反转(要求保持其他bit的值不变)。(总分10分)

5、有10000个正整数,每个数的取值范围均在1到1000之间,编程找出从小到大排在第3400(从0开始算起)的那个数,将此数的值返回,要求不使用排序实现。(总分10分)

嵌入式基本知识

1、简述处理器中断处理的过程(中断向量、中断保护现场、中断嵌套、中断返回等)。(总分10分)

2、简述处理器在读内存的过程中,CPU核、cache、MMU如何协同工作?画出CPU核、cache、MMU、内存之间的关系示意图加以说明(可以以你熟悉的处理器为例)。(总分10分)

基本通信知识

1、请说明总线接口USRT、I2C、USB的异同点(串/并、速度、全/半双工、总线拓扑等)。(总分5分)

2、列举你所知道的linux内核态和用户态之间的通信方式并给出你认为效率最高的方式,说明理由。(总分5分)

系统设计

有一个使用UART进行通信的子系统X,其中UART0进行数据包接收和回复,UART1进行数据包转发。子系统X的通信模块职责是从UART0接收数据包,如果为本地数据包(receiver 为子系统X),则解析数据包中的命令码(2字节)和数据域(0~128字节),根据命令码调用内部的处理程序,并将处理结果通过UART0回复给发送端,如果非本地数据包,则通过UART1转发。如果由你来设计子系统X的通信模块:

1)请设计通信数据包格式,并说明各字段的定义;(总分5分)

2)在一个实时操作系统中,你会如何部署模块中的任务和缓存数据,画出任务间的数据流视图加以说明;(总分5分)

3)你会如何设置任务的优先级,说说优缺点;(总分5分)

4)如果将命令码对应的处理优先级分为高、低两个等级,你又会如何设计;(总分5分)

C语言

keil 51 code 什么意思:改变了存储位置

https://zhidao.baidu.com/question/106734427.html

image-20220922233817580

那么其他有关键词改变其他变量位置吗

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好家伙VCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值