1.gets()函数

问:请找出下面代码里的问题:

#include<stdio.h> 
int main(void) 

    char buff[10]; 
    memset(buff,0,sizeof(buff)); 
 
    gets(buff); 
 
    printf("\n The buffer entered is [%s]\n",buff); 
 
    return 0; 

答:上面代码里的问题在于函数gets()的使用,这个函数从stdin接收一个字符串而不检查它所复制的缓存的容积,这可能会导致缓存溢出。这里推荐使用标准函数fgets()代替。

2.strcpy()函数

问:下面是一个简单的密码保护功能,你能在不知道密码的情况下将其破解吗?

#include<stdio.h> 
 
int main(int argc, char *argv[]) 

    int flag = 0; 
    char passwd[10]; 
 
    memset(passwd,0,sizeof(passwd)); 
 
    strcpy(passwd, argv[1]); 
 
    if(0 == strcmp("LinuxGeek", passwd)) 
    { 
        flag = 1; 
    } 
 
    if(flag) 
    { 
        printf("\n Password cracked \n"); 
    } 
    else 
    { 
        printf("\n Incorrect passwd \n"); 
 
    } 
    return 0; 

答:破解上述加密的关键在于利用攻破strcpy()函数的漏洞。所以用户在向“passwd”缓存输入随机密码的时候并没有提前检查“passwd”的容量是否足够。所以,如果用户输入一个足够造成缓存溢出并且重写“flag”变量默认值所存在位置的内存的长“密码”,即使这个密码无法通过验证,flag验证位也变成了非零,也就可以获得被保护的数据了。例如:

$ ./psswd aaaaaaaaaaaaa 
 
Password cracked 
虽然上面的密码并不正确,但我们仍然可以通过缓存溢出绕开密码安全保护。

要避免这样的问题,建议使用 strncpy()函数。

作者注:最近的编译器会在内部检测栈溢出的可能,所以这样往栈里存储变量很难出现栈溢出。在我的gcc里默认就是这样,所以我不得不使用编译命令‘-fno-stack-protector’来实现上述方案。

3.main()的返回类型

问:下面的代码能 编译通过吗?如果能,它有什么潜在的问题吗?

#include<stdio.h> 
 
void main(void) 

    char *ptr = (char*)malloc(10); 
 
    if(NULL == ptr) 
    { 
        printf("\n Malloc failed \n"); 
        return; 
    } 
    else 
    { 
        // Do some processing 
        free(ptr); 
    } 
 
    return; 

答:因为main()方法的返回类型,这段代码的错误在大多数编译器里会被当作警告。main()的返回类型应该是“int”而不是“void”。因为“int”返回类型会让程序返回状态值。这点非常重要,特别当程序是作为依赖于程序成功运行的脚本的一部分运行时。

4.内存泄露

问:下面的代码会导致内存泄漏吗?

#include<stdio.h> 
 
void main(void) 

    char *ptr = (char*)malloc(10); 
 
    if(NULL == ptr) 
    { 
        printf("\n Malloc failed \n"); 
        return; 
    } 
    else 
    { 
        // Do some processing 
    } 
 
    return; 

答:尽管上面的代码并没有释放分配给“ptr”的内存,但并不会在程序退出后导致内存泄漏。在程序结束后,所有这个程序分配的内存都会自动被处理掉。但如果上面的代码处于一个“while循环”中,那将会导致严重的内存泄漏问题!

提示:如果你想知道更多关于内存泄漏的知识和内存泄漏检测工具,可以来看看我们在Valgrind上的文章。

5.free()函数

问:下面的程序会在用户输入'freeze'的时候出问题,而'zebra'则不会,为什么?

#include<stdio.h> 
 
int main(int argc, char *argv[]) 

    char *ptr = (char*)malloc(10); 
 
    if(NULL == ptr) 
    { 
        printf("\n Malloc failed \n"); 
        return -1; 
    } 
    else if(argc == 1) 
    { 
        printf("\n Usage  \n"); 
    } 
    else 
    { 
        memset(ptr, 0, 10); 
 
        strncpy(ptr, argv[1], 9); 
 
        while(*ptr != 'z') 
        { 
            if(*ptr == '') 
                break; 
            else 
                ptr++; 
        } 
 
        if(*ptr == 'z') 
        { 
            printf("\n String contains 'z'\n"); 
            // Do some more processing 
        } 
 
       free(ptr); 
    } 
 
    return 0; 

