精华内容
下载资源
问答
  • 一个有趣的C语言代码

    万次阅读 2013-03-20 16:14:58
    #include long x[]={1, 3,6,8,0,0,0,0, 32,32, 4,32, 1022,32,1020,36 ,32,1022, 34,508,32 , 32,509,34 , 508,32,36, 32,0,32,36,1022,508,80, 1023,
     #include                     <stdio.h>
         long x[]={1,             3,6,8,0,0,0,0, 32,32,
     4,32, 1022,32,1020,36        ,32,1022, 34,508,32 ,
     32,509,34 , 508,32,36,      32,0,32,36,1022,508,80,
            1023,               32,260              ,136,
       32,    32 ,   508 ,      260,32              , 32,
      0404,  514,   32,32       ,0,0,0              ,0x0,
      994 ,  0466,   0772,      47,548,950, 168, 296, 559,
     694,69  ,174,  0x228,      0x3B6 , 01646,0141,01744,
      01266  ,0124   ,2033
     ,142,   01666   ,1010    ,175, 149,566,67,168,708,1006,
     1018,  296,676  ,01055   ,66,548,788,812,66,100,0,0,00,
     0,698,  33856,  0,8466   ,16929,     33831,      64512,
     8466,   16930,  33824,0  ,56882,     19908,      39872,
    0};int   main(   int _1   ,char * *_2 ) { int _3,_4;(_3= 
     1<<2,   _1^2)   ?main    (_1^1L?--_1:((1L<<4)+(1L<<3)),
             _2):0            ;while      (_4=0,      _3 --)
             while            (putchar    (*(x+(      _1<<2)
             +_3)&            1<<_4?(1<<5)+(1<<2):1<<5),++_4
             <1<<             4); return putchar (1+2+3+4);}
    展开全文
  • 这里是需要突出显示内容   春节十二响这个梗 01 - Spring12下载 02 - Spring12分析 03 - Spring12扩展 04 - 如何用Scratch实现Spring12    GitHub源码 修改后GitHub源码 ...


    代码千万条,注释第一条

      春节十二响这个梗出自《流浪地球》电影中一个名为李一一(不是横线,是一二三的一,还是两个)的程序员在最后修改发动机喷射形式的时候提到的内容,因为背景及现场气氛的共同作用,这个内容成为这部电影令人印象深刻的情节之一
      在电影播出后不久,由于电影中李一一程序员的身份,进而激起了现实众多程序员博主纷纷出动模拟春节十二响代码,其中一位博主在GitHub中开源了C语言伪代码,不能编译运行,但是目前得到了 180+star(在GitHub中获得别人的star是非常困难的),下面一起分析这个 spring12
    Alt

    01 - Spring12下载

      点击spring12-GitHub源码进入页面,点击clone or download就可以下载zip压缩包,解压后得到文件
    Alt
      因为是伪代码,所以只有一个.c程序,这个程序的名字很搞笑,还引来众多程序员一本正经地讨论,具体可以看issues

    02 - Spring12分析

      在查看spring12的时候,很多人都想去编译运行,但是发现编译器报出一堆错误,因为这个spring12只是伪代码,打开spring12 ,代码量非常少

    #env "planet_engine"
    
    int init() {
        set_engine_number_mask(ENGINE_ALL);
        set_funeral_level(FUNERAL_FULL);
        // 允许误差10秒以内
        if (unix_time() < make_unix_time(2082, 1, 28, 23, 59, 60-10)) return ERR_ENGIN_ENV;
        return engine_check_init(); // after compile and before real run
    }
    int main() {
        set_curve(CURVE_NATURAL); // 自然曲线耗费燃料最少
        for (int i :range(0, 12, 1)) {
            engine_start();
            wait_engine(ENGINE_STATE_CHAGNE);
            sleep(2000);
            engin_stop();
            wait_engine(ENGINE_STATE_CHAGNE);
            sleep(4000); // 这个时长在模拟器里听起来更像心跳
        }
        return 0;
    }
    int final() {
        engine_ensure_shutdown();
    }
    

      这位博主在必要的地方写了注释,整段代码看下来,更像是看了一篇短文一样:声明环境、初始化发动机、开启发动机12响、关闭发动机,有趣的是,代码中还混了Python的语法

    2.1 - 声明环境

    #env "planet_engine"
    

      从这一句就可以看出这段代码是伪代码,这里声明程序运行环境位于"行星发动机"内,于是程序中所有的API都应该来自行星发动机SDK,所以如果你想编译运行,就请先找到SDK(做梦吧)

    2.2 - 初始化发动机

    int init() {
        set_engine_number_mask(ENGINE_ALL);
        set_funeral_level(FUNERAL_FULL);
        // 允许误差10秒以内
        if (unix_time() < make_unix_time(2082, 1, 28, 23, 59, 60-10)) return ERR_ENGIN_ENV;
        return engine_check_init(); // after compile and before real run
    }
    

      程序并不是严格按照C语言风格从main()开始,而是按照Python从init()开始,因为main()中没有显式调用init(),所以接下来运行init(),进行的步骤有:

    • 设置发动机掩码,准备开启所有发动机
    • 设置危险级别,所有发送机危险级别置为最高
    • 检查当前时间,发动机将在2082年1月28日23小时59分钟第50~60秒内开启,当前时间的误差必须在此范围内
    • 检查发动机状态,即将开启发动机

    2.3 - 启动发动机12响

    int main() {
        set_curve(CURVE_NATURAL); // 自然曲线耗费燃料最少
        for (int i :range(0, 12, 1)) {
            engine_start();
            wait_engine(ENGINE_STATE_CHAGNE);
            sleep(2000);
            engin_stop();
            wait_engine(ENGINE_STATE_CHAGNE);
            sleep(4000); // 这个时长在模拟器里听起来更像心跳
        }
        return 0;
    }
    

      到C语言的main()函数了,先是设置燃料耗费曲线为自然曲线(这是外国一个超高效冷冻站控制系统设备的功耗曲线,确保效率最大的情况下功耗最低),接下来进行类似单片机LED灯的打开、延迟、关闭、延迟的闪烁操作,连续闪烁12次,就是模拟春节十二响了,只是代码中的开启停止还需要等待一段时间,延迟的时间分别是2s和4s,打开发动机2s,再停止发动机4s,一快一慢,就类似于心跳的频率

    2.4 - 关闭发动机

    int final() {
        engine_ensure_shutdown();
    }
    

      最后在行星环境SDK中调用API关闭所有发动机,春节十二响模拟完成

    03 - 总结

    开始
    init
    1-设置发动机掩码
    2-设置危险级别
    3-检查当前时间
    4-检查发动机状态
    main
    1-设置功耗曲线
    2-发动机闪烁12次
    final
    关闭发动机
    结束
    • 可以看得出,这位博主的知识领域应该涉及到C、Python、Unix && Linux以及数学领域
    • 虽然是简单的伪代码,但是提出了一个简易的框架,后面将会有扩展

    04 - Spring12扩展

      伪代码始终不能满足程序员的好奇心,于是基于此框架,在CSDN上的一位博主用C语言+windows库实现了春节十二响的CMD演示,【流浪地球】春节十二响程序开源代码,CMD效果如下,简直是1分钱特效…
    在这里插入图片描述
      这位博主的源码很简单,一堆的printf()以及格式控制,调用图形库变颜色闪烁,一股非主流风扑鼻而来,有兴趣的伙伴可以读这位博主的源码,虽然写得一般,但是有个1分钱效果还算可以了

    05 - 如何用Scratch动画显示Spring12

      CDM没有动画显示,想要简单的动画显示,可以用Scratch,小白将使用Scratch实现spring12的动画效果,期待后续

    展开全文
  • 有趣的c语言

    千次阅读 2019-01-29 18:07:38
    结果,工作了三年多,稀里糊涂的代码写了不少,犯错误也很多,吃一堑长一智过程中,也会经常有很多不解:“这样写,难道不对么?” 最近终于有时间,终于还是决定花点时间,把c语言掌握清晰一点,选中了这本...

    写在前面

    工作三年多,常听各位前辈讲:

    语言是其次的,重要的是思想。

    深以为然,于是继续贯彻陶渊明的:

    好读书,不求甚解。

    结果,工作了三年多,稀里糊涂的代码写了不少,犯的错误也很多,吃一堑长一智的过程中,也会经常有很多不解:“这样写,难道不对么?”

    最近终于有时间,终于还是决定花点时间,把c语言掌握的清晰一点,选中了这本已经带领无数大神走向巅峰的神书《c专家编程》,打算通读一遍,空口无凭,记个笔记,所以,到这里您可以返回了,这,只不过是一个简陋的读书笔记。。。

    01 #define中的空格

    记得最开始学习编程的时候,有听过一些说法,宏定义中的空格实际上没有作用,例如:

    //01-1.1
    #include <stdio.h>    
                                                                                                                                                              
    #define sum(a, b)       ((a) + (b))                                                                                                                                             
    #define sum_1(a, b)     ((a)+(b))                                                                                                                                                                                                                                                                                                                 
    #define sum_2(a,b) ((a)+(b))                                                                                                                                                    
                                                                                                                                                                                    
    int main(int argc, char* argv[])                                                                                                                                                
    {                                                                                                                                                                               
        printf("sum = %d\n", sum(1, 1));                                                                                                                                            
        printf("sum_1 = %d\n", sum_1(1, 1));                                                                                                                                        
        printf("sum_2 = %d\n", sum_2(1, 1));                                                                                                                                        
        return 0;                                                                                                                                                                   
    }          
    

    预编译后的产物,其实是这样的:

    //01-1.2
    //...不相关,省略
    
    int main(int argc, char* argv[])
    {
     printf("sum = %d\n", ((1) + (1)));
     printf("sum_1 = %d\n", ((1)+(1)));
     printf("sum_2 = %d\n", ((1)+(1)));
     return 0;
    }
    

    上面的空格的确不会影响结果输出,而且,行尾多加的几个空格也没有什么用处,然而另外一些情况,显然不是如此:

    //01-2.1.1
    #define a(y) a_expanded(y)
    
    int a_expanded(int y)
    {
            return y + 10;
    }
    
    int main(int argc, char* argv[])
    {
            int x = 100;
    
            a(x);
            return 0;
    }
    

    预编译后如下:

    //01-2.1.2
    //...不相关,省略
    
    int main(int argc, char* argv[])
    {
     int x = 100;
    
     a_expanded(x);
     return 0;
    }
    

    而下面这段代码,加了两个空格之后,含义却完全不同:

    //01-2.2.1
    #define a (y) a_expanded (y)
    
    int a_expanded(int y)
    {
            return y + 10;
    }
    
    int main(int argc, char* argv[])
    {
            int x = 100;
    
            a(x);
            return 0;
    }
    

    预编译后的产物却是这样:

    //01-2.2.2
    //...不相关,省略
    
    int main(int argc, char* argv[])
    {
     int x = 100;
    
     (y) a_expanded (y)(x);
     return 0;
    }
    

    那么显然,空格影响了宏定义的含义,当然,代码01-2.2.1实际上无法执行,因此,宏定义中,显然还是要小心的注意某些位置空格的问题。

    02 const修饰的是谁?

    int foo(const char** p)                                                                                                                                                         
    {                                                                                                                                                                                                                                                                                                                                             
    }                                                                                                                                                                               
                                                                                                                                                                                    
    int main(int argc, char** argv)                                                                                                                                                 
    {                                                                                                                                                                               
        foo(argv);                                                                                                                                                                  
    }                 
    

    上面这段例子里,编译会报warning:

    const.c: In function ‘main’:
    const.c:8:9: warning: passing argument 1 of ‘foo’ from incompatible pointer type [-Wincompatible-pointer-types]
         foo(argv);
             ^~~~
    const.c:1:5: note: expected ‘const char **’ but argument is of type ‘char **’
     int foo(const char** p)
         ^~~
    

    那么显然,编译器认为,char**并不能直接复制给const char **,会发生隐式类型转换,可是,下面的例子却很常见:

    int foo(const char* p)                                                                                                                                                          
    {         
    }                                                                                                                                                                                
    int main(int argc, char* argv)                                                                                                                                                  
    {                                                                                                                                                                               
        foo(argv);                                                                                                                                                                  
    }                                                                                                                                                                               
    

    那么,一个简单的问题就是,c语言的参数传递实际上是一个赋值过程,那么char*可以赋值给const char*,为什么char **不能赋值给const char**

    两个操作数都是指向有限定符或无限定符的相容类型的指针,左边指针所指向的类型必须包含右边指针所指向类型的全部限定符。

    那么,上面主要有两处需要注意:

    1. 赋值的左右两边必须类型相容。
    2. 左边必须包含右边的全部限定符。

    根据这两个条件,解释以下这个问题:
    char*是一个指向没有限定符char类型的指针。
    const char*是一个有const限定符char类型的指针。
    char类型相容,左边包含右边的限定符,因此可以赋值。

    char**是一个指向char类型的指针的指针。
    const char**是一个指向有const限定符限制的char类型的指针的指针。
    那么显然,二者均没有限定符,且是指针,前者指向char*,后者指向const char*,二者不相容,也因此,char**实际上与const char**不相容,因此,上面的编译会报warning。

    看完上面的例子,并不能让人很容易明白,很多情况下依然是一头雾水,那么,再举个例子,看看const到底修饰的是谁?

    #include <stdio.h>                                                                                                                                                              
                                                                                                                                                                                    
    void test_const_1(void)                                                                                                                                                         
    {                                                                                                                                                                               
        char arr[][10] = {"abc", "def", "hij"};                                                                                                                                     
                                                                                                                                                                                    
        const char *pStr = arr[0];                                                                                                                                                  
        const char *pStr1 = arr[1];                                                                                                                                                 
        const char *pStr2 = arr[2];                                                                                                                                                 
                                                                                                                                                                                    
        const char **ppStr = &pStr;                                                                                                                                                 
        const char **ppStr1 = &pStr1;                                                                                                                                               
                                                                                                                                                                                    
        const char ***pppStr = &ppStr;                                                                                                                                              
                                                                                                                                                                                    
        pStr = pStr1;                                                                                                                                                               
        *pStr = arr[1][1];                                                                                                                                                          
                                                                                                                                                                                    
        ppStr = &pStr1;                                                                                                                                                             
        *ppStr = pStr1;                                                                                                                                                             
        **ppStr = arr[1][1];                                                                                                                                                        
                                                                                                                                                                                    
        pppStr = &ppStr1;                                                                                                                                                           
        *pppStr = &pStr1;                                                                                                                                                           
        **pppStr = pStr1;                                                                                                                                                           
        ***pppStr = arr[1][1];                                                                                                                                                      
    }                                                                                                                                                                               
                                                                                                                                                                                    
    void test_const_2(void)                                                                                                                                                         
    {                                                                                                                                                                               
        char arr[][10] = {"abc", "def", "hij"};                                                                                                                                     
                                                                                                                                                                                    
        const char *pStr = arr[0];                                                                                                                                                  
        const char *pStr1 = arr[1];                                                                                                                                                 
        const char *pStr2 = arr[2];                                                                                                                                                 
                                                                                                                                                                                    
        const char **ppStr = &pStr;                                                                                                                                                 
        const char **ppStr1 = &pStr1;                                                                                                                                               
                                                                                                                                                                                    
        const char ***pppStr1 = NULL;                                                                                                                                               
        char* const **pppStr2 = NULL;                                                                                                                                               
        char** const *pppStr3 = NULL;                                                                                                                                               
                                                                                                                                                                                    
        pppStr1 = &ppStr;                                                                                                                                                           
        *pppStr1 = &pStr1;                                                                                                                                                          
        **pppStr1 = pStr1;                                                                                                                                                          
        ***pppStr1 = arr[1][1];                                                                                                                                                     
        
        /*!
         * 错误用法,赋值表达式左边不包含右边的全部限定                                                                                                                             
         * ppStr中const修饰char,表示不能通过**ppStr修改char的值                                                                                                                    
         * pppStr2中const修饰char *,表示不能通过**pppStr2修改char* 的指向
         */
        //pppStr2 = &ppStr;
        /*!
         * 错误用法,赋值表达式左边不包含右边的全部限定
         * pStr1中const修饰char,表示不能通过*pStr1修改char的值
         * pppStr2中const修饰char *,表示不能通过**pppStr2修改char* 的指向
         */
        //*pppStr2 = &pStr1;//! 隐式类型转换
        **pppStr2 = pStr1;
        ***pppStr2 = arr[1][1];
        
        /*!
         * 错误用法,赋值表达式左边不包含右边的全部限定
         * ppStr中const修饰char,表示不能通过**ppStr修改char的值
         * pppStr3中const修饰char**,表示不能通过*pppStr3修改char** 的指向
         */
        //pppStr3 = &ppStr;
        *pppStr3 = &pStr1;
        /*!
         * 错误用法,赋值表达式左边不包含右边的全部限定
         * pStr1中const修饰char,表示不能通过*pStr1修改char的值
         * pppStr3中const修饰char**,表示不能通过*pppStr3修改char** 的指向
         */
        //**pppStr3 = pStr1;
        ***pppStr3 = arr[1][1];
    }   
    
    int main(int argc, char* argv[])
    {
        test_const_1();
    
        test_const_2();
    
        return 0;
    }
    

    猜猜看,上面哪些内容会编译报错?

    const.c: In function ‘test_const_1’:
    const.c:17:8: error: assignment of read-only location ‘*pStr’
      *pStr = arr[1][1];
            ^
    const.c:21:10: error: assignment of read-only location ‘**ppStr’
      **ppStr = arr[1][1];
              ^
    const.c:26:12: error: assignment of read-only location ‘***pppStr’
      ***pppStr = arr[1][1];
                ^
    const.c: In function ‘test_const_2’:
    const.c:47:13: error: assignment of read-only location ‘***pppStr1’
      ***pppStr1 = arr[1][1];
                 ^
    const.c:61:12: error: assignment of read-only location ‘**pppStr2’
      **pppStr2 = pStr1;
                ^
    const.c:70:11: error: assignment of read-only location ‘*pppStr3’
      *pppStr3 = &pStr1;
               ^
    

    经过上面的例子,可以简单的记忆为,const修饰的指针变量中,去掉数据类型,const后面的变量不可直接修改,例如char** const *pppStr3*pppStr3不可以直接赋值修改。

    展开全文
  • C语言的游戏代码

    2013-04-25 18:33:44
    有趣的C语言代码集萃,可以用来编写游戏。
  • 12个有趣的C语言问答

    2021-01-31 01:47:53
    Q:以下代码有个被隐藏住问题,你能找到它吗?A:这个不显眼问题就是使用了gets()方法。此方法接受一个string类型参数,但是却没有检测此数值是否有足够空间来拷贝数据。所以这里我们一般用 fgets()方法将来更...
  • 有趣的c语言算法经典

    2011-05-11 09:36:21
    对于初学c语言不久来说,这不失为一个很好资料,里面给出了详细代码
  • 有趣的C语言编程下面讲的是一个关于相亲数对的源程序 首先我先讲一下什么是相亲数对例如num1和num2两个数当num1的真因子数之和所谓真因子数就是出了书本身以外的其他因子数等于num2而num2的真因子数之和等于num1这样...
  • 12个有趣的C语言面试题 本文的12个C语言面试题,涉及指针、进程、运算、结构体、函数、内存,而且有趣,看看你能做出几个! 1.gets()函数 问:请找出下面代码里的问题 #include int main(void) { ...

    12个有趣的C语言面试题


    本文的12个C语言面试题,涉及指针、进程、运算、结构体、函数、内存,而且有趣,看看你能做出几个!

    1.gets()函数

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

    <span class="preprocessor">#include<stdio.h></span>
    <span class="keyword">int</span> main(<span class="keyword">void</span>)
    {
    <span class="indent">  </span><span class="keyword">char</span> buff[<span class="number">10</span>];
    <span class="indent">  </span>memset(buff,<span class="number">0</span>,<span class="keyword">sizeof</span>(buff));
    <span class="indent">  </span>gets(buff);
    <span class="indent">  </span>printf(<span class="string">"\n The buffer entered is [%s]\n"</span>,buff);
    <span class="indent">  </span><span class="keyword">return</span> <span class="number">0</span>;
    }
    

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

    2.strcpy()函数

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

    <span class="preprocessor">#include<stdio.h> </span>
     <span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">char</span> *argv[]) 
    { 
    <span class="indent">  </span><span class="keyword">int</span> flag = <span class="number">0</span>; 
    <span class="indent">  </span><span class="keyword">char</span> passwd[<span class="number">10</span>]; 
    <span class="indent">  </span>memset(passwd,<span class="number">0</span>,<span class="keyword">sizeof</span>(passwd)); 
    <span class="indent">  </span>strcpy(passwd, argv[<span class="number">1</span>]); 
    <span class="indent">  </span><span class="keyword">if</span>(<span class="number">0</span> == strcmp(<span class="string">"LinuxGeek"</span>, passwd)) 
    <span class="indent">  </span>{ 
    <span class="indent">  </span><span class="indent">  </span>flag = <span class="number">1</span>; 
    <span class="indent">  </span>} 
    <span class="indent">  </span><span class="keyword">if</span>(flag) 
    <span class="indent">  </span>{ 
    <span class="indent">  </span><span class="indent">  </span>printf(<span class="string">"\n Password cracked \n"</span>); 
    <span class="indent">  </span>} 
    <span class="indent">  </span><span class="keyword">else</span> 
    <span class="indent">  </span>{ 
    <span class="indent">  </span><span class="indent">  </span>printf(<span class="string">"\n Incorrect passwd \n"</span>);  
    <span class="indent">  </span>} 
    <span class="indent">  </span><span class="keyword">return</span> <span class="number">0</span>; 
    }
    

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

    $ ./psswd aaaaaaaaaaaaa 
     
    Password cracked

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

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

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

    3.main()的返回类型

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

    <span class="preprocessor">#include<stdio.h> </span>
     <span class="keyword">void</span> main(<span class="keyword">void</span>) 
    { 
    <span class="indent">  </span><span class="keyword">char</span> *ptr = (<span class="keyword">char</span>*)malloc(<span class="number">10</span>); 
    <span class="indent">  </span><span class="keyword">if</span>(NULL == ptr) 
    <span class="indent">  </span>{ 
    <span class="indent">  </span><span class="indent">  </span>printf(<span class="string">"\n Malloc failed \n"</span>); 
    <span class="indent">  </span><span class="indent">  </span><span class="keyword">return</span>; 
    <span class="indent">  </span>} 
    <span class="indent">  </span><span class="keyword">else</span> 
    <span class="indent">  </span>{ 
    <span class="indent">  </span><span class="indent">  </span><span class="comment">// Do some processing </span>
    <span class="indent">  </span><span class="indent">  </span>free(ptr); 
    <span class="indent">  </span>} 
    <span class="indent">  </span><span class="keyword">return</span>; 
    }
    

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

    4.内存泄露

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

    <span class="preprocessor">#include<stdio.h>  </span>
    <span class="keyword">void</span> main(<span class="keyword">void</span>) 
    { 
    <span class="indent">  </span><span class="keyword">char</span> *ptr = (<span class="keyword">char</span>*)malloc(<span class="number">10</span>); 
    <span class="indent">  </span><span class="keyword">if</span>(NULL == ptr) 
    <span class="indent">  </span>{ 
    <span class="indent">  </span><span class="indent">  </span>printf(<span class="string">"\n Malloc failed \n"</span>); 
    <span class="indent">  </span><span class="indent">  </span><span class="keyword">return</span>; 
    <span class="indent">  </span>} 
    <span class="indent">  </span><span class="keyword">else</span> 
    <span class="indent">  </span>{ 
    <span class="indent">  </span><span class="indent">  </span><span class="comment">// Do some processing </span>
    <span class="indent">  </span>}  
    <span class="indent">  </span><span class="keyword">return</span>; 
    }
    

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

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

    5.free()函数

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

    <span class="comment">#include<stdio.h>  </span>
    <span class="keyword">int</span> main(<span class="keyword">int</span> argc, char <span class="variable">*argv</span>[]) 
    { 
    <span class="indent">  </span>char <span class="variable">*ptr</span> = (char<span class="variable">*)</span>malloc(<span class="number">10</span>); 
    <span class="indent">  </span><span class="keyword">if</span>(NULL == ptr) 
    <span class="indent">  </span>{ 
    <span class="indent">  </span><span class="indent">  </span><span class="keyword">printf</span>(<span class="string">"\n Malloc failed \n"</span>); 
    <span class="indent">  </span><span class="indent">  </span><span class="keyword">return</span> -<span class="number">1</span>; 
    <span class="indent">  </span>} 
    <span class="indent">  </span><span class="keyword">else</span> <span class="keyword">if</span>(argc == <span class="number">1</span>) 
    <span class="indent">  </span>{ 
    <span class="indent">  </span><span class="indent">  </span><span class="keyword">printf</span>(<span class="string">"\n Usage  \n"</span>); 
    <span class="indent">  </span>} 
    <span class="indent">  </span><span class="keyword">else</span> 
    <span class="indent">  </span>{ 
    <span class="indent">  </span><span class="indent">  </span>memset(ptr, <span class="number">0</span>, <span class="number">10</span>); 
    <span class="indent">  </span><span class="indent">  </span>strncpy(ptr, argv[<span class="number">1</span>], <span class="number">9</span>); 
    <span class="indent">  </span><span class="indent">  </span><span class="keyword">while</span>(<span class="variable">*ptr</span> != <span class="string">'z'</span>) 
    <span class="indent">  </span><span class="indent">  </span>{ 
    <span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="keyword">if</span>(<span class="variable">*ptr</span> == <span class="string">''</span>) 
    <span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="keyword">break</span>; 
    <span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="keyword">else</span> 
    <span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>ptr++; 
    <span class="indent">  </span><span class="indent">  </span>} 
    <span class="indent">  </span><span class="indent">  </span><span class="keyword">if</span>(<span class="variable">*ptr</span> == <span class="string">'z'</span>) 
    <span class="indent">  </span><span class="indent">  </span>{ 
    <span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="keyword">printf</span>(<span class="string">"\n String contains 'z'\n"</span>); 
    <span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="regexp">//</span> Do some more processing 
    <span class="indent">  </span><span class="indent">  </span>} 
    <span class="indent">  </span>   free(ptr); 
    <span class="indent">  </span>} 
    <span class="indent">  </span><span class="keyword">return</span> <span class="number">0</span>; 
    }
    

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

    6.使用_exit退出

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

    <span class="preprocessor">#include<stdio.h>  </span>
    <span class="keyword">void</span> func(<span class="keyword">void</span>) 
    { 
    <span class="indent">  </span>printf(<span class="string">"\n Cleanup function called \n"</span>); 
    <span class="indent">  </span><span class="keyword">return</span>; 
    }  
    <span class="keyword">int</span> main(<span class="keyword">void</span>) 
    { 
    <span class="indent">  </span><span class="keyword">int</span> i = <span class="number">0</span>; 
    <span class="indent">  </span>atexit(func); 
    <span class="indent">  </span><span class="keyword">for</span>(;i<<span class="number">0xffffff</span>;i++); 
    <span class="indent">  </span>_exit(<span class="number">0</span>); 
    }
    

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

    7.void*和C结构体

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

    答:如下:

    <span class="keyword">int</span> func(<span class="keyword">void</span> *ptr)

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

    8.* 和 ++ 操作

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

    <span class="comment">#include<stdio.h>  </span>
    <span class="keyword">int</span> main(void) 
    { 
        char <span class="variable">*ptr</span> = <span class="string">"Linux"</span>; 
        <span class="keyword">printf</span>(<span class="string">"\n [<span class="variable">%c</span>] \n"</span>,<span class="variable">*ptr</span>++); 
        <span class="keyword">printf</span>(<span class="string">"\n [<span class="variable">%c</span>] \n"</span>,<span class="variable">*ptr</span>);  
        <span class="keyword">return</span> <span class="number">0</span>; 
    }

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

    [L]  
     
    [i]

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

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

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

    <span class="comment">#include<stdio.h>  </span>
    <span class="keyword">int</span> main(void) 
    { 
        char <span class="variable">*ptr</span> = <span class="string">"Linux"</span>; 
        <span class="variable">*ptr</span> = <span class="string">'T'</span>;  
        <span class="keyword">printf</span>(<span class="string">"\n [<span class="variable">%s</span>] \n"</span>, ptr); 
        <span class="keyword">return</span> <span class="number">0</span>; 
    }

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

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

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

    答:参见下面这段代码:

    <span class="preprocessor">#include<stdio.h> </span>
    <span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">char</span> *argv[]) 
    { 
    <span class="indent">  </span><span class="keyword">int</span> i = <span class="number">0</span>; 
    <span class="indent">  </span><span class="keyword">char</span> buff[<span class="number">100</span>]; 
    <span class="indent">  </span>memset(buff,<span class="number">0</span>,<span class="keyword">sizeof</span>(buff)); 
    <span class="indent">  </span>strncpy(buff, argv[<span class="number">0</span>], <span class="keyword">sizeof</span>(buff)); 
    <span class="indent">  </span>memset(argv[<span class="number">0</span>],<span class="number">0</span>,strlen(buff)); 
    <span class="indent">  </span>strncpy(argv[<span class="number">0</span>], <span class="string">"NewName"</span>, <span class="number">7</span>); 
    <span class="indent">  </span><span class="comment">// Simulate a wait. Check the process </span>
    <span class="indent">  </span><span class="comment">// name at this point. </span>
    <span class="indent">  </span><span class="keyword">for</span>(;i<<span class="number">0xffffffff</span>;i++); 
    <span class="indent">  </span><span class="keyword">return</span> <span class="number">0</span>; 
    }
    

    11.返回本地变量的地址

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

    <span class="comment">#include<stdio.h>  </span>
    <span class="keyword">int</span>* inc(<span class="keyword">int</span> val) 
    { 
      <span class="keyword">int</span> a = val; 
      a++; 
      <span class="keyword">return</span> &a; 
    }  
    <span class="keyword">int</span> main(void) 
    { 
        <span class="keyword">int</span> a = <span class="number">10</span>; 
        <span class="keyword">int</span> <span class="variable">*val</span> = inc(a); 
        <span class="keyword">printf</span>(<span class="string">"\n Incremented value is equal to [<span class="variable">%d</span>] \n"</span>, <span class="variable">*val</span>); 
        <span class="keyword">return</span> <span class="number">0</span>; 
    }

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

    12.处理printf()的参数

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

    <span class="comment">#include<stdio.h> </span>
    <span class="keyword">int</span> main(void) 
    { 
        <span class="keyword">int</span> a = <span class="number">10</span>, b = <span class="number">20</span>, c = <span class="number">30</span>; 
        <span class="keyword">printf</span>(<span class="string">"\n <span class="variable">%d</span>..<span class="variable">%d</span>..<span class="variable">%d</span> \n"</span>, a+b+c, (b = b<span class="variable">*2</span>), (c = c<span class="variable">*2</span>)); 
     
        <span class="keyword">return</span> <span class="number">0</span>; 
    }

    答:输出结果是:

    <span class="attribute">110..40..60</span>

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

    展开全文
  • 有趣的C语言问答

    2014-09-15 14:22:57
     Q:以下代码有个被隐藏住问题,你能找到它吗? 1 2 3 4 5 6 7 8 9 10 11 12 13 #include   int main(void) {  char buff[10];...
  • 12个有趣的C语言面试题~

    千次阅读 2013-05-30 12:15:12
    12个有趣的C语言面试题 1.gets()函数问:请找出下面代码里的问题:#include int main(void) { char buff[10]; memset(buff,0,sizeof(buff)); gets(buff); printf("\n The buffer entere .. 1.gets()函数...
  • c语言有趣的100个例子代码,一定要看呀。
  • 有趣的C语言】空瓶换汽水问题

    千次阅读 2018-04-27 13:34:40
    有趣的C语言  相信大家学习C语言的过程中都做过这么一道题: 喝汽水,1瓶汽水1元,两个空瓶可以换一瓶汽水,给20元,可以喝多少瓶汽水。编程实现。  那么这道题在C语言中应该如何实现呢?  下面是我写的...
  • 有趣的C语言面试题

    2017-08-10 16:07:31
    1、gets() 和 fgets()函数 ...问题:找出下面代码的问题 #include int main(void) { char buff[10]; memset(buff,0,sizeof(buff)); gets(buff); printf("\n The buffer entered is [%s]\n",buff); r
  • 一道有趣的C语言面试题

    千次阅读 2019-08-28 22:21:59
    今天在吃晚饭时候玩儿手机,无意间看到一道题,出处未知。这里来考考大家^_^: signed char a = (unsigned char)-1; unsigned char b = a; int c = a; int d = b;...各位不用敲代码,猜猜看? ...
  • 12个有趣的C语言问答 0,gets() 方法 Q:以下代码有个被隐藏住的问题,你能找到它吗? A:这个不显眼的问题就是使用了 gets() 方法。此方法接受一个string类型参数,但是却没有检测此数值是否有足够的空间来拷贝...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 610
精华内容 244
关键字:

有趣的c语言代码

c语言 订阅