zoukankan      html  css  js  c++  java
  • 《12个有趣的C语言问答》(4)

    C语言面试问答——《12个有趣的C语言问答》评析(4)

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

    8,Making changes in Code segment 
    Q:以下代码运行时一定会崩溃,你能说出原因吗? 

    #include<stdio.h>
     
    int main(void)
    {
        char *ptr = "Linux";
        *ptr = 'T';
     
        printf(" [%s] ", ptr);
     
        return 0;
    }

    A:这是因为,通过 *ptr = 'T',此行代码尝试更改只读内存存储的字符串'Linux'。此操作当然行不通所以才会造成崩溃。

    Answer: This is because, through *ptr = ‘T’, the code is trying to change the first byte of the string ‘Linux’ kept in the code (or the read-only) segment in the memory. This operation is invalid and hence causes a seg-fault or a crash.

    评:

      很难说这段代码一定崩溃(crash)或发生段错误(seg-fault)。
      在C标准中,修改String literal(The program attempts to modify a string literal)是一种UB(undefined behavior),即未定义行为。用通俗的话来讲,就是C语言没规定这样写有意义。这种情况下,什么事情都可能发生,无论发生什么,都是代码的错误,是程序员的责任。

      事实上,修改String literal可能导致各种结果,有些情况下也可能执行时并不产生崩溃或段错误。

      K&R认为修改String literal应该规定为unspecified behavior(不同于undefined behavior,unspecified behavior可能有多种结果,但代码本身不存在语义错误,而undefined behavior是没有语义),但标准委员会并没有采纳K&R的建议。

      对照一下原文,不难发现译文漏掉了in the code segment。

      但原文说string literal存于代码段或只读段,也是不正确的。这种说法没有依据。C语言并没有规定实现应该把string literal存放在什么地方,这是由实现自己安排的事情。

    9,Process that changes its own name Q:你能否写一个程序在它运行时修改它的名称? (Can you write a program that changes its own name when run?)
    A:以下代码可以完成 :
    Answer: Following piece of code tries to do the required : 

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

    评:

      这个问答比较雷人。

      首先标题是要改变Process的名字,问题是Process有名字吗?

      但在Question中又说要在运行时改Program的名字。如果说Program有名字,那么就只可能是对应可执行文件的名字。可执行文件的名字和Process的名字是无法划等号的。

      Process通常是通过PCB(Process Control Block)管理,其标识的方法通常是id号,当然,PCB中也有所谓外部标识符。如果把PCB的外部标识符理解为Process的名字,虽然极为牵强,但舍此似乎也无法再有任何其他解释了。

      查了一下,某些语言中似乎有Process Name这个概念,也有相关的函数。但是C语言中没有这个概念。

      再看代码,无非是修改了一下argv[0]所指向的字符串而已。尽管argv[0]指向的字符串确实是程序的名称,但这种改变没有实际意义——下次启动程序还得用原来的名字,因为外存中可执行文件的名字并没有改变。

      代码中的注释部分又提到了Process Name,就这样一会Process Name,一会又是Program Name,题目的设计者自己明显概念不清。

      注释说在这里模拟等待,搞不清究竟要在这里等什么。怎么看都觉得是在装模作样。
      再来看代码风格。 

    int i = 0;
     //……
    for(;i<0xffffffff;i++);

      把for语句分为两部分来写,风格乖张。

      把循环体部分的空语句写在行尾,也是一种恶习。对比一下下面的写法就知道了: 

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

      这条for语句本身的语义在一定也成问题。假如int类型的最大值为0x7fffffff,当i值达到int类型最大值之后,i再自加就溢出了。而溢出,是一种未定义行为。

      也就是说,这条语句说的很可能并不是让i值从0逐次加1一直变化到0xffffffff-1。代码作者心里想的究竟是什么,也许他自己都不知道。顺便说一下,int类型的最大值为0x7fffffff时,0xffffffff不是int类型,而是unsigned int类型。

    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);

      这段代码,依然是很蠢。首先莫名其妙地把buff清0: 

      memset(buff,0,sizeof(buff));

      实际上去掉这句没有任何问题。因为后面的 

      strncpy(buff, argv[0], sizeof(buff));

      还要再次填充。举个例子,下面代码段 

    char s[4];
    strncpy( s , "1" , 4);
    printf("%d %d %d %d ",s[0],s[1],s[2],s[3]);

    的输出,一定是 

    49 0 0 0

      这就说明,原来代码中memset()填充的0毫无意义,因为紧接着还要再填充一次。我不是说memset()没用,但到现在为止我看到的大多数memset()都是稀里糊涂地在做无用功,是在滥用memset()。再次说明了很多使用memset()的人根本不知道自己的代码究竟是在做什么。所谓对“大型商业程序,这是良好代码风格的习惯”云云,纯粹是胡扯。

      后面一句

    memset(argv[0],0,strlen(buff));

    不但没用,逻辑上也有点荒唐。应该

    memset(argv[0],0,strlen(argv[0]));<br>

    才合理。尽管我们知道,这里strlen(argv[0])和strlen(buff)的值是一样的。 

    strncpy(argv[0], "NewName", 7);

      这一句属于不懂装懂、煞有介事。作者使用strncpy()希望不至于拷贝越界,但这里的7是"NewName"的长度,而不是argv[0]所指向字符串的长度。如果strlen(argv[0])小于7,显然还会发生越界。 

      综上所述,这段代码不但不可能改变Program Name,连正确地修改命令行参数都谈不上,更不要说改变Process Name了。  

      所以,这个问答的“问”,是一个哗众取宠的伪问题。“答”则是生拉硬扯、胡编滥造的解答。

     
     
     
    标签: C语言面试
  • 相关阅读:
    r.js合并实践 --项目中用到require.js做生产时模块开发 r.js build.js配置详解
    javascript模块化编程 从入门到实战
    gulp、browsersync代理跨域
    TensorFlow 1.4利用Keras+Estimator API进行训练和预测
    python multiprocess pool模块报错pickling error
    python中用修饰器进行异常日志记录
    利用Laplacian变换进行图像模糊检测
    Keras查看model weights .h5 文件的内容
    python中利用redis构建任务队列(queue)
    Tensorflow 使用slim框架下的分类模型进行分类
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3327480.html
Copyright © 2011-2022 走看看