转:从编译链接过程解析static函数的用法
关于static函数的用法
就像我们熟知的那样,变量可以分全局的和局部的,函数也可以分全局的和局部的。
比如说,在一个工程的common.h中定义了一个全局变量 int test;那么在整个工程的作用范围内,该变量都是存在的,在编译的时候会将其保存在整个工程全局的变量表中,文件(.h或.cpp)只要使用声明extern int test;就可使用该变量,而不用包含该变量的头文件common.h,因为该变量的作用域是全局的。
和全局变量一样,大多数的非类成员函数基本上都定义为全局的。我们可以分两种情况讨论,第一种情况,void func();被声明在commom.h中,实现体在common.cpp中。这种情况和全局变量的情况一样,整个工程内的任何程序都可以通过extern voud func();来使用该函数,而不需要包含头文件common.h。当然了,还可以通过包含头文件的方式使用func();。工程内的任何文件都可以包含common.h而不会出现名字冲突问题,因为common.h中只包含了函数的声明而没有实现体,任何包含了common.h的文件因此也就只包含了函数的声明,因此编译是没有问题的。至于函数的实现体,会在链接的时候自动到对应的obj(由对应的cpp生成)文件中寻找。
当把一个全局函数的声明和实现都放在common.h中时,如果有2个以上文件都包含了common.h,在编译的时候就无法通过,因为每包含一次common.h,就会在全局空间内保存一个func()实现体(最终会保存在obj固定的全局区域),如此一来,就会有多个个func()实现体保存在全局空间中(分别保存在多个obj中,在链接的时候会整合成一个总的exe),从而导致命名冲突。所以,一般都会使用class来封装函数,可以避免很多的命名冲突问题。
还有一种方法可以避免全局函数的命名冲突,可以将函数的声明和实现都放在common.h中,但是要加上static声明,即static void func(){};加上static就表明该函数只在本文件(common.h)中可用,在包含该头文件的obj全局空间内不会保存func()的实现体,任何文件要想使用func(),只能通过包含头文件(或者说是包含该函数的作用域)的方式来使用它,不能通过extern void func();来直接使用,因为此时的func()以不在整个工程全局空间的控制范围内。
还有一种情况,如果将函数的声明放在头文件中,将函数的实现放在对应的cpp文件中,并且将实现体加上了static声明,那么此时的static没有太大作用,因为既然将函数体放在了cpp中,别人一般不会包含你的cpp文件,也就不存在命名冲突问题了,除非有几个文件非要包含你的cpp文件不可,那么此时的static就派上用场了。
其实只要对编译的过程理解了,这些问题都很容易搞懂。下面简单讲一下编译的原理。
程序写好之后会先进行编译,在编译阶段,编译器会为每个cpp文件生产一个.obj文件,该文件就保存了每个文件中的全局变量或者全局函数的标识符,如果一个文件使用了另外一个文件中的全局函数并且没有包含其头文件,在编译的时候,编译器首先会寻找是否使用了extern声明,如果找到了,此时编译器就放心了,它知道这个函数是在别的地方定义了,在链接的时候会自动找到的,所以编译器就编译通过了。
编译完之后就会对每个.obj文件进行整合链接,最终生成可执行的exe文件。在链接阶段,此时的所有相关的.obj文件都被放到了整个工程的全局空间内,链接器会检查所有的.obj文件是否存在全局变量或者函数的命名冲突问题,如果多个obj文件都包含了某个全局函数的实现体,此时编译器就会报命名冲突错误,因为每个obj包含的不是函数的声明,而是结结实实的函数体,大家都知道,函数可以被声明多次,但是函数体只能定义一次,那么此时的函数体被每个obj都定义了一次,肯定会导致命名冲突问题。
解决的办法上面也说了,只能将函数体移到cpp文件中,头文件中只包含函数的声明,由此只有该cpp文件对应的obj文件中存在函数体,那么链接的时候果断不会有问题了;或者将函数体声明为static,此时的每个obj文件中就不会有该函数的定义了,该函数在编译阶段已经被整合到obj代码中了,不会出现在obj的全局空间中,所以在链接的时候不会有问题。
链接器的作用就是将每个单独的obj文件按照代码中的依赖关系进行整合(其中就包括对全局空间变量或者函数进行整合),最终生成exe文件。
补充一下,工程中的每一个cpp文件都会生成一个对应的obj文件。 关于static基本的用法就这么多,以后如有更深入的研究再继续补充
转:从编译链接过程解析static函数的用法的更多相关文章
- GCC编译链接过程
编译链接过程 代码 #cat main.c #include <stdio.h> int add(int x, int y); int sub(int x, int y); int mul ...
- [转]C++编译链接过程详解
C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接.编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程.链接是把目标文件.操作 ...
- C-从源文件到可执行文件的详细编译链接过程
一直用windows一键搞定, 没有去了解详细的编译链接过程, 今天看了一篇文章, 顺便实验和记录在Linux下逐步生成的步骤. 预处理: 执行#include, #define, #if, #ifd ...
- 转:C语言的编译链接过程的介绍
11:42:30 C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接.编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程.链接 ...
- C++, Java和C#的编译、链接过程解析
总是感觉java是解释性语言,转载下一篇感觉写的容易理解的文章 转自 http://www.cnblogs.com/rush/p/3155665.html 1.1.1 摘要 我们知道计算机不能直接理解 ...
- 【对象模型】C++模版的编译链接过程——编译器真的会检查所有tocken层面的错误么?
模版(template)设计的初衷,是设计一种自动实例化机制,不需要使用者参与,编译器可根据使用者提供的模版参数再套用类的定义来实例化.所谓实例化,除了包含对于程序变量的实例化,即开辟空间并设置某些变 ...
- C/C++编译链接过程详解
有些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错误信息不能定位到某 ...
- Delphi编译/链接过程
下面展示了Delphi是怎样编译源文件,并且把它们链接起来,最终形成可执行文件. 当Delphi编译项目(Project)时,将编译项目源文件.窗体单元和其他相关单元,在这个过程中将会发生好几件事情: ...
- Delphi 编译/链接过程
随机推荐
- linux下对date和timestamp的互转
1. date 到 timestamp: $ date -d '2009-12-01 23:20' +%s 12596808002. timestamp 到 date$ date -d '1970-0 ...
- LeetCode Flatten 2D Vector
原题链接在这里:https://leetcode.com/problems/flatten-2d-vector/ 题目: Implement an iterator to flatten a 2d v ...
- 对字符串算md5
这个问题要是写代码 是很简单的一个问题 能不能再简单一点呢,比如一条命令 一条sql,当然你要是在线转换也很快 shell printf admin|md5sum 注意printf 与echo区别 ...
- grunt安装、配置、在webstrom中使用
1.全局范围安装 Grunt命令行(CLI) npm install -g grunt-cli Grunt CLI的任务很简单:调用与Gruntfile在同一目录中 Grunt.这样带来的好处是,允许 ...
- sql 表分区使用
如果你的数据库中某一个表中的数据满足以下几个条件,那么你就要考虑创建分区表了. 1.数据库中某个表中的数据很多.很多是什么概念?一万条?两万条?还是十万条.一百万条?这个,我觉得是仁者见仁.智者见智的 ...
- WPF关于Generic.xaml
如果需要用到Themes/Generic.xaml作为默认风格资源文件,不要忘了该项目的AssemblyInfo.cs中必须要有以下这段: [assembly: ThemeInfo( Resource ...
- Asp.net MVC进入请求管道的过程
Asp.net MVC进入请求管道的过程 Asp.Net MVC 跟AspNet 入口解释 Asp.Net MVC请求处理过程 mvc 请求模型 mvc的原理 mvc模型 NewMVCPipleLin ...
- 给jar包进行数字签名(2014-06-28记)
整理一下两年前用到的一些资料. 为了使Applet或者Java Web Start程序能够访问客户端本地资源,需要对Applet或者JWS程序jar包进行数据签名,当客户端打开Applet或者JWS程 ...
- SpringMVC 手动控制事务提交
描述 事务还是一个比较好的东东,有了这个,我们在做流程性的东西的时候,就会很好,很nice. 现在看看 SpringMVC 如何实现的,详细请看代码: 1.配置文件 applicationContex ...
- T-SQL 的简单查询语句
通配符: “_”: 代表匹配一个字符 “%”: 代表匹配多个字符: []:表示范围,可以包含多个数据 [^] 表示取反 “-“ 表示范围 逻辑与 and 逻辑或 or 逻辑非 not 聚会函数 : ...