c语言面试常见题

一、typedef

为现有类型创建一个新的名字, 使用最多的地方是创建易于记忆的类型名
typedef int size;此声明定义了一个 int 的同义字,名字为 size

想看http://baike.baidu.com/view/1283800.htm

12:考查typedef类型定义,函数指针

1typedef int (*test) ( float * , float*)
test tmp;

 tmp 的类型是
(a) 函数的指针,该函数以 两个指向浮点数(float)的指针(pointer)作为参数(arguments)
      Pointer to function of having two arguments that is pointer to float
(b) 整型
(c) 函数的指针,该函数以 两个指向浮点数(float)的指针(pointer)作为参数(arguments),并且函数的返回值类型是整型 
      Pointer to function having two argument that is pointer to float and return int
(d) 以上都不是

另外一例:

typedef void*ptr_tp_func(int);/*它表示ptr_tp_func是函數指針,該函數接受一個int參數,返回值為void*/

ptr_tp_func signal(int, ptr_tp_func);/*表示signal是一個函數,接受兩個參數,一個int和一個ptr_tp_func,返回值是ptr_tp_func類型*/

二、按位运算小技巧

1统计i有多少位为1for( ; i ; i&=i-1) k++;

2宏写出swap(x,y)

 #define swap(x,y)

      x=x+y;      y=x-y;      x=x-y;

3、一句实现判断x是不是2的若干次幂:x&(x-1)false:true;

4、实现左移循环移n位:a=(a<<n)|(a>>(8*sizeof(int)-n));

5、快速求一个数的7倍:X<<3-X

三、编译宏

      #define     定义宏
         #undef      取消已定义的宏
         #if         如果给定条件为真,则编译下面代码
         #ifdef      如果宏已经定义,则编译下面代码
         #ifndef     如果宏没有定义,则编译下面代码
         #elif       如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
         #endif      结束一个#if……#else条件编译块
         #error      停止编译并显示错误信息

四、指针

1 10个指针的数组 int *p[10];

     int (*p)[10]p是一个指针。它的类型是:指向int x[10]这样的一维数组的指针。

    指向函数的指针,函数有一个整型参数并返回一个整数型 int (*p)(int);

一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数 int (*p[10])(int);

2、char a[9]="abcdefg";     char *p="abcdefg";

其它区别:1.指针保存数据的地址,如p保存的是常量字符串“abcdefg”地址,对p内容的修改是非法的 ;数组保存的是数据,对数组的内容可以直接修改。

五、限定符的用法

1const  

 1)作用:a.告知参数的应用目的

                 b.使优化器产生紧凑的代码

                 c.保护不希望被改变的参数:防止参数意外改变

   2)例子:a.  const int a;  a是一整型常量

                  b. int const a;  同上

                 c. const int *a; 指针a指向的内容不可变

                 d. int * const a;指针a指向的内容可变,但指针不可变

                 e. int const * a const; 都不可变

面试的问题:

     a.一个参数既可以是const又是volatile吗?

     b.一个指针可以是volatile吗?

    答:a.可以,例子:只读的状态寄存器,它是volatile因为可能被意想不到的改变,是const因为不希望程序试图去改变它。

           b.可以。一个例子是中断服务子程序修改一个指向一个buffer的指针时。

此类考题记住volatile型变量可能随时改变即可。

2restrict

restrict关键字允许编译器优化某部分代码以更好地支持计算。它只能用于指针,表明该指针是访问该对象唯一且初始的方式。

int ar[10];

int * restrict restar= (int *) malloc(10 * sizeof(int));

 int * par= ar;

for (n=0; n<10; n++) {

par[n]+=5;

restar[n] +=5;

ar[n] *=2;

par[n] +=3;

restar[n] +=3;

 }

restar是访问它所指向的数据块的唯一且初始的方式,编译器可以把涉及restar的两条语句替换成下面的语句,效果相同:

restar[n] +=8;/*可以进行替换*/

3、register

register关键字请求让编译器将变量a直接放入寄存器里面,以提高读取速度,因为register变量可能不存放在内存中,C语言中register关键字修饰的变量不可以被取地址,但是c++中进行了优化。

六、精度和优先级

1类型不同的操作数运算,精度向低级自动转换。无符号+有符号=无符号

2.int a=12,则执行完语句 a+=a-=a*a后,a的值是-264.

所有的赋值符(包括复合赋值)都具有右结合性,就是在表达式中最右边的操作最先执行,然后从右到左依次执行。

3c中函数参数默认是从右向左压栈的,printf计算参数时也是

     int arr[]={6,7,8,9,10};     int *ptr=arr;    *(ptr++)+=123;

     printf("%d,%d\n",*ptr,*(++ptr));结果为8

  printf内的参数从右向左运算。

4位运算要考虑机器字长,算术运算符优先级高于移位运算符

unsigned char = 0xA5;   unsigned char  b = ~a>>4+1; b250.   

5三个float:a,b,c .问值 (a+b)+c==(b+a)+c (a+b)+c==(a+c)+b

不一定相等,float存在大数淹没小数的情况,如 
float   a=100000000000,b=-100000000000,c=0.00000000001; 
cout < <a+b+c < <endl; 
cout < <a+c+b < <endl; 
结果是1e-0110 
尽量把数量级相近的数先相加

 

