C/C++语言其实刚学起来难度不大,学着学着就发现这也不对那也不对。碰上高手的代码,有时候都看不懂。一大堆的宏定义,一大堆的指针,一连串的继承、重载、多态覆盖。看得头昏脑胀。实在是非常痛苦。再加上如果开发MFC/ATL程序,这些程序是MS在WINAPI上的封装,通过VISUAL STUDIO工具一阵自动生成。最后你发现,C/C++在哪都不知道,你只知道某个API,某个类了。语言不见了。出现这种情况,有些人说好,说明你已经达到一个境界了。可是你自己会清楚,C/C++,这个语言,你究竟在哪?别人让你举几个C/C++基本特征,心中也说不清楚。看到C/C++语言写的代码,你也是心里似懂不懂的,有时候,心里想,何必究根呢?只要程序运行没错就行。真要程序Coredump了,再去GDB调试吧。我相信工作中的人想法80%都是这样想的。没有多少人愿意花时间去深究C/C++的内部机制的。一个因为时间不允许,工作任务紧,另一个确实来说,参考资料也很少。这里C++/C语言深度剖析,是自己的一个学习记录。那么首先从C/C++源码开始说起。
【1】版本和功能声明,说到这里,不得不提编程规范,也就是通常意义意上的Code specific。这里所说的编程规范,我相信几乎每家公司都有,可是一拿到代码库上的代码一看,还是乱七八糟的。这不能怪Review的人,也不能怪写Code的人。首先我们要理解,写程序和管程序的都是人。人都是弹性,不像机器,设定一个规则,就一定按照这个规则执行。人是这个世界上最奇怪的一种机器,他是不按常理出牌的。因此,要严格的要求一个人去遵守某种规则基本上是不可能的。那为什么还是每家公司都搞这些写在墙上没人看的规范,干嘛呢?也许是领导自我安慰,也许是QA部门的一种成绩。反正我理解写在纸上的规范通常都不是规范。只有落实到行动中的才是规范。那么我这里提规范干嘛呢?那我这里写这个规范是从解决自己的痛苦来说。我们通常很痛苦的就是拿到别人代码,一个目录,不知道从哪看起,不停的找main函数。找到main函数然后一个一个的找来找去,猜来猜去。显然一个清晰的目录结构对别人对自己都是一种享受。C/C++中有一个默认的命名规则就是 头文件 include目录 源码文件 src目录或者source目录。库文件 lib目录.编译后执行文件 bin目录。第二个痛苦的就是拿到一个CPP文件中,函数一大堆,不知道干嘛?那么必要的简短注释通常是非常有用的。那这里有一个问题,就是你说这注释是写在头文件中好还是源文件中好呢?这也是一个纠结的问题?两边都写就显得傻,写哪一边比较好呢?我们知道不管是SVN、git还是Clearcase它都是以文件作为管理单位的。因此,对每个文件来说,都是公司或个人的私有财产。因此,从这点来说,每个文件都需要进行版本声明、作者声明。那么功能说明从某种意义上说还是以头文件为主。通常来说头文件说明主要功能。源文件中说明实现细节。下面是一个推荐范本。
这个范本实际也是比较死的。实际上有些公司是CR/SR号来标识版本修改记录的。下面是一个参考例子。
【2】预处理写法。我们知道编译器在预处理过程中通常都是针对#开头的符号进行简单替换处理。这里有三个问题,我们分别解决。
1)第一个问题,就是同一个源码文件,如果包含头文件过程,出现重复。应该怎么处理。从语法上来说有时候肯定没错,当然有时候也会出现错误,因为相同的代码拷贝了两份。但是从生成文件的效率来说,肯定不好。如下所示:
file1.h 含有extern int size
file2.h 含有#include “file1.h” f
ile3.h 含有#include “file1.h”
这时如果file.c含有 #include “file2.h” 和#include “file3.h”。 这时经过预处理编译生成的file.i中就会含有两个extern int size。造成声明失败。因此,我们这可以采用如下两种解决方法:一种是使用ifndef/define/endif,
#ifndef FILE1
#define FILE1 “file1.h”
#include FILE1
#endif
另一种是使用#pragma once。这一种在VC中比较常见,但是不是所有编译器支持。它是针对单个文件在物理上只被编译一次,如果相同的文件有多份拷贝,也就是说内容相同,但文件名不一样。这种方法是行不通的。所以最好的方法还是第一种。因此,在写C++头文件时,一般在开头都要如下进行声明。
2)第二问题就是include 关于系统路径头文件和当前用户定义路径搜索的写法,我们都知道其中系统路径用#include<file.h>,用户路径用#include “file.h”。当然这里在编译时,我们是需要不管是GCC还VC的CL都是需要指定-I参数路径。VC是在IDE中进行配置。GCC是通过-I参数设置。但是通常我们开发时C++对标准库写法的建议是#<iostream>,这又是为什么呢?我们知道C++与C不同,有几点是C完全没有的:命名空间和.hpp尾的头文件。首先讲第一点,可能最容易接受的就是可移植性,C++新标准为了不受.h的牵制,并且实际上它也不建议了。就使用不带后缀的系统库。第二点,接着前面来说,你没有后缀,那编译器到底搜索的是哪个文件呢?这一点是有点让人接受不了,但实际上标准并没有明确区分,比喻说,<iostream>和<iostream.h>,前者可以肯定的是在命名空间std中,并且支持宽符,std标准组件中搜索到就不一定是iostream.h文件,并且里面的函数都是有命名空间的,也不是全局函数。可能看到这里更迷惑了。这还要从C++引入命名空间讲起。理论上讲引入命名空间导致了C++不再有绝对意义上的全局函数,也就避免了大规模开发时需要避免全局变量和全局函数同名的问题。因此,C++标准库都是建立在std命名空间范围内。但是我们知道C都是全局函数,C++是C的超集。如何兼容C的函数呢?C++使用了一个蠢办法,就是同名重写到std空间中去,比喻说iostream对应就是iostream.h。但是为了避免混淆,使用std命名空间的,就不带h。
3)第三个问题也比较重要,就是C和C++代码混合编译时,该如何处理。我相信大部分C++程序中都或多或少中含有纯C的代码,C++因为一些特性与C不完全兼容,因此,混和编译C++和C程序时时候会产生一些问题,具体什么问题呢?我们以函数来做比较,比喻函数void foo(int x,int y)在C++编译器生成时是_foo_int_int函数名,而在C中因为没有重载特性,所以生成的函数名是_foo,这时如果在编译时恰好这个函数是在C库中,那么用C++生成的这个函数就无法在链接时查到正确的C库。同理反过来也是这样,如果一段C程序使用到C++生成的函数,也会出现同样的问题,因此,处理这此问题就需要如下使用extern “C”来定义头文件。
另一个问题是如果C中使用C++生成的函数,怎么使用呢?显然C中是不能使用extern “C”标识的。那么该如何处理呢?可以在C++中声明这个函数为extern “C”,但在C中使用extern来引用。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。