zoukankan      html  css  js  c++  java
  • 有意思的12道C面试题及答案

    这篇文章涉及到的都是一些C语言的基础知识,是从一篇英文文章中部分翻译和加入我自己的认识!!!!

    1. gets()函数
    Q:下面的代码中隐含着安全问题,能发现吗?

     1 #include<stdio.h>
     2 int main(void)
     3 {
     4   char buff[10];
     5   memset(buff,0,sizeof(buff));
     6 
     7   gets(buff);
     8 
     9   printf("\n The buffer entered is [%s]\n",buff);
    10 
    11   return 0;
    12 }

    A:问题在于gets()函数,这个函数是接收标准输入的一串字符串,并且没有检查字符串缓冲区的大小就
    直接拷贝到buff数组中,这可能导致在写入buff内存时溢出,可以使用fgets()函数代替这个函数,

    char *fgets( char *str,int n,FILE *stream );

    2.strcpy()函数
    Q:下面代码是一个密码验证的过程,是否可以在不知道密码的情况下验证通过

     1 #include<stdio.h>
     2 #include <string.h>
     3 
     4 int main(int argc, char *argv[])
     5 {
     6 
     7   char passwd[10];
     8   int flag = 0;
     9   memset(passwd,0,sizeof(passwd));
    10 
    11   strcpy(passwd, argv[1]);
    12 
    13   if(0 == strcmp("LinuxGeek", passwd))
    14   {
    15     flag = 1;
    16   }
    17 
    18   if(flag)
    19   {
    20     printf("\n Password cracked \n");
    21   }
    22   else
    23   {
    24     printf("\n Incorrect passwd \n");
    25 
    26   }
    27   return 0;
    28  }

    A:strcpy()函数没有验证输入的字符串长度,所有在执行时可能出现写内存出现溢出,这很危险,如这代码,
    flag是初始化为0的,当内存溢出时可能会写到flag内存中,这会使得flag内存不为0,即使不执行if语句的
    比较flag也为真,所以就相当于密码正确

    如: $ ./psswd aaaaaaaaaaaaa
    输出: Password cracked

    可以使用strncpy()函数代替
    现在编译器也发现这种情况,所以在为程序分配内存时是分散的分配内存,如果要看到上面执行的情况,使用gcc的话
    可以使用 ‘-fno-stack-protector’参数(我没验证)

    3.main()函数的返回类型
    Q:下面代码是否能编译通过?是否还存在其他问题

     1 #include<stdio.h>
     2 
     3 void main(void)
     4 {
     5   char *ptr = (char*)malloc(10);
     6 
     7   if(NULL == ptr)
     8   {
     9     printf("\n Malloc failed \n");
    10     return;
    11   }
    12   else
    13   {
    14     // Do some processing
    15 
    16     free(ptr);
    17   }
    18 
    19   return;
    20 }

    A:对于现在的编译器这段代码是可以编译通过的,不过是会有警告,main()返回类型最好使用int类型,
    当一个函数执行结束时最后返回一个状态值,现在C/C++返回一个0值表示程序正常退出,否则有异常.

    4.内存泄露
    Q:下面代码执行结果会出现内存泄露吗?

     1 #include<stdio.h>
     2 
     3 void main(void)
     4 {
     5   char *ptr = (char*)malloc(10);
     6 
     7   if(NULL == ptr)
     8   {
     9     printf("\n Malloc failed \n");
    10     return;
    11   }
    12   else
    13   {
    14     // Do some processing
    15   }
    16 
    17   return;
    18 }

    A:其实内存泄露是个很严重的问题,其实上面代码执行结果不会出现内存泄露,虽然没有使用free()
    回收内存,但是当程序执行结束后程序里分配的内存会自动释放,如果是分配的内存放到一个死循环里
    就会出现严重的内存泄露,或者程序一直执行着,动态分配的内存会一直占有着无法释放.
    有篇文章介绍了内存泄露的检测方法:http://www.cnblogs.com/skynet/archive/2011/02/20/1959162.html

    5.free()函数
    Q:下面代码执行时输入如 ‘freeze’会崩溃但输入如t ‘zebra’就不会为什么?

     1 #include<stdio.h>
     2 
     3 int main(int argc, char *argv[])
     4 {
     5     char *ptr = (char*)malloc(10);
     6 
     7     if(NULL == ptr)
     8     {
     9         printf("\n Malloc failed \n");
    10         return -1;
    11     }
    12     else if(argc == 1)
    13     {
    14         printf("\n Usage  \n");
    15     }
    16     else
    17     {
    18         memset(ptr, 0, 10);
    19 
    20         strncpy(ptr, argv[1], 9);
    21 
    22         while(*ptr != 'z')
    23         {
    24             if(*ptr == '')
    25                 break;
    26             else
    27                 ptr++;
    28         }
    29 
    30         if(*ptr == 'z')
    31         {
    32             printf("\n String contains 'z'\n");
    33             // Do some more processing
    34         }
    35 
    36        free(ptr);
    37     }
    38 
    39     return 0;
    40 }

    A:这个问题主要是指针移到的问题,当输入如‘zebra’这样的字符串('z'开头)时,ptr指针没有移到,所以
    ptr指针指向的内存还是malloc分配的原内存的起始地址,但是输入‘freeze’时,ptr指针移到了,已经不是指向原来
    分配的内存的起始地址了,所有free时就会出错

    题外话:在实现如strcpy的函数时,如下

     1 char *strcpy(char *strDest, const char *strSrc)
     2 {
     3     assert((strDest != NULL) && (strSrc != NULL));
     4     
     5     if(strDest == strSrc)
     6     return strDest; //注意这个.. 
     7 
     8     char *pstr = strDest; //保存原始地址
     9     while((*strDest++ = *strSrc++) != '\0');
    10     return pstr;
    11 }

    定义了一个指针pstr保存了原始地址,然后再执行移到操作,结束后再返回写入的原始地址以便进行链式操作,
    我实现这个函数的时候就是出现了一个很二的错误,就是移到操作后直接返回strDest指针,现在的strDest指针
    已经移到字符串末尾了,如果那样的话要释放strDest内存就会出错了

    6.atexit和_exit
    Q:下面代码中,atexit()函数没有被调用,why?

     1 #include<stdio.h>
     2 void func(void)
     3 {
     4     printf("\n Cleanup function called \n");
     5     return;
     6 }
     7 
     8 int main(void)
     9 {
    10     int i = 0;
    11 
    12     atexit(func);
    13 
    14     for(;i<0xffffff;i++);
    15 
    16     _exit(0);
    17 }

    A:主要是因为_exit()函数,这个函数是直接终止程序,没有调用一些如atexit()的清理函数,要想调用
    atexit()函数,就需要调用exit()函数或者return返回操作退出程序.

    MSDN给出了exit()和_exit()函数的区别

    The exit and _exit functions terminate the calling process. exit calls, in last-in-first-out (LIFO) order, 
    the functions registered by atexit and _onexit, then flushes all file buffers before terminating the process. _exit terminates the process without processing atexit or _onexit or flushing stream buffers. The status value is typically set to 0 to indicate a normal exit and set to some other value to indicate an error

    7.void* 和 C 结构体
    Q:能否设计一个函数能够接受任何类型的参数? 也可以传递多个参数给这个函数?
    A:

    int func(void *ptr);

    传递的时候需要强制转换为void*类型,到函数内以后再强制转换回来,如果要传多个参数,可以定义一个
    结构体,将要传递的参数放到结构体里,定义一个结构体对象,对成员赋值后,将对象传给函数.
    注:我不知道在哪里看到有人说C/C++是不安全的语言,因为可以类型强制转换,但在这里发现强制转换
    带来的好处.


    8. * 和 ++ 操作
    Q:下面的代码输出的结果是什么? and Why?

     1 #include<stdio.h>
     2 
     3 int main(void)
     4 {
     5     char *ptr = "Linux";
     6     printf("\n [%c] \n",*ptr++);
     7     printf("\n [%c] \n",*ptr);
     8 
     9     return 0;
    10 }

    A:输入的结果:
    [L]
    [i]
    Why? 这个涉及到*号和++的优先级问题,*和++的优先级是一样的,编译器在识别时是先右后左,
    所有先检测到++后到*,不过++是在ptr后面,所以自加在*ptr后执行,输出L,然后移动到i处,下
    一条语句执行的时候输出i.
    原作者给出来的解释我不太理解
    (ptr++ is evaluated first and then *ptr. So both these operations result in ‘L’),
    为什么说两个操作结果都是'L'???

    9.改变代码区(只读区)
    Q:下面代码为什么会崩溃?

     1 #include<stdio.h>
     2 
     3 int main(void)
     4 {
     5     char *ptr = "Linux";
     6     *ptr = 'T';
     7 
     8     printf("\n [%s] \n", ptr);
     9 
    10     return 0;
    11 }

    A:因为 *ptr = 'T' 操作尝试改变在代码区的 "Linux"的字符串,这是不合法的,其实如果要改变的话
    可以先动态分配一块内存(在堆区),然后再Copy "Linux"到这块内存中,可以就可以执行*ptr = 'T'操作

    10.程序改变自己的名字
    Q:如何实现一个程序在运行的时候改变自己的名字
    A:这个需要知道main()函数的两个参数的意义了

     1 #include<stdio.h>
     2 #include <string.h>
     3 int main(int argc, char *argv[])
     4 {
     5     int i = 0;
     6     char buff[100];
     7 
     8     memset(buff,0,sizeof(buff));
     9 
    10     strncpy(buff, argv[0], sizeof(buff));
    11     memset(argv[0],0,strlen(buff));
    12 
    13     strncpy(argv[0], "NewName", 7);
    14 
    15     // Simulate a wait. Check the process
    16     // name at this point.
    17     for(;i<0xffffffff;i++);
    18 
    19     return 0;
    20 }

    main()函数的argc表示的是传进来的参数个数,argv[]保存的是参数的内容,但是
    argv[0]保存的是程序自己名字

    11.返回局部变量地址
    Q:下面的代码是否存在问题?如果有的话那如何修改?

     1 #include<stdio.h>
     2 
     3 int* inc(int val)
     4 {
     5   int a = val;
     6   a++;
     7   return &a;
     8 }
     9 
    10 int main(void)
    11 {
    12     int a = 10;
    13 
    14     int *val = inc(a);
    15 
    16     printf("\n Incremented value is equal to [%d] \n", *val);
    17 
    18     return 0;
    19 }

    A:问题是返回了一个局部变量的地址,a的作用域只在inc()函数中,函数结束后a的内存会释放,如果使用
    一个已经被释放了的内存相当危险,解决方法可以传给inc()函数的参数修改为传地址或者引用(C++)
    不使用值传递,int* inc(int *val)或者int* inc(int &val)

    12.printf()函数参数的执行
    Q:代码的输出结果是什么?

     1 #include<stdio.h>
     2 
     3 int main(void)
     4 {
     5     int a = 10, b = 20, c = 30;
     6 
     7     printf("\n %d..%d..%d \n", a+b+c, (b = b*2), (c = c*2));
     8 
     9     return 0;
    10 }

    A:输出的结果为
    110..40..60
    函数的参数是从右到左执行的,但是打印是从左到右的

    这篇文章加入了我的一些表述,可能存在不足或者不对的表达,如有兴趣可以读原作者的英文文章

    http://www.thegeekstuff.com/2012/08/c-interview-questions/

  • 相关阅读:
    HDU 5795 A Simple Nim ——(Nim博弈 + 打表)
    【Insertion Sorted List】cpp
    【Merge K Sorted Lists】cpp
    【Merge Two Sorted Lists】cpp
    【Merge Sorted Array】cpp
    【Sum Root to Leaf Numbers】cpp
    【Binary Tree Maximum Path Sum】cpp
    【Path Sum II】cpp
    【Path Sum】cpp
    【Maximum Depth of Binary Tree 】cpp
  • 原文地址:https://www.cnblogs.com/bizhu/p/2674225.html
Copyright © 2011-2022 走看看