答:这里的问题在于,代码会(通过增加“ptr”)修改while循环里“ptr”存储的地址。当输入“zebra”时,while循环会在执行前被终止,因此传给free()的变量就是传给malloc()的地址。但在“freeze”时,“ptr”存储的地址会在while循环里被修改,因此导致传给free()的地址出错,也就导致了seg-fault或者崩溃。

6.使用_exit退出

问:在下面的代码中,atexit()并没有被调用,为什么?

#include<stdio.h> 
 
void func(void) 

    printf("\n Cleanup function called \n"); 
    return; 

 
int main(void) 

    int i = 0; 
 
    atexit(func); 
 
    for(;i<0xffffff;i++); 
 
    _exit(0); 

这是因为_exit()函数的使用,该函数并没有调用atexit()等函数清理。如果使用atexit()就应当使用exit()或者“return”与之相配合。

7.void*和C结构体

问:你能设计一个能接受任何类型的参数并返回interger(整数)结果的函数吗?

答:如下:

int func(void *ptr) 
如果这个函数的参数超过一个,那么这个函数应该由一个结构体来调用,这个结构体可以由需要传递参数来填充。

8.*和++操作

问:下面的操作会输出什么?为什么?

#include<stdio.h> 
 
int main(void) 

    char *ptr = "Linux"; 
    printf("\n [%c] \n",*ptr++); 
    printf("\n [%c] \n",*ptr); 
 
    return 0; 

答:输出结果应该是这样:

[L]  
 
[i] 
因为“++”和“*”的优先权一样,所以“*ptr++”相当于“*(ptr++)”。即应该先执行ptr++,然后才是*ptr,所以操作结果是“L”。第二个结果是“i”。

9.问:修改代码片段(或者只读代码)

问:下面的代码段有错,你能指出来吗?

#include<stdio.h> 
 
int main(void) 

    char *ptr = "Linux"; 
    *ptr = 'T'; 
 
    printf("\n [%s] \n", ptr); 
 
    return 0; 

答:这是因为,通过*ptr = ‘T’,会改变内存中代码段(只读代码)“Linux”的第一个字母。这个操作是无效的,因此会造成seg-fault或者崩溃。

10.会改变自己名字的进程

问:你能写出一个在运行时改变自己进程名的程序吗?

答:参见下面这段代码:

#include<stdio.h> 
 
int main(int argc, char *argv[]) 

    int i = 0; 
    char buff[100]; 
 
    memset(buff,0,sizeof(buff)); 
 
    strncpy(buff, argv[0], sizeof(buff)); 
    memset(argv[0],0,strlen(buff)); 
 
    strncpy(argv[0], "NewName", 7); 
 
    // Simulate a wait. Check the process 
    // name at this point. 
    for(;i<0xffffffff;i++); 
 
    return 0; 

11.返回本地变量的地址

问:下面代码有问题吗?如果有,该怎么修改?

#include<stdio.h> 
 
int* inc(int val) 

  int a = val; 
  a++; 
  return &a; 

 
int main(void) 

    int a = 10; 
    int *val = inc(a); 
    printf("\n Incremented value is equal to [%d] \n", *val); 
 
    return 0; 

答:尽管上面的程序有时候能够正常运行,但是在“inc()”中存在严重的漏洞。这个函数返回本地变量的地址。因为本地变量的生命周期就是“inc()”的生命周期,所以在inc结束后,使用本地变量会发生不好的结果。这可以通过将main()中变量“a”的地址来避免,这样以后还可以修改这个地址存储的值。

12.处理printf()的参数

问:下面代码会输出什么?

#include<stdio.h> 
 
int main(void) 

    int a = 10, b = 20, c = 30; 
    printf("\n %d..%d..%d \n", a+b+c, (b = b*2), (c = c*2)); 
 
    return 0; 

答:输出结果是:

110..40..60 
这是因为C语言里函数的参数默认是从右往左处理的,输出时是从左往右。

