《12个有趣的C语言问答》评析(2)

前文链接:http://www.cnblogs.com/pmer/p/3313913.html

(没存盘,遭遇过热保护。至少4个问答的评论白写了。默哀一下)

2,main() 方法的返回类型 
Q:请问下面这段代码能否通过编译?如果能的话,那么这段代码中隐含什么问题吗?

#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;
}

A:答案是代码能通过编译,但是会留下针对main()方法的返回类型的警告。main()方法的真正返回类型应该为'int'而非'void'。这是因为'int'返回类型能够让程序返回状态值。尤其是当这段程序作为其他应用的附属程序时这个状态值将更加重要。

Answer: The code will compile error free but with a warning (by most compilers) regarding the return type of main()function. Returntype of main() should be ‘int’ rather than ‘void’. This is because the ‘int’ return type lets the program to return a status value. This becomes important especially when the program is being run as a part of a script which relies on the success of the program execution.

评:

  很难说这段代码能否通过编译,因为void main(void)根本就不是标准中规定的C程序的基本形式。诚然,在很多编译器上这段代码都能“带病”通过编译,但

不能保证在所有编译器上都能通过。C语言没有给出可以通过编译的承诺,通过编译只能理解为编译器擅自放水而已。
  写到这里发现,其实原文中有这层意思(“by most compilers”),但翻译者却偷工减料,歪曲了原文的意思。
  下风格问题:
  代码中的写了一个if语句,怪异的是if字句中有return语句而else字句中没有,两条return语句一脚门里一脚门外。下面两种写法要好得多:

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

3,内存泄露

Q:请问以下代码有内存泄露吗?

#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;
}

A:好,虽然上面的代码没有对指针 ptr 进行内存释放,但实际上即使是程序结束也不会造成内存泄露,因为当程序结束时所有一开始被占据的内存就全部清空了。但如果上面这段代码是在 while 循环里面那将会造成严重的问题。
Answer: Well, Though the above code is not freeing up the memory allocated to ‘ptr’ but still this would not cause a memory leak asafter the processing is done the program exits. Since the program terminates so all the memory allocated by the program isautomatically freed as part of cleanup. But if the above code was all inside a while loop then this would have caused serious memoryleaks.

Note: 如果你需要了解更多关于内存泄露的问题以及如何使用工具检测内存泄露,你可以参考这篇文章 Valgrind

评:

  这段问答的第一个毛病是,main()函数不规范。前一个问答中作者把void main( void )写法否定了,这个问答中却自己这样写,自掌嘴巴。
  诚然,这段代码确实不存在内存泄漏,但代码中自己不free,却把这个本该由程序员完成的活交给系统,这是很不足取的。尽管没有内存泄漏,但free还是应该写。
  代码中另一个问题就是return语句一脚门里一脚门外的问题,前面说过,不再赘述。

4,free() 方法 
Q:以下代码当用户输入'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;
}

A:问题的根源是因为代码在while循环中改变了 ptr 指针的地址。当输入为'zebra'时,while循环甚至在执行 第一遍前就结束了,所以free()释放的内存地址就是一开始malloc()分配的地址。但是当输入'freeze'时, ptr记录的地址在while循环中被更改,因为将会是错误的地址传递到free()方法中引起崩溃。
Answer: The problem here is that the code changes the address in ‘ptr’ (by incrementing the ‘ptr’) inside the while loop. Now when ‘zebra’ is supplied as input, the while loop terminates before executing even once and so the argument passed to free() is the same address as given by malloc(). But in case of ‘freeze’ the address held by ptr is updated inside the while loop and hence incorrect address is passed to free() which causes the seg-fault or crash.

评:

  Answer中的解释没什么问题。但造成问题更主要的原因其实是把所有代码都写在了main()中。如果其中

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
}

部分的功能由一函数完成,那么根本不会产生这样的问题。

  翻译的毛病很多:

  “改变了ptr指针的地址”,其实原意是 改变了ptr中(存放)的地址。

  “因为将会是错误的地址传递到free()方法中引起崩溃”,这简直就不是中国话,另外漏翻了“seg-fault”。

  风格方面依然存在return语句一脚门里一脚门外的毛病。其实存在多个return语句时,把if-else写成多个if通常显得更清爽。

return -1;

是非标准写法,应该是编译器的方言。

memset(ptr, 0, 10);

一句,啰嗦,头脑不清。但在这个例子里,由于是通过malloc()申请的内存,所以当strlen(argv[1])大于等于9时,memset(ptr, 0, 10);会起到提前设置ptr[9]为\0的效果。但要实现这点其实简单地

ptr[9]='\0'

就可以了,没必要兴师动众地调用memset()函数。

  如果一定要将所申请内存中的各个数据对象初始化为0,其实可以通过

char *ptr = (char*)calloc(10,1);

简单地实现。

  和malloc()不同calloc()申请的内存中的数据初始化为0。而且,如果是为数组申请空间,calloc()也显得比malloc()更正式一点。

(未完待续)

 
 
 
标签: C语言面试

