嵌入式linux开发面试题解析——C语言部分

2019-07-12 15:01发布

 1、编写统计一个数二进制表示中有多少个1的函数         int count_bit1(int m)         {      int count = 0;      while(m)      {      m = m & (m-1);      count++;       }     return count;         }   2、编写一个函数判断一个数是否是2的N次方          int is_number(int num)          {       if( m & (m - 1) == 0)    return 0;//如果一个数是2N次方,返回0    else    return 1;//如果一个数不是2N次方,返回1          }   3、用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)     #define    SECONDS_PER_YEAR    (1UL*60 * 60 * 24 * 365)    兼容16位CPU,          #define    SECONDS_PER_YEAR    (60 * 60 * 24 * 365)UL这种用法在GCC是不能编译通过的。 4、预处理器标识#error的目的是什么? #error error-message停止编译并显示错误信息,error-message不需要双引号
5、嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?           while(1)
          {       ;
          }   6、用变量a给出下面的定义    a) 一个整型数(An integer 
   b)一个指向整型数的指针( A pointer to an integer 
   c)一个指向指针的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an integer
   d)一个有10个整型数的数组( An array of 10 integers    e) 一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers 
   f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers 
   g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as anargument and returns an integer 
   h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take aninteger argument and return an integer    定义如下:     a) int a; // An integer 
    b) int *a; // A pointer to an integer 
    c) int **a; // A pointer to a pointer to an integer 
    d) int a[10]; // An array of 10 integers     e) int *a[10]; // Anarray of 10 pointers to integers 
等价于int *(a[10]);     f) int (*a)[10]; // Apointer to an array of 10 integers 
    g) int (*max_function)(int a); // A pointer to afunction a that takes an integer argument and returns an integer     h) int (*a[10])(int);// An array of 10 pointers to functions that take an integer argument andreturn an integer   7、键字static的作用是什么?     在C语言中,关键字static有三个明显的作用:
    A、一旦声明为静态变量,在编译时刻开始永远存在,不受作用域范围约束,但是如果是局部静态变量,则此静态变量只能在局部作用域内使用,超出范围不能使用,但是它确实还占用内存,还存在.
    B、在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
    C、在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。   8、typedef与define的区别     typedef是C语言中用来声明自定义数据类型,配合各种原有数据类型来达到简化编程的目的的类型定义关键字     #define是预处理指令,是宏定义。        int a = 80;        typedef int * PINT;        const PINT p = &a;//const修饰指针类型,限定指针变量p为只读        PINT const p = &a;//const修饰指针类型,限定指针变量p为只读 9、k输出的值是多少     int main(int argc, char *argv[])     {       char k = 0;       int i;       for(i = 0; i < 127; i++)           k += i & 3;       printf("k = %d ", k);       return 0;     }     i:0 1 2 3 4 5 6 7 i & 3:0 1 2 3 0 1 2 3  k:0 1 2 3 6 6 7 9 i共计有32组0 1 2 3和一组0 1组成,因此k=32 *(0 + 1 + 2 + 3)+(0 + 1)= 187,由于k为char,所以k在127以后的数是-127,-126....,所以K = (187-127)+ (-127)= -67; 输出-67; 10、嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。     在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。解答出如下:     int *ptr;      ptr = (int *)0x67a9;      *ptr = 0xaa55; 嵌入式开发中常用应用:     #define rPCONA(*(volatile unsigned *)0x1D20000)     #define rPDATA(*(volatile unsigned *)0x1D20004)   11、下列代码输出结果是多少 char str1[] = "abc";  char str2[] = "abc";  const char str3[] = "abc";  const char str4[] = "abc";    const char *str5 = "abc";  const char *str6 = "abc";  char *str7 = "abc";  char *str8 = "abc";    cout << ( str1 == str2 ) << endl;  cout << ( str3 == str4 ) << endl;  cout << ( str5 == str6 ) << endl;  cout << ( str7 == str8 ) << endl;   解答:     输出结果是:0 0 1 1      解析:str1,str2,str3,str4是数组变量,有各自的内存空间;而str5,str6,str7,str8是指针,指向相同的常量区域。
