嵌入式培训
达内IT学院
400-996-5531
今天要跟大家分享的是嵌入式面试题二十道,如果你想在2019春节之后找到一份嵌入式相关的工作,那么就从这二十道嵌入式面试题开始准备吧!都有答案有解析的哦!
1. C语言中,修饰符volatile含义是什么?其应用场合有哪些?
答:volatile关键字的作用
volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。下面举例说明。在DSP开发中,经常需要等待某个事件的触发,所以经常会写出这样的程序:
short flag; void test() { do1(); while(flag==0); do2(); }
这段程序等待内存变量flag的值变为1(怀疑此处是0,有点疑问,)之后才运行do2()。变量flag的值由别的程序更改,这个程序可能是某个硬件中断服务程序。例如:如果某个按钮按下的话,就会对DSP产生中断,在按键中断程序中修改flag为1,这样上面的程序就能够得以继续运行。但是,编译器并不知道flag的值会被别的程序修改,因此在它进行优化的时候,可能会把flag的值先读入某个寄存器,然后等待那个寄存器变为1。如果不幸进行了这样的优化,那么while循环就变成了死循环,因为寄存器的内容不可能被中断服务程序修改。为了让程序每次都读取真正flag变量的值,就需要定义为如下形式:
volatile short flag;
需要注意的是,没有volatile也可能能正常运行,但是可能修改了编译器的优化级别之后就又不能正常运行了。因此经常会出现debug版本正常,但是release版本却不能正常的问题。所以为了安全起见,只要是等待别的程序修改某个变量的话,就加上volatile关键字。
2. 请问TCP/IP协议分为哪几层?FTP协议在哪一层?
答:
TCP/IP协议并不完全符合OSI的七层参考模型。传统的开放式系统互连参考模型,是一种通信协议的7层抽象的参考模型,其中每一层执行某一特定任务。该模型的目的是使各种硬件在相同的层次上相互通信。这7层是:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。而TCP/IP通讯协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。这4层分别为:
应用层:应用程序间沟通的层,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。
传输层:在此层中,它提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。
互连网络层:负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)。
网络接口层:对实际的网络媒体的管理,定义如何使用实际网络(如Ethernet、SerialLine等)来传送数据。
3. 在网络应用中,函数htons,htonl,ntohs,ntohl的作用是什么?
答:
htons
htons函数用来转换u_short来自主机的TCP / IP网络字节顺序(即big-endian )的. u_short htons ( u_short hostshort ) ; 参数hostshort [ ] 16位元数的主机字节顺序. 返回值的htons函数返回值的TCP /IP网络字节顺序. 须知htons函数有一个16位号码主机字节顺序并返回一个16位数字网络字节命令中使用的TCP /IP网络.
htonl
将主机的无符号长整形数转换成网络字节顺序。
#include <winsock.h> u_long PASCAL FAR htonl( u_long hostlong);
hostlong:主机字节顺序表达的32位数。
注释:
本函数将一个32位数从主机字节顺序转换成网络字节顺序。
返回值:htonl()返回一个网络字节顺序的值。
参见:htons(), ntohl(), ntohs().
ntohs
将一个无符号短整形数从网络字节顺序转换为主机字节顺序。
#include<winsock.h> u_short PASCAL FAR ntohs(u_short netshort);
netshort:一个以网络字节顺序表达的16位数。
注释:
本函数将一个16位数由网络字节顺序转换为主机字节顺序。
返回值:ntohs()返回一个以主机字节顺序表达的数。
参见:htonl(), htons(),ntohl().
ntohl
将一个无符号长整形数从网络字节顺序转换为主机字节顺序。
#include <winsock.h> u_long PASCAL FAR ntohl(u_long netlong);
netlong:一个以网络字节顺序表达的32位数。
注释:
本函数将一个32位数由网络字节顺序转换为主机字节顺序。
返回值:ntohl()返回一个以主机字节顺序表达的数。
参见:htonl(), htons(),ntohs().
4. C语言中static函数与普通函数的区别是什么?
答:
1.static有什么用途?(请至少说明两种)
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所有函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用
static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?
全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用; static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
5. 请实现内存复制函数voidmemcpy(void *dst const void *src,int size)
原型:extern void*memcpy(void *dest, void *src, unsigned int count);
用法:#include<string.h>
功能:由src所指内存区域复制count个字节到dest所指内存区域。
说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。
举例:
// memcpy.c #include <syslib.h> #include <string.h> int main(int argc, char* argv[]) { char *s="Golden Global View"; char d[20]; clrscr(); memcpy(d,s,strlen(s)); d[strlen(s)]='\0'; printf("%s",d); getchar(); return 0; }
截取view
#include <string.h> int main(int argc, char* argv[]) { char *s="Golden Global View"; char d[20]; memcpy(d,s+14,4); //memcpy(d,s+14*sizeof(char),4*sizeof(char));也可 d[5]='\0'; printf("%s",d); getchar(); return 0; }
输出结果:
View
初始化数组
char msg[10]; memcpy(msg,0,sizeof(memcpy));
方法2:
写一个内存拷贝函数
// 考虑重叠的状况 void* _memcpy(void* dest, void* src, int len) { if(!dest || !src || !len || dest == src) return dest; char* pdest = static_cast<char*>(dest); char* psrc = static_cast<char*>(src); // dest 在 src + len 范围内 if(pdest > psrc && pdest < (psrc + len)) { // 先备份被覆盖部分 int need = psrc + len - pdest; int offset = pdest - psrc; char* pcache = new char[need]; int i = 0; for (i = 0; i < need; ++i) pcache[i] = psrc[offset + i]; // 拷贝起始部分 for (i = 0; i < offset; ++i) pdest[i] = psrc[i]; // 拷贝剩余部分 for (i = 0; i < need; ++i) pdest[offset + i] = pcache[i]; delete[] pcache; } else { for (int i = 0; i < len; ++i) pdest[i] = psrc[i]; } return dest; }
6.32位机器上,假设有一个32位数字0x1234abcd保存在0x00000000开始的内存中,那么在little endian和big endian的机器上,按字节该整数在内存中存放的顺序是怎么样的?
答:大端模式:数据的高字节存储在内存地址的低字节,(正常存储)小端模式:数据的高字节存储在内存地址的高字节.
littleendian:0x00000000-0x000000003h:cd,ab,34,12 big endian: 0x00000000-0x000000003h: 12,34,ab,cd
7.ISO七层模型是什么,tcp/udp属于哪一层?
答:物理层-数据链路层-网络层-传输层-会话层-表示层-应用层,tcp/udp工作在传输层。
8.下面是一个中断服务程序的代码,请指出有那些问题?
_interrupt double compute_area(double radius) { double area=PI*radius*radius; return area; }
答:中断子程序不能有返回值,去掉return area; compute_area之前的double关键字。这个函数有太多的错误了,以至让人不知从何说起了:
1)ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。
2) ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。
3) 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。
4) 与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。
8.多任务系统中,常见的任务通讯机制有哪些?
答:操作系统还提供进程间的通讯机制来帮助完成这样的任务。Linux中常见的进程间通讯机制有:信号、管道、共享内存、信号量和套接字等。
9.请实现内存复制函数memcpy(void *dst,const void*src,int size).
答:
举例:
// memcpy.c #include <syslib.h> #include <string.h> int main(int argc, char* argv[]) { char *s="Golden Global View"; char d[20]; clrscr(); memcpy(d,s,strlen(s)); d[strlen(s)]='\0'; printf("%s",d); getchar(); return 0; }
截取view
#include <string.h> int main(int argc, char* argv[]) { char *s="Golden Global View"; char d[20]; memcpy(d,s+14,4); //memcpy(d,s+14*sizeof(char),4*sizeof(char));也可 d[5]='\0'; printf("%s",d); getchar(); return 0; }
输出结果:
View
初始化数组
char msg[10]; memcpy(msg,0,sizeof(memcpy));
10.请列举主流linux的发布版本(四个以上)。
答:
(1)Redhat有两大Linux产品系列,其一是免费的Fedora Core系列主要用于桌面版本,提供了较多新特性的支持。另外一个产品系列是收费的Enterprise系列,这个系列分成:AS/ES/WS等分支。
(2)Advanced Server,缩写即AS。AS在标准Linux内核的基础上,做了性能上的增强,并提高了可靠性,集成了众多常见服务器的驱动程序。可轻松识别IBM/DELL/HP等常见机架式服务器的磁盘阵列卡等设备。
11.当前linux最主流的两大桌面环境是什么,两者区别是什么?
答:KDE与GNOME是目前Linux/UNIX系统最流行的图形操作环境
12.linux系统下主要三类设备文件类型是什么?
答:块设备、字符设备、网络设备。
Linux 中的设备有2种类型:字符设备(无缓冲且只能顺序存取)、块设备(有缓冲且可以随机存取)。每个字符设备和块设备都必须有主、次设备号,主设备号相同的设备是同类设备(使用同一个驱动程序)。这些设备中,有些设备是对实际存在的物理硬件的抽象,而有些设备则是内核自身提供的功能(不依赖于特定的物理硬件,又称为"虚拟设备")。
13.以太网的MTU是多大?
答:通常意义上的以太网MTU是指没有以太网header和FCS的以太网payload部分,IEEE802规定了大小为0~1500字节;所以,二层以太网帧长应该为这个长度加上18B(6B的DA、6B的SA和2B的Length/Etype以及4B的FCS),这样大小应该<1518;在Dot1Q情况下,应该在加上4B的802.1Q的Tag,即应该<1522B;在MPLS VPN环境中,IGP标签和VPN标签各是4B,所以,作为中间环节的交换机如果不支持Jumbo帧的话,就需要手工配置MTU=1500+N*4(N为MPLS Tag层数)。
14. ARP协议作用
答:IP数据包常通过以太网发送。以太网设备并不识别32位IP地址:它们是以48位以太网地址传输以太网数据包的。因此,IP驱动器必须把IP目的地址转换成以太网网目的地址。在这两种地址之间存在着某种静态的或算法的映射,常常需要查看一张表。地址解析协议(Address Resolution Protocol,ARP)就是用来确定这些映象的协议。
ARP工作时,送出一个含有所希望的IP地址的以太网广播数据包。目的地主机,或另一个代表该主机的系统,以一个含有IP和以太网地址对的数据包作为应答。发送者将这个地址对高速缓存起来,以节约不必要的ARP通信。
如果有一个不被信任的节点对本地网络具有写访问许可权,那么也会有某种风险。这样一台机器可以发布虚假的ARP报文并将所有通信都转向它自己,然后它就可以扮演某些机器,或者顺便对数据流进行简单的修改。ARP机制常常是自动起作用的。在特别安全的网络上, ARP映射可以用固件,并且具有自动抑制协议达到防止干扰的目的。
15. 编写一个简单的ECHO服务器
答:如下代码是一个简单的echo服务器,它示例的一些Winsock函数的用法。
#include <iostream> using namespace std; #ifdef WIN32 # include <WinSock2.h> /// 也可在setting/liker/input/Additional dependencies加入导入库引用 # pragma comment(lib, "ws2_32.lib") /// 包含AcceptEx,TransmitFile,DisconnectEx等函数 # include <mswsock.h> # pragma comment(lib,"Mswsock.lib") # define PRINT_SOCKET_ERROR(FUNC) do { cout << "<" << #FUNC << "> Call Error!" << "" << "Error Number : " << WSAGetLastError() << endl; } while(0) #endif // WIN32 #define PORT 5432 int main(int argc, char* argv[]) { // = 初始化Lib库 int nRet; WSAData wsaData; if ((nRet = WSAStartup(0x0202, &wsaData)) != 0) { PRINT_SOCKET_ERROR(WSAStartup); return -1; } // = 设置监听socket句柄 SOCKET hListen; /// #1 if ((hListen = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { PRINT_SOCKET_ERROR(socket); return -1; } /// #2 SOCKADDR_IN hostAddr; { u_long nIp = htonl(INADDR_ANY); { /// 相关函数示例 char szHostName[256] = {0}; gethostname(szHostName, 256); hostent* thisHost = gethostbyname(szHostName); Ip = inet_addr(inet_ntoa(*((in_addr*)thisHost->h_addr_list[0]))); } hostAddr.sin_family = AF_INET; hostAddr.sin_port = htons(PORT); hostAddr.sin_addr.s_addr = nIp; } if ((nRet = bind(hListen, (PSOCKADDR)&hostAddr, sizeof(SOCKADDR_IN))) != 0) { PRINT_SOCKET_ERROR(bind); return -1; } /// #3 if ((nRet = listen(hListen, 5)) != 0) { PRINT_SOCKET_ERROR(listen); return -1; } else { cout << "Listening at " << inet_ntoa(hostAddr.sin_addr) << ":" << ntohs(hostAddr.sin_port) << endl; } // = 处理请求 SOCKET hConnected; SOCKADDR_IN remoteAddr; int remote_addr_size = sizeof(SOCKADDR_IN); if ((hConnected = accept(hListen, (PSOCKADDR)&remoteAddr, &remote_addr_size)) == INVALID_SOCKET) { PRINT_SOCKET_ERROR(accept); return -1; } else { cout << "Connect from " << inet_ntoa(remoteAddr.sin_addr) << ":" << ntohs(remoteAddr.sin_port) << endl; } // = 数据收发 do { char buffer[1024] = {0}; int recv_bytes = recv(hConnected, buffer, 1024, 0); if (recv_bytes == -1) { PRINT_SOCKET_ERROR(recv); break; } else if (recv_bytes == 0) { /// 客户端关闭了链接 cout << "Connection closed from " << inet_ntoa(remoteAddr.sin_addr) << ":" << ntohs(remoteAddr.sin_port) << endl; closesocket(hConnected); break; } else { buffer[recv_bytes] = 0; cout << buffer; cout.flush(); int send_bytes = send(hConnected, buffer, recv_bytes, 0); if (send_bytes != recv_bytes) { cout << "Not send out all data!" << endl; } } } while (1); closesocket(hListen); // = 清理Lib库 if (WSACleanup() != 0) { PRINT_SOCKET_ERROR(WSACleanup); return -1; } return 0; };
16. 什么是DSP?请简述DSP与通用CPU的差别。
答:以微处理器解度看,两者似乎没什么差别. 但二者所专注的邻域却完全不一样. 通用的CPU专注的事务处理,对实时性的支持相对DSP差了很多.虽然一些通用CPU也在加入一些高性能的运算支持,但它和DSP相比还是天差地远.
比如现在的通用DSP可以在几个us内完成1024点的复数FFT,通用处理器是咋都达不到这水平(现在的情况),虽然其系统时钟有可能比DSP高出一个数量级.但一个周期完成多个复杂操作和多个周期完成一个操作的差别是很大的.
DSP完成的都是算法密集性的事务,可能工作比较单一而简单,但对实时要求很高很高.必需要某个确定的时间内(有可能就那么几us)完成所需要的所有操作.
通用CPU对事务管理很突出, 在这方面的能力比DSP支持得要好得多.
所以大多数的高性能系统会是DSP和通用CPU的结合.充分利用各自的优点.
由于DSP特注重高性能,其结构在同等条件比通用CPU要复杂得多,设计上也更困难.因此其价格相比通用CPU要贵那么一些(只是通用DSP和通用CPU 相比),一些很单一功能的DSP可是很便宜的,比通用CPU还便宜. 通用DSP因为要考虑通用,所以结构和专用的也是不一样的.从表面上来看,DSP与标准微处理器有许多共同的地方:一个以ALU为核心的处理器、地址和数据总线、RAM、ROM以及I/O端口,从广义上讲,DSP、微处理器和微控制器(单片机)等都属于处理器,可以说DSP是一种CPU。但DSP和一般的CPU又不同:
首先是体系结构:CPU是冯.诺伊曼结构的,而DSP有分开的代码和数据总线即“哈佛结构”,这样在同一个时钟周期内可以进行多次存储器访问——这是因为 数据总线也往往有好几组。有了这种体系结构,DSP就可以在单个时钟周期内取出一条指令和一个或者两个(或者更多)的操作数。
标准化和通用性:CPU的标准化和通用性做得很好,支持操作系统,所以以CPU为核心的系统方便人机交互以及和标准接口设备通信,非常方便而且不需要硬件 开发了;但这也使得CPU外设接口电路比较复杂,DSP主要还是用来开发嵌入式的信号处理系统了,不强调人机交互,一般不需要很多通信接口,因此结构也较为简单,便于开发。如果只是着眼于嵌入式应用的话,嵌入式CPU和DSP的区别应该只在于一个偏重控制一个偏重运算了。
流水线结构:大多数DSP都拥有流水结构,即每条指令都由片内多个功能单元分别完成取指、译码、取数、执行等步骤,这样可以大大提高系统的执行效率。但流水线的采用也增加了软件设计的难度,要求设计者在程序设计中考虑流水的需要。
快速乘法器:信号处理算法往往大量用到乘加(multiply-accumulate,MAC)运算。DSP有专用的硬件乘法器,它可以在一个时钟周期内 完成MAC运算。硬件乘法器占用了DSP芯片面积的很大一部分。(与之相反,通用CPU采用一种较慢的、迭代的乘法技术,它可以在多个时钟周期内完成一次 乘法运算,但是占用了较少了硅片资源)。
地址发生器:DSP有专用的硬件地址发生单元,这样它可以支持许多信号处理算法所要求的特定数据地址模式。这包括前(后)增(减)、环状数据缓冲的模地址以及FFT的比特倒置地址。地址发生器单元与主ALU和乘法器并行工作,这就进一步增加了DSP可以在一个时钟周期内可以完成的工作量。
硬件辅助循环:信号处理算法常常需要执行紧密的指令循环。对硬件辅助循环的支持,可以让DSP高效的循环执行代码块而无需让流水线停转或者让软件来测试循环终止条件。
低功耗:DSP的功耗较小,通常在0.5W到4W,采用低功耗的DSP甚至只有0.05W,可用电池供电,很适合嵌入式系统;而CPU的功耗通常在20W以上。
17. 静态局部变量与普通局部变量的区别是什么?
答:全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。
static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件
static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。程序的局部变量存在于(堆栈)中,全局变量存在于(静态区 )中,动态申请数据存在于( 堆)中。
18.请说明DSP中定点、浮点的概念
答:定点数指小数点在数中的位置是固定不变的,通常有定点整数和定点小数。在对小数点位置作出选择之后,运算中的所有数均应统一为定点整数或定点小数,在运算中不再考虑小数问题。
(1)定义:数据中小数点位置固定不变的数 (2)种类:定点整数(3)小数点在符号位与有效位之间。
注:定点数受字长的限制,超出范围会有溢出。
19.定点数与浮点数区别
答:定点表示法运算直观,但数的表示范围较小,不同的数运算时要考虑比例因子的选取,以防止溢出。浮点表示法运算时可以不考虑溢出,但浮点运算,编程较难。要掌握定、浮点数的转换方法及浮点数规格化方法。
20.什么是cache?cache有哪些操作?
答:cache n. 高速缓冲存储器 一种特殊的存储器子系统,其中复制了频繁使用的数据以利于快速访问。存储器的高速缓冲存储器存储了频繁访问的 RAM 位置的内容及这些数据项的存储地址。当处理器引用存储器中的某地址时,高速缓冲存储器便检查是否存有该地址。如果存有该地址,则将数据返回处理器;如果没有保存该地址,则进行常规的存储器访问。因为高速缓冲存储器总是比主RAM 存储器速度快,所以当 RAM 的访问速度低于微处理器的速度时,常使用高速缓冲存储器。
感谢您的阅读,以上就是达内嵌入式为大家分享的二十道嵌入式面试题,你都看懂了吗?千万不要死记硬背哦,要在理解中记忆,这样才不会忘记,也才不会有记住了但不会使用的尴尬!好了,更多嵌入式培训相关的内容尽在达内嵌入式培训,敬请关注!
免责声明:内容和图片源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。
填写下面表单即可预约申请免费试听!怕钱不够?可就业挣钱后再付学费! 怕学不会?助教全程陪读,随时解惑!担心就业?一地学习,可全国推荐就业!
Copyright © 京ICP备08000853号-56 京公网安备 11010802029508号 达内时代科技集团有限公司 版权所有
Tedu.cn All Rights Reserved