zoukankan      html  css  js  c++  java
  • 嵌入式经典面试题

    1. 关键字volatile有什么含意 并给出三个不同的例子。
     
    一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
    1). 并行设备的硬件寄存器(如:状态寄存器)
    2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
    3). 多线程应用中被几个任务共享的变量
     
     
    2, 一个指针可以是volatile 吗?解释为什么。
    2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
     
    9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。
    #define BIT3 (0x1<<3)
    static int a;
    void set_bit3(void)
    {
    a |= BIT3;
    }
    void clear_bit3(void)
    {
    a &= ~BIT3;
    }
    一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作
     
     
    11. 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。
     
    __interrupt double compute_area (double radius)
    {
    double area = PI * radius * radius;
    printf(" Area = %f", area);
    return area;
    这个函数有太多的错误了,以至让人不知从何说起了:
    1). ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。
    2). ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。
    3). 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的
    4). 与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。
     
    13. 评价下面的代码片断:
     
    unsigned int zero = 0;
    unsigned int compzero = 0xFFFF;
    /*1's complement of zero */
     
    对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:
     
    unsigned int compzero = ~0;
     
    这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。
    到了这个阶段,应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好,那么这个测试就在这里结束了。但如果显然应试者做得不错,那么我就扔出下面的追加问题,这些问题是比较难的,我想仅仅非常优秀的应试者能做得不错。提出这些问题,我希望更多看到应试者应付问题的方法,而不是答案。不管如何,你就当是这个娱乐吧…
    }
     
     
     
    1、线形表a、b为两个有序升序的线形表,编写一程序,使两个有序线形表合并成一个有序升序线形表h;
     
     
    答案在 请化大学 严锐敏《数据结构第二版》第二章例题,数据结构当中,这个叫做:两路归并排序
    Linklist *unio(Linklist *p,Linklist *q){
    linklist *R,*pa,*qa,*ra;
    pa=p;
    qa=q;
    R=ra=p;
    while(pa->next!=NULL&&qa->next!=NULL){
    if(pa->data>qa->data){
    ra->next=qa;
    qa=qa->next;
    }
    else{
    ra->next=pa;
    pa=pa->next;
    }
    }
    if(pa->next!=NULL)
    ra->next=pa;
    if(qa->next!=NULL)
    ra->next==qa;
    return R;
    }
     
     
    3、用递归算法判断数组a[N]是否为一个递增数组。
    递归的方法,记录当前最大的,并且判断当前的是否比这个还大,大则继续,否则返回false结束:
    bool fun( int a[], int n )
    {
    if( n= =1 )
    return true;
    if( n= =2 )
    return a[n-1] >= a[n-2];
    return fun( a,n-1) && ( a[n-1] >= a[n-2] );
    }
     
     
    2.单连表的建立,把'a'--'z'26个字母插入到连表中,并且倒叙,还要打印!
    方法1:
    typedef struct val
    {    int date_1;
         struct val *next;
    }*p;
     
    void main(void)
    {    char c;
        
         for(c=122;c>=97;c--)
            { p.date=c;
              p=p->next;
             }
     
         p.next=NULL;
    }
    }
    方法2:
    node *p = NULL;
    node *q = NULL;
     
    node *head = (node*)malloc(sizeof(node));
    head->data = ' ';head->next=NULL;
     
    node *first = (node*)malloc(sizeof(node));
    first->data = 'a';first->next=NULL;head->next = first;
    p = first;
     
    int longth = 'z' - 'b';
    int i=0;
    while ( i<=longth )
    {
    node *temp = (node*)malloc(sizeof(node));
    temp->data = 'b'+i;temp->next=NULL;q=temp;
     
    head->next = temp; temp->next=p;p=q;
    i++;
    }
     
    print(head);
     
     
    一个递规反向输出字符串的例子,可谓是反序的经典例程.
     
    void inverse(char *p)
    {
         if( *p = = '' )
    return;
         inverse( p+1 );
         printf( "%c", *p );
    }
     
    int main(int argc, char *argv[])
    {
         inverse("abc");
     
         return 0;
    }
    2。运行的结果为什么等于15
     
    #include "stdio.h"
    #include "string.h"
     
    void main()
    {
     
    char aa[10];
    printf("%d",strlen(aa));
    }
     
    答案:sizeof()和初不初始化,没有关系;strlen()和初始化有关。
     
     
    6。int a,b,c 请写函数实现C=a+b ,不可以改变数据类型,如将c改为long int,关键是如何处理溢出问题
    答案:bool add (int a, int b,int *c)
    {
    *c=a+b;
    return (a>0 && b>0 &&(*c<a || *c<b) || (a<0 && b<0 &&(*c>a || *c>b)));
    }
     
     
    7。分析:
    struct bit
    {    int a:3;
         int b:2;
         int c:3;
    };
    int main()
    {
       bit s;
       char *c=(char*)&s;
        cout<<sizeof(bit)<<endl;
       *c=0x99;
        cout << s.a <<endl <<s.b<<endl<<s.c<<endl;
          int a=-1;
        printf("%x",a);
       return 0;
    }
    输出为什么是?
     
    答案:4
    1
    -1
    -4
    ffffffff
    因为0x99在内存中表示为 100 11 001 , a = 001, b = 11, c = 100(在vc环境中,一般是由右到左进行分配的)
    当c为有符合数时, c = 100, 最高1为表示c为负数,负数在计算机用补码表示,所以c = -4;同理
    b = -1;
    当c为有符合数时, c = 100,即 c = 4,同理 b = 3
     
     
    9。下面这个程序执行后会有什么错误或者效果:
    #define MAX 255
    int main()
    {
        unsigned char A[MAX],i; //i被定义为unsigned char
        for (i=0;i<=MAX;i++)
           A[i]=i;
    }
     
     
    答案:死循环加数组越界访问(C/C++不进行数组越界检查)
    MAX=255
    数组A的下标范围为:0..MAX-1,这是其一..
    其二.当i循环到255时,循环内执行:
       A[255]=255;
    这句本身没有问题..但是返回for (i=0;i<=MAX;i++)语句时,
    由于unsigned char的取值范围在(0..255),i++以后i又为0了..无限循环下去.
     
     
    2.对于一个频繁使用的短小函数,C语言中应用什么实现,C++中应用什么实现?
    c用宏定义,c++inline
     
    6.软件测试都有那些种类?
    黑盒:针对系统功能的测试    白合:测试函数功能,各函数接口
     
    1. 进程和线程的差别。
    线程是指进程内的一个执行单元,也是进程内的可调度实体.
    与进程的区别:
    (1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
    (2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
    (3)拥有资源:进程是拥有资源的一个独-立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
    (4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
     
    11.进程死锁的原因
    资源竞争及进程推进顺序非法
    12.死锁的4个必要条件
    互斥、请求保持、不可剥夺、环路
    13.死锁的处理
    鸵鸟策略、预防策略、避免策略、检测与解除死锁
     
     
    9.纯虚函数如何定义?使用时应注意什么?
    virtual void f()=0;
    是接口,子类必须要实现
     
     
    2:int main()
       {
        int x=3;
        printf("%d",x);
        return 1;
      
       }
    问函数既然不会被其它函数调用,为什么要返回1?
    mian中,c标准认为0表示成功,非0表示错误。具体的值是某中具体出错信息
    1. 要对绝对地址0x100000赋值,我们可以用
    (unsigned int*)0x100000 = 1234;
    那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?
    *((void (*)( ))0x100000 ) ( );
    首先要将0x100000强制转换成函数指针,:
    (void (*)())0x100000
    然后再调用它:
    *((void (*)())0x100000)();
    typedef可以看得更直观些:
    typedef void(*)() voidFuncPtr;
    *((voidFuncPtr)0x100000)();
     
     
    位域:  
    有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:    
    struct 位域结构名    
    { 位域列表 };   
    其中位域列表的形式为:类型说明符位域名:位域长度    
    例如:    
    struct bs   
    {   
    int a:8;   
    int b:2;   
    int c:6;   
    };   
    位域变量的说明与结构变量说明的方式相同。可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:    
    struct bs   
    {   
    int a:8;   
    int b:2;   
    int c:6;   
    }data;   
    说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明:   
    1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:    
    struct bs   
    {   
    unsigned a:4   
    unsigned :0 /*空域*/   
    unsigned b:4 /*从下一单元开始存放*/   
    unsigned c:4   
    }   
    在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。
    2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。   
    3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:    
    struct k   
    {   
    int a:1   
    int :2 /*该2位不能使用*/   
    int b:3   
    int c:2   
    };   
    从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。   
    二、位域的使用位域的使用和结构成员的使用相同,其一般形式为:位域变量名•位域名位域允许用各种格式输出。   
    main(){   
    struct bs   
    {   
    unsigned a:1;   
    unsigned b:3;   
    unsigned c:4;   
    } bit,*pbit;   
    bit.a=1;   
    bit.b=7;   
    bit.c=15;   
    pri
     
    1、头文件中的 ifndef/define/endif 干什么用?(5分)
    答:防止该头文件被重复引用。
     
    (2)strcpy能把strSrc的内容复制到strDest,为什么还要char * 类型的返回值?
    答:为了实现链式表达式。
     
    六、编写类String的构造函数、析构函数和赋值函数(25分)
    已知类String的原型为:
        class String
        {
          public:
           String(const char *str = NULL); // 普通构造函数
           String(const String &other);        // 拷贝构造函数
           ~ String(void);                     // 析构函数
           String & operate =(const String &other);  // 赋值函数
          private:
           char   *m_data;             // 用于保存字符串
        };
        请编写String的上述4个函数。
    标准答案:
    // String的析构函数
       String::~String(void)               // 3分
    {
      delete [] m_data;                      
    // 由于m_data是内部数据类型,也可以写成 delete m_data;
       }
        // String的普通构造函数            
       String::String(const char *str)      // 6分
    {
      if(str==NULL)                         
      {
         m_data = new char[1];    // 若能加 NULL 判断则更好
         *m_data = ‘’;                     
      }                                        
      else
      {
         int length = strlen(str);          
         m_data = new char[length+1];  // 若能加 NULL 判断则更好     
         strcpy(m_data, str);               
      }
    }
    // 拷贝构造函数
       String::String(const String &other)   // 3分
       { 
      int length = strlen(other.m_data);
      m_data = new char[length+1];      // 若能加 NULL 判断则更好   
      strcpy(m_data, other.m_data);        
    }
    // 赋值函数
       String & String::operate =(const String &other)    // 13分
       { 
          // (1) 检查自赋值                     // 4分
          if(this == &other)
             return *this;
      
    // (2) 释放原有的内存资源            // 3分
          delete [] m_data;
         
          // (3)分配新的内存资源,并复制内容 // 3分
      int length = strlen(other.m_data);
      m_data = new char[length+1];         // 若能加 NULL 判断则更好
          strcpy(m_data, other.m_data);
         
          // (4)返回本对象的引用            // 3分
          return *this;
    }
     
     

    <wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">





  • 相关阅读:
    vue中的 computed 和 watch 的区别
    mysql8.0 初始化数据库及表名大小写问题
    sql server alwayson 调整数据文件路径
    zabbix 自定义监控 SQL Server
    mysql 创建用户及授权
    mysql 设置从库只读模式
    mysql8.0 主从复制安装及配置
    centos8.0安装mysql8.0
    centos8替换阿里数据源
    npm publish 报错 【you or one of your dependencies are requesting a package version that is forbidden by your security policy】
  • 原文地址:https://www.cnblogs.com/lvonve/p/8639327.html
Copyright © 2011-2022 走看看