如何像计算机科学家一样思考?(C语言篇)

Mr.C/文

物格而后知至,知至而后意诚,意诚而后心正,心正而后身修,身修而后家齐,家齐而后国治,国治而后天下平。自天子以至於庶人,一是皆以修身为本。 —— 《大学》

  • 前言

《如何像计算机科学家一样思考?(C语言篇)》

早上在Instagram看到这张图片,觉得蛮有意思便转发到朋友圈,然后…
有说“写这种代码会被打”,有一脸蒙圈的,以及还有相当一部分是想把我拉黑的。

它在Ins上的评论是这样的(如果翻译得不对,请老师指正!):

《如何像计算机科学家一样思考?(C语言篇)》

  • C语言

其实,作为一名较老(cheng)司(xu)机(yuan),在推荐语言时,都会优先推荐Python,而不会推荐C。因为Python语法简单、现成的可用工具很多,关于这个问题的具体讨论可以参考 “为什么知乎上大多数人不推荐C语言入门?”

C语言的地位如何,我想TIOBE的排名可以说明,一直稳居三甲之内。(P.S. 刚查了一下,C语言八月份排名TIOBE有史(2001年)以来最低 T_T, 相关的同学要保持一颗警惕的心了。):

《如何像计算机科学家一样思考?(C语言篇)》

  • 所以,那张图到底在说什么?

图中的代码部分: void (* ( * f [] ) ( ) )( )

实际上这里定义了一个数组f[],数组中保存了未确定个函数指针,该类函数指针是指向一个返回值为void 参数为空的函数的函数指针。**(但需注意的是,在C语言的数组定义中,是需要确定数组大小的,否则编译不过) **

《如何像计算机科学家一样思考?(C语言篇)》

那到底是什么鬼?!!

咳咳,正确的打开理解方式是这样的:以“上帝版密室逃脱”为例

  • 我们在密室逃脱中,上帝给了我们一串钥匙: key = f[]
  • 这串钥匙中的任意一把可以打开其相对应的一个房间门 ** room = (* )() **
  • 打开这个房间门后,我们需要过五关斩六将(函数执行),寻找到下一关的钥匙** anotherKey**
  • 这个钥匙可以开启下一个房间门(因为我们第一次所开的门不同,此时我们所通往下一关的钥匙可能开启的是同一道门,也可能不是,这取决于上帝怎么设计。)
  • 下一个房间就是*anotherRoom = void ( ) ( ) ** ,在该房间内上帝可能放了一个惊喜也可能什么都没有。

所以,这个定义只是相当于现实生活中的一个“双层密室逃脱”而已,这样说是不是
容易理解多了?
容易理解多了?
容易理解多了?

《如何像计算机科学家一样思考?(C语言篇)》

……

……

……

好吧……

《如何像计算机科学家一样思考?(C语言篇)》

  • Talk is cheap,show me the code

Linus Torvalds said:“Talk is cheap, show me the code”。
So,我们用代码来解析一下试试看,顺便安利一下他的TED视频: 【TED】Linux 操作系统之父.

首先,定义一个房间的钥匙的类型:

 typedef void (* room_key) ();//最终房间的钥匙

然后,我们创建两个最终的房间(假设一间房里面藏有1W奖金和一间房什么都没有):

void nothing_in_the_room(void)
{ 
        printf("Sorry! You Get Nothing.\\\\n");  

}

 void something_in_the_room(void)
{ 
        printf("Congratulation! You Get Ten Thounsands.\\\\n");  

}
//这里可以注意到,这是两个返回值为void,参数为空的函数。

接下来,我们需要把上面房间的钥匙藏在另一个房间里(由于我们上面定义了两个房间,那么我们这里就需要在一个或N个房间内藏有可以开上面两个房间的钥匙,为了展示我们拥有的是一串钥匙( f[] ),这里我们定义三个房间):

room_key get_room_key1( void)
{
     room_key = nothing_in_the_room;//这里相当于把钥匙藏在该房间内,该钥匙对应的房间什么都没有
     printf( "You had got the key\\\\n ");
     return room_key;
}

room_key get_room_key2( void)
{
     room_key = something_in_the_room();//这里相当于把钥匙藏在该房间内,该钥匙对应的房间藏有1W
     printf( "You had got the key\\\\n ");
     return room_key;
}

room_key get_room_key3( void)
{
     room_key = nothing_in_the_room();//这里相当于把钥匙藏在该房间内,该钥匙对应的房间同样什么都没有
     printf( "You had got the key\\\\n ");
     return room_key;
}
//通过以上定义,我们就把上面两个房间的钥匙藏在了下面三个房间中,
//其中两个房间所藏的钥匙打开的是那间什么没有的屋子,因此我们可以认为中奖概率为1/3

**等灯邓蹬~~~ **此时我们讨论的重点该出场了,也就是我们所拥有的钥匙,它可以这样被使用:

void ( * (* f[] ) ( ) ) ( ) = {get_room_key1,get_room_key2,get_room_key3};
//这里就是我们所拥有的三把钥匙了,这三把钥匙分别藏在f[0]、f[1]以及f[2]中。

把上面的函数放到一个完整的游戏过程里面就是这样的:

void main (void)
{
    void ( * (* f[] ) ( ) ) ( ) = {get_room_key1,get_room_key2,get_room_key3};   
    //这里就是我们所拥有的三把钥匙了,这三把钥匙分别藏在f[0]、f[1]以及f[2]中。
   
    printf("Welcome To My Game! --- Mr.C\\\\n");

    int key_number = -1;//定义钥匙的编号,不同编号的钥匙对应不同的房间
    int play_times = 10;//玩家可以选择钥匙的次数
    int i = 0;

    for(i = 0; i < play_times;i++) 
    {
        printf("\\\\n=============Game Start=============\\\\n");
        printf("Please input your key number\\\\n");
        scanf(“%d",&key_number);//获取玩家输入的钥匙编码

        if ( key_number >= 0 && key_number < 3)
         {
               f[key_number]()();//通过上帝的钥匙,打开两个房间门
               /*为了便于阅读,我们可以分两步调用:
               step1:
                        room_key key = f[key_number]( );//通过第一把钥匙打开第一个房间,并获取下一把钥匙
               step2:
                        key();//通过第二把钥匙打开第二个房间门
               */
          }else {

               printf("You has not that key,I know everything.Don't cheat me\\\\n");     
                        
          } //end if      

    }// end for

}//end main

运行结果如下:

《如何像计算机科学家一样思考?(C语言篇)》

  • 结语

C语言博大精深,现在许多操作系统(大至Linux、unix,小至各Soc芯片的任务系统)或者大型项目的核心都是用C语言完成的,包括Python的核心也是C语言实现的。
但C语言的实际应用会像本文所举的例子有那么复杂么?
其实并没有,犹如上面评论所说,经常用这种充满技巧的方式写代码其实很容易被打shi的。当然,可以理解到这种复杂程度的指针个人觉得是必须而且是有一定用处的(起码比那些比较运算符优先级的题目实用多了),据说在Linux内核的网络驱动中有用到过类似本文中同一复杂程度的指针,找不到源码了,所以只能是据说了T-T.

In the end, 标题似乎定得有点大,But…

《如何像计算机科学家一样思考?(C语言篇)》

我们讲究的是,格局!格局!

《如何像计算机科学家一样思考?(C语言篇)》

点赞