zoukankan      html  css  js  c++  java
  • 函数,从编辑到编译 (上)

    0. 序

    我从一生下来就呆在这个昏暗的地方。

    我不明白为什么程序员这么喜欢 Dark Mode,Brighten Mode 才是我的最爱。听说最近连 iphone 都开始支持 Dark Mode 了,没话讲。。。说好的绝不妥协呢?

    我周围是熙熙攘攘的函数群,穿插着变量声明和宏定义。

    在我们这里,函数是一等公民。

    当然,不光在 C++,在面向过程的 C 语言、面向对象的 Java ,尤其是在那些函数式编程的语言里,我们都扮演着举足轻重的角色。

    能力越大,责任越大。我和一群函数伙伴们就负责维护着程序的功能。每个函数的一小步,合起来就是功能模块的一大步。

    作为一门静态编译型语言,我们不像那些解释语言一样,写完就能直接运行,而是要先经过编译这一道坎,成为机器语言,才能够运行在我们赖以生存的机器上。

    这道坎不是那么好过的,再顶尖的程序员,也会在这上面栽跟头。

    放在往常,虽然程序偶尔会出 bug ,但大家齐心协力,可谓虫(bug)挡杀虫,过五关斩六将,整个程序也称得上是井井有条。

    但这次,我们遇到了大问题。

    1. 预编译

    今天的一切看起来都很平凡,至少我是这么认为的。

    屏幕外的程序员像平常一样敲着代码,我们像平常迎接着新函数的到来,像平常一样嬉笑怒骂,像平常一样期待着预编译进程的到来。

    预编译进程是整个编译进程的先锋。

    像往常一样,我们从磁盘出发,沿着总线来到了内存。这里就是进程的工作车间。

    预编译进程第一步会 删除所有 #define,展开宏定义。处理条件预编译指令。

    #define WINDOWS
    #define BUFSIZE 1024
    #define DEPTH 4
    #define DECODE "utf-8"
    ...
    

    上面的就是宏定义,每次我们都要在预编译进程的指挥下,把语句里出现的宏替换成对应的值。

    这一步其实本来不需要我们干的,程序员怕麻烦,想要做到“一处修改,处处更改”,就发明了宏定义,让编译器来干这些“脏活累活”。

    处理条件预编译指令就有点不一样了:

    //windows or linux
    #ifdef WINDOWS
    <experssion1>
    #else
    <experssion2>
    #endif
    

    如果宏定义有这个 WINDOWS,就只留下 <experssion1>,没有的话就留 <experssion2> 。说白了,就是个预编译阶段能执行的 if... else ... 语句。上面的语句一处理,就变成了:

    <experssion1>
    

    对,注释也会被删除。

    可怜那些注释,这一辈子都不曾领略 CPU 里的风景。

    第二步是处理 #include预编译指令。

    这一步就比上面的复杂多了。用专业的话来说,处理 “#include ”预编译指令,就是将被包含的文件插入到该预编译指令的位置。这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。

    #include "config.h"
    <expressions>
    

    别看他们现在就只有短短两句,等把 config.h文件内容复制过来,信息量一下子就大了。

    #ifndef _CONFIG_H_
    #define _CONFIG_H_
    
    #define VERSION "1.0.0"
    #define MODE 1
    ...
    ...
    #endif
    
    <expressions>
    

    补充一句,这个 .h 后缀的家伙,叫头文件。他是我们与其他文件的函数公民的沟通渠道。

    头文件这个家伙和源文件不太一样,他是包含功能函数、数据接口声明的载体文件,主要用于保存程序的声明。也就是说,头文件里是没有函数的——我们曾多次试图占领头文件的领地,但都没有成功——都是因为程序员的约束。

    每个头文件都会带有一组条件预编译语句,用来防止自己被多次编译。

    至于怎么做到的,这太简单了,我不说你也能想出来。

    听说有的编译器还支持 #pragma once ,添在头文件第一行就能做到相同的事情。可惜我们的编译器有点旧,不兼容他们。

    最后这步就比较快了,添加行号和文件名标识

    走到这里,我们已经得到了编译器调试的需要的行号信息,如果编译到哪一步出错,或者出现 warning 这样的警告,就能把行号显示出来,方便程序员及时发现问题源头。

    今天的预编译比我想象中要快一点,可能这次没什么进程跟我们抢 CPU 资源吧。

    预编译阶段结束,# 的数量大大减少,仅剩下几个 #pragma 指令留在这里。

    和其他宏定义指令不一样的是,#pragma 是能够跟编译器平起平坐的存在,预编译进程见了都得避让三分。

    #pragma  warning( disable: 4507 34; once: 4385; error: 164 )
    

    像这条指令,就是专门给编译器看的,意思是 ‘不显示4507和34号警告信息 ,4385号警告信息仅报告一次,把164号警告信息作为一个错误’ 。可以说,她是程序员和编译器之间的信鸽。

    对于我来说,预编译阶段是比较轻松的,最复杂也只是处理条件预编译指令——删除几行代码罢了。

    未完待续

    如果大家对文章有什么看法和意见,欢迎提出来~

  • 相关阅读:
    java环境--JDK和Tomcat在linux上的安装和配置
    转载:jQuery的deferred对象详解
    js 模板引擎 -Art Template
    sublime text的快捷键
    Spring MVC 配置Controller详解
    转:几款免费的图表js插件
    tomcat manager详解
    C#判断一个string是否为数字
    调用摄像头并将其显示在UGUI image上自适应屏幕大小
    unity监测按下键的键值并输出+unity键值
  • 原文地址:https://www.cnblogs.com/tobe98/p/11564536.html
Copyright © 2011-2022 走看看