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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qr_ljj/article/details/53069679


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\n", 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\n",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<<n) 

    GPXX |= (1<< 7) | (1<< 4 ) | (1<< 0)//047位置1,其他保留

    B、将寄存器指定位(第n位置为0

    GPXX &= ~1<<n )

    将寄存器的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 <stdio.h>

 int main(int argc, char **argv)

 {

      union

      {

          int a;

          char b;

      }c;

      c.a = 1;

      if(c.b == 1)

      {

          printf("little\n");

      }

      else

          printf("big\n");

      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 <stdio.h>

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;

21、.h头文件中的ifndef/define/endif 的作用?
    答:防止该头文件被重复引用。

 

22、#include 与 #include “file.h”的区别?
    答:前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。

 

23、描述实时系统的基本特性
    答 :在特定时间内完成特定的任务,实时性与可靠性。

 

24、全局变量和局部变量在内存中是否有区别?如果有,是什么区别?
    答 :全局变量储存在静态数据区,局部变量在堆栈中。

 

25、堆栈溢出一般是由什么原因导致的?
    答 :    A、没有回收垃圾资源
             B、层次太深的递归调用

 

26、不能做switch()的参数类型
    答 :switch的参数不能为实型。

 

27、局部变量能否和全局变量重名?
    答:能,局部会屏蔽全局。要用全局变量,需要使用”::”
局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内

 

28、如何引用一个已经定义过的全局变量?
    答 可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变量,假定你将那个变量写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。

 

29、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
    答 可以,在不同的C文件中以static形式来声明同名全局变量。
可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错。

 

30、do……while和while……有什么区别?
    答 前一个循环一遍再判断,后一个判断以后再循环。

 

31、程序的内存分配
    答:一个由C/C++编译的程序占用的内存分为以下几个部分
    A、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
    B、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
    C、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
    D、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。
    E、程序代码区—存放函数体的二进制代码

 

32、解释堆和栈的区别
    答:堆(heap)和栈(stack)的区别
 A、申请方式
    stack:由系统自动分配。例如,声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间
    heap:需要程序员自己申请,并指明大小,

在c中malloc函数
    如p1=(char*)malloc(10);
    但是注意p1本身是在栈中的。
  B、申请后系统的响应
    栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
    堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
  C、申请大小的限制
    栈:栈是向低地址扩展的数据结构,是一块连续的内存的区域,即栈顶的地址和栈的最大容量是系统预先规定好的,linux系统可以使用ulimit -a命令查看当前系统的栈的大小默认为10M(64位系统),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
    堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
  D、申请效率的比较:
    栈:由系统自动分配,速度较快。但程序员是无法控制的。
    堆:是由malloc分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
  E、堆和栈中的存储内容
    栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
    堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。
  F、存取效率的比较
    char s1[]=”aaaaaaaaaaaaaaa”;
    char *s2=”bbbbbbbbbbbbbbbbb”;
    aaaaaaaaaaa是在运行时刻赋值的;
    而bbbbbbbbbbb是在编译时就确定的;
    但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
    比如:
#include
void main()
{
char a=1;
char c[]=”1234567890″;
char *p=”1234567890″;
a = c[1];
a = p[1];
return;
}
对应的汇编代码
10:a=c[1];
004010678A4DF1mov cl,byteptr[ebp-0Fh]
0040106A884DFCmov byteptr[ebp-4],cl
11:a=p[1];
0040106D8B55ECmov edx,dwordptr[ebp-14h]
004010708A4201mov al,byteptr[edx+1]
004010738845FCmov byteptr[ebp-4],al
    第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。


33、什么是预编译,何时需要预编译?
    答:预编译又称为预处理,是做些代码文本的替换工作。处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等,就是为编译做的预备工作的阶段,主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。
GCC编译系统在对程序进行通常的编译之前,先进行预处理。GCC提供的预处理功能主要有以下三种:宏定义文件包含条件编译

使用场合如下:

    A、总是使用不经常改动的大型代码体。
    B、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。

 

34、描述内存分配方式以及它们的区别?
    答:

    A、从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
    B、在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
    C、从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多

 

35、简述数组与指针的区别?
    答:数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。
    A、修改内容上的差别
char a[] = “hello”;
a[0] = ‘X’;
char *p = “world”; // 注意p 指向常量字符串
p[0] = ‘X’; // 编译器不能发现该错误,运行时出现段错误
    B、用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。
char a[] = “hello world”;
char *p = a;
cout<< sizeof(a) << endl; // 12 字节
cout<< sizeof(p) << endl; // 4 字节
计算数组和指针的内存容量
void Func(char a[100])
{
cout<< sizeof(a) << endl; // 4 字节而不是100 字节
}


36、论述含参数的宏与函数的优缺点
答:

带参宏                 函数
处理时间     编译时                 程序运行时
参数类型     没有参数类型问题       定义实参、形参类型
处理过程     不分配内存             分配内存
程序长度     变长                   不变
运行速度     不占运行时间           调用和返回占用时间


37、写一个“标准”宏
答:交换两个参数值的宏定义为:. #define SWAP(a,b)\
(a)=(a)+(b);\
(b)=(a)-(b);\
(a)=(a)-(b);
输入两个参数,输出较小的一个:

#define MIN(A,B) ((A) < (B))? (A) : (B))
表明1年中有多少秒(忽略闰年问题):

#define SECONDS_PER_YEAR (60UL* 60 * 24 * 365)
已知一个数组table,用一个宏定义,求出数据的元素个数
#define NTBL (sizeof(table)/sizeof(table[0]))

 

38、A.c 和B.c两个c文件中使用了两个相同名字的static变量,编译的时候会不会有问题?这两个static变量会保存到哪里(栈还是堆或者其他的)?
    答:

    static的全局变量,表明变量仅在本模块中有意义,不会影响其他模块。
    存放静态数据区,但是编译器对他们的命名是不同的。
    如果要使变量在其他模块也有意义的话,需要使用extern关键字。

 

39、下面的代码输出是什么,为什么?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts(“> 6″) : puts(“<= 6");
}
    当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。

 

40、交换两个变量的值,不使用第三个变量。
    即a=3,b=5,交换之后a=5,b=3;
有两种解法, 一种用算术算法, 一种用^(异或)
a = a + b;
b = a - b;
a = a - b;
or
a = a^b;// 只能对int,char..
b = a^b;
a = a^b;
or
a ^= b ^= a;

41、下面的程序会出现什么结果
#include <stdio.h>
#include <stdlib.h>

#include <string.h>
void getmemory(char *p)
{
    p=(char *) malloc(100);
    strcpy(p,”hello world”);
}
int main( )
{
    char *str=NULL;
    getmemory(str);
    printf(“%s/n”,str);
    free(str);
    return 0;
}
程序崩溃,getmemory中的malloc 不能返回动态内存, free()对str操作很危险

 

42、(void *)ptr 和 (*(void**))ptr的结果是否相同?
答:ptr为同一个指针
(void *)ptr 和 (*(void**))ptr值是相同的

 

43、对绝对地址0×100000赋值且想让程序跳转到绝对地址是0×100000去执行
(unsigned int*)0×100000 = 1234;
首先要将0×100000强制转换成函数指针,即:
(void (*)())0×100000
然后再调用它:
*((void (*)())0×100000)();
用typedef可以看得更直观些:
typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0×100000)();

 