12个有趣的c面试题目相关推荐

  1. 12种排序算法:原理、图解、动画视频演示、代码以及笔试面试题目中的应用

    0.前言 从这一部分开始直接切入我们计算机互联网笔试面试中的重头戏算法了,初始的想法是找一条主线,比如数据结构或者解题思路方法,将博主见过做过整理过的算法题逐个分析一遍(博主当年自己学算法就是用这种比 ...

  2. c语言滑稽的编程,12个滑稽的C语言面试问答——《12个有趣的C语言问答》评析(5)...

    A,局部变量的返回地址 Q:下面的代码有问题吗?如果有,如何修改? #include int* inc(int val) { int a = val; a++; return &a; } in ...

  3. C语言经典面试题目(转的,不过写的的确好!)

    第一部分:基本概念及其它问答题 1.关键字static的作用是什么? 这个简单的问题很少有人能回答完全.在C语言中,关键字static有三个明显的作用: 1). 在函数体,一个被声明为静态的变量在这一 ...

  4. 世界五百强面试题目及应答评点

    世界五百强面试题目及应答评点 作者:深圳的小屋    文章来源:天涯虚拟社区      更新时间:2004-12-3       问题1             你为什么觉得自己能够在这个职位上取得成 ...

  5. 嵌入式工程师的经典面试题目及答案

    上个星期,去深圳一家搞ARM开发的公司面试,HR叫我做了一份卷子,里面都是C编程,心中暗喜,因为这些题基本上都在程序员面试宝典里见过.后来回到学校,在网上搜索,原来这些题都是嵌入式工程师的经典面试题目 ...

  6. C/C++面试题目汇总

    C/C++面试题目汇总 最近因为找工作,收集了很多C语言方面方面的面试题以及答案.现在新工作搞定了,决定把这些资料发出来,送给有需要的朋友,免得再象我一样到处搜寻,实在辛苦. 发布之前先申明两点:   ...

  7. 嵌入式工程师的经典面试题目

    嵌入式工程师的经典面试题目 预处理器(Preprocessor) 1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)       #define  SECONDS ...

  8. 世界五百强面试题目及应答评点(50题)

    分享一下我老师大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow 世界五百强面试题目 ...

  9. python面试题目

    python面试题目 原文地址:https://www.usblog.cc/blog/post/justzhl/b5cc9a05c7d2 问题一:以下的代码的输出将是什么? 说出你的答案并解释. ? ...

最新文章

  1. IDEA Java解析GeoJson.json文件
  2. MongoDB日志切换(Rotate Log Files)指南
  3. Android之循环执行次数のHandlerRunnable
  4. [Django]网页中利用ajax实现批量导入数据功能
  5. [转] 理解SVG transform坐标变换
  6. 使用 Nginx 提升网站访问速度(转)
  7. P2872 [USACO07DEC]Building Roads S(最小生成树)
  8. 服务器java项目转移到另一个盘_将svn的项目从一台服务器转移到另外一台服务器...
  9. redis 命令别名_redis 命令、命令行根据前缀(通配符)批量删除redis存储的key
  10. 【GNN】图网络|图神经网络(GNN)结构化数据分析
  11. python调用百度查询关键字_Python模拟搜索百度关键字
  12. 沉没的王国---揭秘滇东自杞国(1)
  13. 动力电池SOC估算方法综述
  14. 虾米带你轻松搞定Vuejs 系列
  15. 计算机知识竞赛口号,知识竞赛比赛口号大全
  16. 云虚拟主机数据库连接和url重写
  17. R7-17 程序填空题2
  18. 数据中心光纤布线的发展趋势
  19. 计算机课怎样制作ppt 课件ppt,计算机多媒体课件制作.ppt
  20. [转贴]八岁女童墓志铭:我来过,我很乖~~~~~ (是个人就流泪)

热门文章

  1. Android 高级UI解密 (三) :Canvas裁剪 与 二维、三维Camera几何变换(图层Layer原理)
  2. fatal: unable to connect to gitlab.domian: gitlab.domian : Temporary failure in name resolution
  3. 如何运行PHP文件 /创建PHP项目【基于VScode、XAMPP】超级详细,亲测有效,这一篇就够了
  4. Handling Complexity in the Halo 2 AI
  5. NOIP模拟 葫芦(分数规划)
  6. FX:{6630f2d7-bd52-4072-bfa7-863f3d0c5da0}
  7. 多硬盘分区管理fdisk
  8. Wifi-direct 相关记录
  9. 世人谓我太疯癫,我笑世人看不穿
  10. 【LeetCode】643. 子数组最大平均数 I