嵌入式linux面试题解析(二)——C语言部分一

2019-07-13 06:50发布

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

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次方,返回0elsereturn 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 integerse) 一个有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 integerse) 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 integerh) 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 7i & 3:0 1 2 3 0 1 2 3 k:0 1 2 3 6 6 7 9i共计有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  |  0x12Big-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    分析:花括号里嵌套的是小括号而不是花括号!这里是花括号里面嵌套了逗号表达式!其实这个赋值就相当于int a [3][2]={ 1, 3, 5};
20、评价下面的代码片断    unsigned int zero = 0;
    unsigned int compzero = 0xFFFF;     对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:     unsigned int compzero = ~0;
本文出自 “生命不息,奋斗不止” 博客,转载请与作者联系!