《12个有趣的C语言问答》评析2的更多相关文章

  1. 《12个有趣的C语言问答》(4)

    C语言面试问答——<12个有趣的C语言问答>评析(4) 前文链接:http://www.cnblogs.com/pmer/p/3324063.html 8,Making changes i ...

  2. 12个有趣的C语言问答(详解)

    本文参照博文<12个有趣的C语言问答>,在原文的基础上增加来对应的知识点的详细介绍. 1 gets()方法 Q:下面的代码有一个被隐藏的问题,你能找到它吗? #include <st ...

  3. 12个滑稽的C语言面试问答——《12个有趣的C语言问答》评析(5)

    前文链接:http://www.cnblogs.com/pmer/archive/2013/09/17/3327262.html A,局部变量的返回地址 Q:下面的代码有问题吗?如果有,如何修改? # ...

  4. 12个有趣的C语言问答

    转自:http://www.admin10000.com/document/913.html 1,gets() 方法 Q:以下代码有个被隐藏住的问题,你能找到它吗? 1 2 3 4 5 6 7 8 9 ...

  5. 12个有趣的c语言面试题&nbsp;

    1.gets()函数 问:请找出下面代码里的问题: #include int main(void) { char buff[10]; memset(buff,0,sizeof(buff)); gets ...

  6. 12个有趣的C语言面试题

    摘要:12个C语言面试题,涉及指针.进程.运算.结构体.函数.内存,看看你能做出几个! 1.gets()函数 问:请找出下面代码里的问题: #include<stdio.h> int ma ...

  7. Ubuntu 12.04上安装R语言

    Ubuntu 12.04上安装R语言 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ R的安装 sudo gedit /etc/apt/sources. ...

  8. 12个有趣的c面试题目

    1.gets()函数 问:请找出以下代码里的问题: #include<stdio.h>  int main(void)  {      char buff[10];      memset ...

  9. 一个有趣的C语言问题

    这个问题是知乎上的一个问题,看了以后觉得比较有意思.代码短到只有十多行,但是这么短的代码却输出了很奇怪的结果.很多人回答的时候都是站在理论的角度上说明代码的问题,但是实际的问题还是没有说明其中的问题. ...

随机推荐

  1. 教你一步一步部署.net免费空间OpenShift系列之二------创建应用

    接上回书,注册完毕后需要在Openshift上创建一个应用空间,如如何创建空间呢,Openshift本身是不直接支持Mono来部署ASP.Net程序的,需要借助openshift-community- ...

  2. 【百度地图API】——如何用label制作简易的房产标签

    原文:[百度地图API]--如何用label制作简易的房产标签 摘要: 最近,API爱好者们纷纷说,自定义marker太复杂了!不仅定义复杂,连所有的dom事件都要自己重新定义.有没有快速简易创建房产 ...

  3. 【百度地图API】如何制作孪生姐妹地图?

    原文:[百度地图API]如何制作孪生姐妹地图? 任务描述: 我想要两张一模一样的地图!我想要双子地图!我想要孪生姐妹地图! 好好好,统统满足大家! 在这里我不需要使用百度地图API提供的地图缩略图控件 ...

  4. ASP.NET 5:依赖注入

    ASP.NET 5:依赖注入 1.背景 如果某个具体的(或类)对象被客户程序所依赖,通常把它们抽象成抽象类或接口.简单说,客户程序摆脱所依赖的具体类型,称之为面向接口编程. 那么问题来了?如何选择客户 ...

  5. angularJS之使用指令封装DOM操作

    angularJS之使用指令封装DOM操作 创建指令 指令也是一种服务,只是这种服务的定义有几个特殊要求: 必须使用模块的directive()方法注册服务 必须以对象工厂/factory()方法定义 ...

  6. ARM指令集中经常使用的存储和载入指令

    ARM微处理器支持载入/存储指令用于在寄存器和存储器之间传送数据,载入指令用于将存储器中的数据传送到寄存器,存储指令则完毕相反的操作.经常使用的载入存储指令例如以下: -  LDR     字数据载入 ...

  7. MVC验证02-自定义验证规则、邮件验证

    原文:MVC验证02-自定义验证规则.邮件验证 本文体验MVC自定义验证特性,来实现对邮件的验证.对于刚写完的自定义验证特性,起初只能支持后端验证.如果要让前端jquery支持,还必须对jquery的 ...

  8. JS时间戳比较大小:对于一组时间戳(开始时间~结束时间)和另一组时间戳进行比较,用于判断被比较时间戳组是否在要求范围内

    /* *JS时间戳比较大小:对于一组时间戳(开始时间~结束时间)和另一组时间戳进行比较,用于判断被比较时间戳组是否在要求范围内 *@param date1 date2(形如:'2015-01-01'类 ...

  9. svg的自述

    svg可缩放矢量图形(Scalable Vector Graphics). SVG 使用 XML 格式定义图像. SVG 是使用 XML 来描述二维图形和绘图程序的语言. 什么是SVG? SVG 指可 ...

  10. Cocos2d-x 2.3.3版本 FlappyBird

    Cocos2d-x 2.3.3版本 FlappyBird   本篇博客基于Cocos2d-x 2.3.3, 介绍怎样开发一款之前非常火的一款游戏FlappyBird.本篇博客内容大纲例如以下:   1 ...