关于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函数的用法的更多相关文章

  1. GCC编译链接过程

    编译链接过程 代码 #cat main.c #include <stdio.h> int add(int x, int y); int sub(int x, int y); int mul ...

  2. [转]C++编译链接过程详解

    C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接.编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程.链接是把目标文件.操作 ...

  3. C-从源文件到可执行文件的详细编译链接过程

    一直用windows一键搞定, 没有去了解详细的编译链接过程, 今天看了一篇文章, 顺便实验和记录在Linux下逐步生成的步骤. 预处理: 执行#include, #define, #if, #ifd ...

  4. 转:C语言的编译链接过程的介绍

    11:42:30 C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接.编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程.链接 ...

  5. C++, Java和C#的编译、链接过程解析

    总是感觉java是解释性语言,转载下一篇感觉写的容易理解的文章 转自 http://www.cnblogs.com/rush/p/3155665.html 1.1.1 摘要 我们知道计算机不能直接理解 ...

  6. 【对象模型】C++模版的编译链接过程——编译器真的会检查所有tocken层面的错误么?

    模版(template)设计的初衷,是设计一种自动实例化机制,不需要使用者参与,编译器可根据使用者提供的模版参数再套用类的定义来实例化.所谓实例化,除了包含对于程序变量的实例化,即开辟空间并设置某些变 ...

  7. C/C++编译链接过程详解

    有些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错误信息不能定位到某 ...

  8. Delphi编译/链接过程

    下面展示了Delphi是怎样编译源文件,并且把它们链接起来,最终形成可执行文件. 当Delphi编译项目(Project)时,将编译项目源文件.窗体单元和其他相关单元,在这个过程中将会发生好几件事情: ...

  9. Delphi 编译/链接过程

     

随机推荐

  1. 【翻译】How To Tango With Django 1.5.4 第四章

    4.模板和静态媒体 这章讲解模板引擎 4.1使用模板 前面我们讲解了view和url 映射,创建出了django 的web页面,现在就要将模板混合进去 好的网站在布局上总是有许多重复的.django提 ...

  2. [LeetCode]题解(python):118 Pascal's Triangle

    题目来源 https://leetcode.com/problems/pascals-triangle/ Given numRows, generate the first numRows of Pa ...

  3. Codeigniter2.25部署Linux(php5.6)

    1).默认路由:修改system/core/Router.php 中第146行.如下图所示.ps:转换成小写我也是醉了...注释的代表是codeigniter作者写的,而上面的是我更改的 2).mod ...

  4. js展开一颗树

    Tree View 指令不支持 树结构数据源, 只支持单层数组.(也许是我没发现,人家可以设置) .我只能把树展开,变成单层数组.然后还要记录已经递归到第一层了.比如这样. <!doctype ...

  5. Knockout.js是什么?

    从本节开始介绍关于KnockoutJs相关的内容,本节主要介绍knockoutjs一些重要特性与优点,以及它与Jquery等框架库之间的区别. 1.Knockout.js是什么? Knockout是一 ...

  6. 简要介绍Apache、php、mysql安装和工具介绍

    1 安装Apache 网站:www.Apache.org下载相应的Apache,目前下载了近期的:httpd-2.2.15-win32-x86-openssl-0.9.8msi 安装简要步骤如下图: ...

  7. 【java开发系列】—— struts2简单入门示例

    前言 最近正好有时间总结一下,过去的知识历程,虽说东西都是入门级的,高手肯定是不屑一顾了,但是对于初次涉猎的小白们,还是可以提供点参考的. struts2其实就是为我们封装了servlet,简化了js ...

  8. [转]装完CentOS后,重新开机启动后显示: Initial setup of CentOS Linux 7 (core)

    转:装完Centos7提示Initial setup of CentOS Linux 7 (core)   在用U盘装完CentOS后,重新开机启动后显示: Initial setup of Cent ...

  9. 关于学习Knockoutjs--入门(二)

    这两天终于闲一丢丢了,可以有多点时间学习一下拉.接下来要写到的还是Knockoutjs. Knockout是建立在以下3个核心功能之上的: 1. 属性监控与依赖跟踪 2. 声明式绑定 3. 模版机制 ...

  10. lucene5.5 field

    lucene常见Field IntField 主要对int类型的字段进行存储,需要注意的是如果需要对InfField进行排序使用SortField.Type.INT来比较,如果进范围查询或过滤,需要采 ...