题目五
选择题目
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的特点
多选题
- 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)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;
我回答
- 还没记住
-
所谓的大端模式(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
那么其他有关键词改变其他变量位置吗