44、下面的函数实现在一个数上加一个数,有什么错误?请改正。
int add_n ( int n )
{
    static int i = 100;
    i += n;
    return i;
}
    当你第二次调用时得不到正确的结果,难道你写个函数就是为了调用一次?问题就出在 static上


45、下面这个程序执行后会有什么错误或者效果:
#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了..无限循环下去. 

46、请问一下程序将输出什么结果?
char *RetMenory(void)
{
    char p[] = “hellow world”;
    return p;
}
void Test(void)
{
    char *str = NULL;
    str = RetMemory();
    printf(str);
}
    RetMenory执行完毕,p资源被回收,指向未知地址。返回地址,str的内容应是不可预测的, 打印的应该是str的地址

 

47、对下面程序进行分析
void test2()
{
    char string[10], str1[10];
    int i;
    for(i=0; i<10; i++)
    {
        str1[i] = 'a';
    }
    strcpy( string, str1 );
}
解答:

字符数组str1不能在数组内结束

strcpy(string, str1)调用使得从str1内存起复制到string内存起所复制的字节数具有不确定性

指出库函数strcpy工作方式

str1不能在数组内结束:因为str1的存储为:{a,a,a,a,a,a,a,a,a,a},没有'\0'(字符串结束符),所以不能结束
strcpy( char *s1,char *s2)他的工作原理是,扫描s2指向的内存,逐个字符付到s1所指向的内存,直到碰到'\0',因为str1结尾没有'\0',所以具有不确定性,不知道他后面还会付什么东东。
正确应如下
void test2()
{
    char string[10], str1[10];
    int i;
    for(i=0; i<9; i++)
    {
        str1[i] = 'a'+i; //把abcdefghi赋值给字符数组
    }
    str[i]='\0';//加上结束符
    strcpy( string, str1 );
}

 