七、大小端问题

  大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中。armpowerpc

  小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。intelx86

 

大小端测试程序1

       int checkSystem( )

       {union check

          {int i;    char ch;

          }c;       c.i=1;        return (c.ch==1);

       }

 测试大小端的程序2

 

在使用little endian的系统中 这些函数会把字节序进行转换

 define HTONS(n) ((((u16_t)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))

ltons unsigned short类型从主机序转换到网络序

htonl unsigned long类型从主机序转换到网络序

ntohs unsigned short类型从网络序转换到主机序

ntohl unsigned long类型从网络序转换到主机序

在使用big endian类型的系统中 这些函数会定义成空宏

利用linux中自带的宏进行判断:

#if __BYTE_ORDER == __LITTLE_ENDIAN 
// 小头字节序 
#elif __BYTE_ORDER == __BIG_ENDIAN 
// 大字节序

八、内存

1、申请内存的函数

void *calloc ( size_t num_elements, size_t element_size );

void *realloc (void *ptr, size_t new_size );

realloc函数用于修改一个原先已经分配的内存块的大小,可以使一块内存的扩大或缩小。当起始空间的地址为空,即*ptr = NULL,则同malloc。callocmalloc相比:calloc分配的内存被初始化为0calloc两个参数:元素个数,元素大小

2、内存分布

BSS段:未初始化的全局变量和静态变量;数据段:初始化的全局变量和静态变量;代码段(或文本段):可执行文件的指令;局部变量运行时创建并存储于栈,函数结束即释放;静态变量和全局变量则在程序结束后释放;可执行文件中包含BSS段所需要的大小,但不是BSS段,堆栈等在程序运行时创建

九、中断

1 找出下面一段ISR的问题。
      __interrupt double compute_area (double radius)
  {
      double area = PI * radius * radius;
      printf("\nArea = %f", area);
      return area;
  }
 1ISR不能传递参数。
 2ISR不能有返回值。
 3ISR应该短且有效率,在ISR中做浮点运算不明智。

4ISR中不应该有重入和性能上的问题,因此不应该使用pintf()函数。

不可重入函数不可以在它还没有返回就再次被调用。例如printfmallocfree等都是不可重入函数。因为中断可能在任何时候发生,例如在printf执行过程中,因此不能在中断处理函数里调用printf,否则printf将会被重入。 函数不可重入大多数是因为在函数中引用了全局变量。例如,printf会引用全局变量stdoutmallocfree会引用全局的内存分配表。

十、链表

一个链表不知道头结点,有一个指针指向其中一个结点,请问如何删除这个指针指向的结点:将这个节点复制成下一个节点的值,然后删除下一个节点

typedef struct LinkList {

         int Element;

         LinkList * next;

}LinkList;

初始化:

linklist *  List_init(){

    linklist *HeadNode= (linklist*)malloc(sizeof(linklist));

    if(HeadNode == NULL) {

        printf("空间缓存不足");

        return HeadNode;

    }

    HeadNode->Element= 0;

    HeadNode->next= NULL;

Return HeadNode;

}

创建链表:

void CreatList(linklist *HeadNode,int *InData,int DataNum)

{

    int i = 0;

    linklist *CurrentNode = (linklist*) HeadNode;

    for(i = 0;i<DataNum;i++)

    {

        CurrentNode->Element = InData[i];

        if(i< DataNum-1)// 由于每次赋值后需要新建结点,为了保证没有多余的废结点

        {

            CurrentNode->next =(linklist *)malloc(sizeof(linklist));

            CurrentNode= CurrentNode->next;

        }

    }

    CurrentNode->next= NULL;

}

插入节点:

bool InsertList(linklist *HeadNode,int LocateIndex,int InData){

    int i=1;// 由于起始结点HeadNode是头结点,所以计数从1开始

    linklist *CurrentNode= (linklist *) HeadNode;

    //CurrentNode指向待插入位置的前一个结点(index -1

    while(CurrentNode&& i<LocateIndex-1){

        CurrentNode= CurrentNode->next;

        i++;

    }

    linklist *NodeToInsert=(linklist*)malloc(sizeof(linklist));

    if(NodeToInsert == NULL){

        printf("空间缓存不足");

        return ERROR;

    }

    NodeToInsert->Element= InData;

    NodeToInsert->next = CurrentNode->next;

    CurrentNode->next = NodeToInsert;

    return OK;

}

具体参考:https://blog.csdn.net/u012531536/article/details/80170893

十一、内联函数

内联函数(inline

带参数的宏

编译(汇编)时展开 

预编译时替换

严格的参数类型检查   

简单的替换

函数体不能太大,不能包含大循环和递归

没要求

当编译器发现某段代码在调用一个内联函数时,它不是去调用该函数,而是将该函数的代码,整段插入到当前位置。这样做的好处是省去了调用的过程,加快程序运行速度。内联函数适用于函数体较小且频繁使用的函数,调用时进行代码的复制,无需跳转、入栈等操作,以空间换时间而且因为函数有严格的参数类型检查,比宏要安全;内联函数在编译时不单独产生代码  

posted @ 2019-03-18 14:32  小蚂蚁_CYJ  阅读(6961)  评论(0编辑  收藏  举报