12、简介##和#的作用     预处理器运算符##是连接符号,由两个井号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元。如果替换文本中的参数与##相邻,则该参数将被实际参数替换,##与前后的空白将被删除,并对替换后的结果重新扫描。 形成一个新的标号,如果这样产生的记号无效,或者结果依赖于##运算顺序,则结果没有定义。     #definepaste(front,back)front##back     宏调用paste(name,_xiaobai)的结果为name_xiaobai     #符是把传递过来的参数当成字符串进行替代     #definedprint(expr)printf(#expr"=%d ",expr)     int a=20,b=10;     dprint(a/b);     打印出 a/b=2
13、关键字volatile有什么含意?并给出三个不同的例子。     定义为volatile的变量表明变量可能会被意想不到地改变,编译器就不会去假设这个变量的值。准确地说,优化器在用到volatile修饰的变量时必须每次都小心地重新读取变量的值,而不是使用保存在寄存器里的备份。     使用volatile变量的例子:
    A、并行设备的硬件寄存器(如:状态寄存器)   B、一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
    C、多线程应用中被几个任务共享的变量 深入理解: 1一个参数既可以是const还可以是volatile吗?解释为什么。 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2一个指针可以是volatile 吗?解释为什么。 是的。尽管这并不很常见。一个例子是当一个中服务子程序修一个指向一个buffer的指针时。
3下面的函数有什么错误:
     int square(volatile int *ptr)
     {
        return *ptr   *ptr;
      }     函数目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int  *ptr)
{
    int a,b;
    a = *ptr;
    b = *ptr;
    return a * b;
}     由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,函数可能返不是你所期望的平方值 long square(volatile int *ptr)
{
    int a;
    a = *ptr;
    return a * a;
}
14、嵌入式系统中断服务子程序(ISR)     中断是嵌入式系统中重要的组成部分,导致了很多编译开发商提供一种扩展—让标准C支持中断。具体代表是,产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。
__interrupt double compute_area (double radius) {     double area = PI * radius * radius;     printf("Area = %f", area);     return area; }     中断程序的特点:
    A、ISR 不能返回一个值。     B、ISR 不能传递参数。     C、在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。     D、printf()经常有重入和性能上的问题。