48、分析下列代码输出的值
int arr[] = {6,7,8,9,10};
int *ptr = arr;
*(ptr++)+=123;
printf(“ %d %d ”, *ptr, *(++ptr));
输出:8 8
过程:对于*(ptr++)+=123;先做加法6+123,然后++,指针指向7;对于printf(“ %d %d ”, *ptr, *(++ptr));从后往前执行,指针先++,指向8,然后输出8,紧接着再输出8

 

49、分析下面的代码:
char *a = "hello";
char *b = "hello";
if(a= =b)
    printf("YES");
else
    printf("NO");
输出YES

"hello"是一个常量字符串位于静态存储区,在程序生命期内恒定不变a和b同时指向同一个hello的。

 

50、已知strcpy函数的原型是:
char * strcpy(char * strDest,const char * strSrc);
不调用库函数,实现strcpy函数。
解释为什么要返回char *。
解答
strcpy实现代码

char * strcpy( char *strDest, const char *strSrc )
{
 assert( (strDest != NULL) && (strSrc != NULL) );
 char *address = strDest;
 while( (*strDest++ = * strSrc++) != '’ );
  return address;
}

 

51、判断一个字符串是不是回文
int IsReverseStr(char *aStr)
{
    int i,j;
    int found=1;
    if(aStr==NULL)
        return -1;
    j=strlen(aStr);
    for(i=0;i<j; i++)
    if(*(aStr+i)!=*(aStr+j-i-1))
    {
        found=0;
        break;
    }
    return found;
}

 

52、写一个函数比较两个字符串str1和str2的大小,若相等返回0,若str1大于
str2返回1,若str1小于str2返回-1
int strcmp ( const char * src,const char * dst)
{
    int ret = 0 ;
    while( ! (ret = *(unsigned char *)src – *(unsigned char *)dst) && *dst)
    {
        ++src;
        ++dst;
    }
    if ( ret < 0 )
        ret = -1 ;
    else if ( ret > 0 )
        ret = 1 ;
    return( ret );

}


53、给定字符串A和B,输出A和B中的最大公共子串。
比如A="aocdfe" B="pmcdfa" 则输出"cdf"
*/
//Author: azhen
#include
#include
#include

char *commanstring(char shortstring[], char longstring[])
{
    int i, j;

 char *substring=malloc(256);

 if(strstr(longstring, shortstring)!=NULL)

return shortstring;

    for(i=strlen(shortstring)-1;i>0; i–) //否则,开始循环计算
    {
        for(j=0; j<=strlen(shortstring)-i; j++)

 {
            memcpy(substring, &shortstring[j], i);
            substring[i]='\0';
            if(strstr(longstring, substring)!=NULL)
            return substring;
        }
    }
    return NULL;
}

int main()
{
    char *str1=malloc(256);
    char *str2=malloc(256);
    char *comman=NULL;

 gets(str1);
    gets(str2);

 if(strlen(str1)>strlen(str2)) //将短的字符串放前面
        comman=commanstring(str2, str1);
    else
        comman=commanstring(str1, str2);

    printf(“the longest comman string is: %s\n”, comman);
}


54、编写一个 C 函数,该函数在一个字符串中找到可能的最长的子字符串,且该字符串是由同一字符组成的。
char * search(char *cpSource, char ch)
{
    char *cpTemp=NULL, *cpDest=NULL;
    int iTemp, iCount=0;
    while(*cpSource)
    {
        if(*cpSource == ch)
        {
            iTemp = 0;
            cpTemp = cpSource;
            while(*cpSource == ch)
                ++iTemp, ++cpSource;
            if(iTemp > iCount)
                iCount = iTemp, cpDest = cpTemp;
            if(!*cpSource)
                break;
        }
    ++cpSource;
    }
    return cpDest;
}

 

55、不用库函数,用C语言实现将一整型数字转化为字符串
int getlen(char *s)

{
    int n;
    for(n = 0; *s != '\0'; s++)
        n++;
    return n;
}
void reverse(char s[])
{
    int c,i,j;
    for(i = 0,j = getlen(s) - 1; i < j; i++,j--)

{
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
}
void itoa(int n,char s[])
{
    int i,sign;
    if((sign = n) < 0)
    n = -n;
    i = 0;
    do{/*以反序生成数字*/
        s[i++] = n%10 + '0';/*get next number*/
    }while((n /= 10) > 0);/*delete the number*/

 if(sign < 0)
        s[i++] = '-';

    s[i] = '\0';
    reverse(s);
}


56、请说出const与#define 相比,有何优点?
答:const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
A、const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
B、有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

 

57、编写用C语言实现的求n阶阶乘问题的递归算法:

答:

long int fact(int n)
{

    if(n==0||n==1)

       return 1;

    else

       return n*fact(n-1);

}

 

58、二分查找算法:

A递归方法实现:

int BSearch(elemtype a[],elemtype x,int low,int high)

/*在下为low,上界为high的数组a中折半查找数据元素x*/

{

int mid;

if(low>high)

return -1;

mid=(low+high)/2;

if(x == a[mid])

return mid;

       if(x<a[mid])

return(bsearch(a,x,low,mid-1));

else

return(BSearch(a,x,mid+1,high));

}

B、非递归方法实现:

int BSearch(elemtype a[],keytype key,int n)

{

int low,high,mid;

low=0;high=n-1;

while(low<=high)

{

mid=(low+high)/2;

if(a[mid].key==key)

return mid;

else if(a[mid].key<key)

low="mid+1;</font">

else

high=mid-1;

}

return -1;

}

 

59、递归计算如下递归函数的值(斐波拉契)

f(1)=1

f(2)=1

f(n)=f(n-1)+f(n-2) n>2

解:

A、非递归算法

int f(int n)

{

int i,s,s1,s2;

s1=1;/*s1用于保存f(n-1)的值*/

s2=1;/*s2用于保存f(n-2)的值*/

s=1;

for(i=3;i<=n;i++)

{

s=s1+s2;

s2=s1;

   s1=s;

}

return(s);

}

B、递归算法

int f(int n)

{

if(n==1||n==2)

           rerurn 1;

       else

           return f(n-1)+f(n-2);

}


60、如何判断一段程序是由C 编译程序还是由C++编译程序编译的?
答:

    #ifdef __cplusplus
    cout<<"c++";
    #else
    cout<<"c";
    #endif


61、结构与共用体有和区别?
答:

  a、结构和共用体都是由多个不同的数据类型成员组成, 但在任何同一时刻, 共用体中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。
    b、对于共用体的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的

猜你喜欢

转载自blog.csdn.net/qr_ljj/article/details/53069679