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;">

  • 相关阅读:
    MVC5+EF6简单实例以原有SQLServer数据库两表联合查询为例
    ArcGIS生成根据点图层生成等值面并减小栅格锯齿的操作步骤
    EF6+MVC5之Oracleo数据库的Code First方式实现
    jquery写的tab切换效果 非常简单
    APP消息推送是否进入消息中心和click、receive事件分析
    Android APP切换到后台接收不到推送消息
    js写的简单轮播图
    凉凉的笔记 two day
    凉凉的笔记 one.day
    在SQL Server数据库中批量导入数据的四种方法
  • 原文地址:https://www.cnblogs.com/lvonve/p/14180376.html
Copyright © 2011-2022 走看看