15、嵌入式C语言开发中的位操作     A、将寄存器指定位(第n位置为1     GPXX |= (1<     GPXX |= (1<< 7) | (1<< 4 ) | (1<< 0)//047位置1,其他保留     B、将寄存器指定位(第n位置为0     GPXX &= ~1<     将寄存器的n位清0,而又不影响其它位的现有状态。     GPXX &= ~1<<4 )     C、嵌入式开发位操作实例     unsigned int i = 0x00ff1234;     //i |= (0x1<<13);//bit131     //i |= (0xF<<4);//bit4-bit71     //i &= ~(1<<17);//清除bit17     //i &= ~(0x1f<<12);//清除bit12开始的5          //取出bit3-bit8     //i &= (0x3F<<3);//保留bit3-bit8,其他位清零     //i >>= 3;//右移3       //给寄存器的bit7-bit17赋值937     //i &= ~(0x7FF<<7);//bit7-bit17清零     //i |= (937<<7);//bit7-bit17赋值       //将寄存器bit7-bit17的值加17    // unsigned int a = i;//a作为i的副本,避免i的其他位被修改    // a &= (0x7FF<<7);//取出bit7-bit17    //a >>= 7;//    // a += 17;//17    // i &= ~(0x7FF<<7);//ibit7-bit17清零    // i |= (a<<7);//+17后的数写入bit7-bit17,其他位不变       //给一个寄存器的bit7-bit17赋值937,同时给bit21-bit25赋值17     i &= ~((0x7FF<<7) | (0x1F<<21));//bit7-bit17bit21-bit25清零     i |= ((937<<7) | (17<<21));//bit7-bit17bit21-bit25赋值     D、位操作的宏定义 //用宏定义将32位数x的第n(bit0为第1)置位 #define SET_BIT_N(x,n) (x | (1U<<(n-1))) //用宏定义将32位数x的第n(bit0为第1)清零 #define CLEAR_BIT_N(x,n) (x & (~(1U<<(n-1)))) //用宏定义将32位数x的第n位到第m(bit0为第1)置位 #define SET_BITS_N_M(x,n,m) (x | (((~0U)>>(32-(m-n+1)))<<(n-1))) //用宏定义将32位数x的第n位到第m(bit0为第1)清零 #define CLEAR_BITS_N_M(x,n,m) (x & (~(((~0U)>>(32-(m-n+1)))<<(n-1)))) //用宏定义获取32位数x的第n位到第m(bit0为第1)的部分 #define GET_BITS_N_M(x,n,m) ((x & ~(~(0U)<<(m-n+1))<<(n-1))>>(n-1))
16、大小端的介绍     Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
     Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
    数字0x12 34 56 78在内存中的表示形式为: 大端模式: 低地址 -----------------> 高地址
0x12  |  0x34  |  0x56  |  0x78 小端模式: 低地址 ------------------> 高地址
0x78  |  0x56  |  0x34  |  0x12 Big-Endian: 低地址存放高位,如下:
高地址
        ---------------
        buf[3] (0x78) -- 低位
        buf[2] (0x56)
        buf[1] (0x34)
        buf[0] (0x12) -- 高位
        ---------------
        低地址
Little-Endian: 低地址存放低位,如下:
高地址
        ---------------
        buf[3] (0x12) -- 高位
        buf[2] (0x34)
        buf[1] (0x56)
        buf[0] (0x78) -- 低位
        --------------
低地址 C语言实现测试大小端:  #include  int main(int argc, char **argv)  {       union       {           int a;           char b;       }c;       c.a = 1;       if(c.b == 1)       {           printf("little ");       }       else           printf("big ");       return 0;   }     常见CPU的大小端:  Big Endian : PowerPC、IBM、Sun
    Little Endian : x86、DEC     常见文件的字节序  Adobe PS – Big Endian
    BMP – Little Endian
    DXF(AutoCAD) – Variable
    GIF – Little Endian
    JPEG – Big Endian
    MacPaint – Big Endian
    RTF – Little Endian
    Java和所有的网络通讯协议都是使用Big-Endian的编码。 大小端的转换: 16位 #define BigtoLittle16(A)   (( ((uint16)(A) & 0xff00) >> 8)    |                                           (( (uint16)(A) & 0x00ff) << 8))   32位 #define BigtoLittle32(A)   ((( (uint32)(A) & 0xff000000) >> 24) |                                           (( (uint32)(A) & 0x00ff0000) >> 8)   |                                           (( (uint32)(A) & 0x0000ff00) << 8)   |                                           (( (uint32)(A) & 0x000000ff) << 24))       从软件的角度上,不同端模式的处理器进行数据传递时必须要考虑端模式的不同。如进行网络数据传递时,必须要考虑端模式的转换。在Socket接口编程中,以下几个函数用于大小端字节序的转换。     ntohs(n)     //16位数据类型网络字节顺序到主机字节顺序的转换       htons(n)     //16位数据类型主机字节顺序到网络字节顺序的转换       ntohl(n)      //32位数据类型网络字节顺序到主机字节顺序的转换       htonl(n)      //32位数据类型主机字节顺序到网络字节顺序的转换    17、引用和指针有什么区别? A、应用必须初始化,指针不必; B、引用处画化后不能改变,指针可以被改变; C、不存在指向空值的引用,但存在指向空值的指针;
18、写出floatboolint类型与零的比较,假设变量为X        Int     ifx==0        Float    ifx > -0.0000001 && x < 0.0000001        Bool:      if(x==false)
19、多维数组的定义     在数组定义int a[2][2]={{3},{2,3}};则a[0][1]的值为0。(对) #include intmain(int argc,char * argv[]) { int a [3][2]={(0,1),(2,3),(4,5)}; int *p; p=a [0]; printf("%d",p[0]); }     问打印出来结果是多少?     答案:1