全局变量

定义:在所有函数外部定义的变量称为全局变量,一般以g_开头,如

char g_szBuf[100];  //	全局变量g_szBuf

int main()
{
printf("%s\r\n",g_szBuf);
return 0;
}

作用范围(作用域):从声明变量的位置开始到源程序结束,即全局变量可以被在其定义之后的其他函数所共享

  • 当扩展名为.c时全局变量不能以未知值进行初始化,不然会报错:initializer is not a constant(初始化式不是常量)

  • 当扩展名为.cpp时全局变量可以以未知值初始化,如函数的返回值(运行时才能确定返回值)

int GetInt()
{
return 0x87654093;
} int g_nTest = GetInt(); int main()
{
return 0;
}

如果以函数的返回值初始化全局变量,则该函数会在main函数之前运行(这是抢在main函数之前执行的方法之一),如上面的GetInt()函数会在main函数之前运行

以常量初始化全局变量,常量在链接阶段会直接被写进文件中,因此在模块创建时就会在内存中存在这个全局变量,模块卸载才会消失,如图所示:

所以如果想修改全局变量可以直接在文件中修改

已初始化的全局变量放在已初始化数据区,未初始化的全局变量放在未初始化数据区

如果想要文件小一点,全局变量使用未初始化(或初始化为0),这样链接时基本不会增加生成的可执行文件的大小,如下图

如果全局变量初始化且不全为0,链接的时候就会在生成的可执行文件中预留足够的空间,从而使可执行文件大小变大

编译器会将所有未初始化数据归纳起来,求对齐后的总值,然后在链接(link)做可执行程序时记录下这个总值,最后运行时操作系统根据这个预设值再提供足够的空间供未初始化数据使用

void __cdecl _cinit (
void
)
{
/*
* initialize floating point package, if present
*/
#if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC)
/*
* MIPS compiler doesn't emit external reference to _fltused. Therefore,
* must always force in the floating point initialization.
*/
_fpmath();
#else /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */ // 如果存在浮点数
if ( _FPinit != NULL )
(*_FPinit)(); // 初始化浮点协处理器 #endif /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */ /*
* do initializations
*/ // 初始化官方对象,如 cin、cout等
_initterm( __xi_a, __xi_z ); /*
* do C++ initializations
*/ // 我们自己的全局变量在该函数进行初始化
_initterm( __xc_a, __xc_z ); }
#ifdef CRTDLL
void __cdecl _initterm (
#else /* CRTDLL */
static void __cdecl _initterm (
#endif /* CRTDLL */
_PVFV * pfbegin,
_PVFV * pfend
)
{
/*
* walk the table of function pointers from the bottom up, until
* the end is encountered. Do not skip the first entry. The initial
* value of pfbegin points to the first valid entry. Do not try to
* execute what pfend points to. Only entries before pfend are valid.
*/
while ( pfbegin < pfend )
{
/*
* if current table entry is non-NULL, call thru it.
*/
if ( *pfbegin != NULL )
(**pfbegin)();
++pfbegin;
}
}

inline修饰符

  • 告诉编译器,直接把该函数的机器码插入到调用它的地方
  • 不是强制性的,编译器有可能会置之不理,如:递归函数通常不会被编译成内联函数
  • 会检查参数是否合法(编译器级别的检查)

extern修饰符

  • extern 修饰符用来告诉编译器,该函数或变量在外部.obj文件中有定义。链接阶段会在其它文件中找,如果链接阶段在其它文件中找不到,则链接不会通过

    // 告诉编译器在别的文件中查找 g_szBuf 数组
    // 注意 extern 后面的变量不能有实质性动作(不能赋值,不能定义)
    extern char g_szBuf[]; // 错误的。 不能赋值
    extern char g_szBuf[] = "hello world"
    • 声明:没有额外的机器行为,只是知会一下编译器,该变量或函数存在
    • 定义:要说明这个事怎么做,如定义一个函数,那就要定义函数的流程,定义函数的结构。那就要产生机器行为
  • extern "C" 用来标识函数或变量,使用标准的C编译器符号粉碎规则

  • extern 用来实现全局变量和函数的跨文件访问

Bug(vc++6.0)

  • 下面程序会崩溃,不会显示1234。原因是编译器认为下面程序没有浮点运算,所以没有初始化浮点协处理器,而scanf()函数中有浮点运算,所以导致了崩溃。后面高版本解决的办法是:不管程序有没有浮点运算都初始化浮点协处理器

    int main()
    {
    float m = 0.0f; // 加上这句编译器就会初始化浮点协处理器,程序可以正常运行
    float f;
    scanf("%f",&f);
    printf("1234");
    return 0;
    }
  • 下面程序会进入死循环,不会显示1234。早期版本的编译器(VC++6.0),因为goto编译器发现return 0是永远不会抵达的代码段,因此这段代码段不会参与编译,即删除 return 0 (将后面代码的行数往上推,不小心多推了一下,循环多了一次),就变成了NEXT: goto NEXT;自己跳自己,就会一直循环下去

    int fun()
    {
    goto NEXT;
    return 0;
    NEXT:
    ;
    } int main()
    {
    fun();
    printf("1234");
    return 0;
    }
  • vc6.0watch窗口使用C风格名称粉碎查找变量,导致.cpp文件中的静态局部变量不能使用watch窗口查看,解决办法将.cpp文件改为.c文件或者借用全局变量在内存窗口定位

静态变量

定义:变量前加static修饰符

作用:

  • 相当于限制了作用域的全局变量

  • 修饰全局变量或函数,则将该变量或函数的作用域限制在其定义的文件,其余文件无法访问,即使在其余文件中使用了extern修饰,也无法使用

    这是因为编译器在编译的时候虽然依然会编译生成全局变量(在.obj有静态全局变量),但是编译器没有为其生成外部符号的导出,所以外部访问不到。导致链接器找不到该静态全局变量符号,无法链接通过

  • 修饰局部变量,则将该变量的作用域限制在定义的函数,其余函数无法访问。

    • 只会初始化一次,如果以常量进行初始化,则不会产生任何代码,编译链接的时候直接将常量写入文件中(已初始化数据区)

      void fun(int n)
      {
      static int stcnTest = 0x789;
      }
    • 如果以变量进行初始化,为了实现只会初始化一次,会有一个全局的标志。早期在多线程中使用静态局部变量会导致多次初始化的问题(在全局标志修改前变量已经被多个线程初始化)。vs2013以后的版本设置了线程安全(将该标志保证在TLS中),解决了这个问题

      int g_isStcnTestInit = 0;
      void fun(int n)
      {
      if(!g_isStcnTestInit)
      {
      static int stcnTest = n;
      g_isStcnTestInit = 1;
      }
      }
  • 面向对象的萌芽期,类似于私有

    私有的函数和变量使用static限制在文件中,一人负责一个文件互不干涉

检查的标准程度(编译链接阶段检查)
全局变量 跨文件
静态全局变量 不能跨文件
静态局部变量 不能跨函数

名称粉碎机制

  • 一般会有作用域,原先名称和层级三项信息(每个编译器的名称粉碎规则是不一样的)

  • extern "C":不管函数参数类型,所以也就不支持函数重载。而C++中函数的参数类型也会参与到名称粉碎,这也是其支持函数重载的原理

  • vc++6.0集成开发环境中有一个undname工具,可以将名称粉碎后的函数名字还原成名称粉碎前函数名

    C:\Program Files (x86)\Microsoft Visual Studio\Common\Tools\UNDNAME.EXE
void fun()
{
// _?stcTest@?1??fun@@YAXXZ@4HA
static int stcTest = 0x3838438;
printf("%d\r\n",stcTest);
} int main(int argc, char *argv[])
{
// _?stcTest@?1??main@@9@4HA
static int stcTest = 0x1314520; printf("%d\r\n",stcTest); fun(); return 0;
}

寄存器变量

  • 定义时加上类型修饰register
  • 只限于int型、char型和指针类型
  • 如果寄存器使用饱和时,程序将寄存器变量自动转换为自动变量处理,不会通知你
  • debug版本无论声明与否都不会使用寄存器变量(为了方便调试)
  • release版本无论声明与否编译器都会上寄存器变量(有空闲时)

C-08\变量类别和名称粉碎机制的更多相关文章

  1. Revit API取得变量的内参名称

    与取得元素变量的内参名称类别有个BuiltInParameter //取得内参名称 [Transaction(TransactionMode.Manual)] [Regeneration(Regene ...

  2. GNU C++的符号改编机制介绍(函数的名称粉碎格式解析)

    转载:http://blog.csdn.net/roland_sun/article/details/43233565 众所周知,强大的C++相较于C增添了许多功能.这其中就包括类.命名空间和重载这些 ...

  3. Lua中如何实现类似gdb的断点调试—08支持通过包名称添加断点

    在前一篇中我们支持了通过函数名称来添加断点,我们同时也提到了在Lua中一个函数的名称的并不是确定的.准确的说,Lua中的函数并没有名称,所谓名称其实是保存这个函数值的变量的名称. 于是通过函数名称添加 ...

  4. Python变量的本质与intern机制

    变量的存储 a = 'abc' 理解:①先在内存中生成一个字符串‘abc’ ②可以把比变量名a看做一个便利贴,然后将a贴到‘abc’中     ③注意顺序,是生成‘abc’,然后再创建a指向‘abc’ ...

  5. Android-Java-静态成员变量&成员变量&局部变量(内存图&回收机制)

    静态成员变量(回收机制) StaticDemo 和 MyDemo package android.java.oop13; class MyDemo { /** * 定义一个静态变量 */ public ...

  6. Java并发编程实战 第15章 原子变量和非阻塞同步机制

    非阻塞的同步机制 简单的说,那就是又要实现同步,又不使用锁. 与基于锁的方案相比,非阻塞算法的实现要麻烦的多,但是它的可伸缩性和活跃性上拥有巨大的优势. 实现非阻塞算法的常见方法就是使用volatil ...

  7. Java基础以及变量和运算符、包机制、javadoc生成

    目录 注释.标识符.关键字 注释 标识符 关键字 标识符注意点 数据类型 强类型语言 弱类型语言 Java的数据类型 基本类型(primitive type) 数值类型 boolean类型 什么是字节 ...

  8. Python-变量、变量作用域、垃圾回收机制原理-global nonlocal

    变量实现原理决定了Python使用的垃圾回收机制为变量引用计数,当这个对象引用计数为0时候,则会自动执行__del__函数回收资源, del方法只是把变量指向的对象引用计数减一而已并删除这个变量 表达 ...

  9. eclipse点击一个变量使相同名称变量高亮显示的方法

    preferences->java->Editor->Mark Occurences 选择最上的复选框,下面的就有很多了. 其中的Local variables就是变量的高亮显示.

  10. 《Java并发编程实战》第十五章 原子变量与非堵塞同步机制 读书笔记

    一.锁的劣势 锁定后假设未释放.再次请求锁时会造成堵塞.多线程调度通常遇到堵塞会进行上下文切换,造成很多其它的开销. 在挂起与恢复线程等过程中存在着非常大的开销,而且通常存在着较长时间的中断. 锁可能 ...

随机推荐

  1. java将流量KB转换为GB、MB、KB格式

    /** * 转换流量格式为xxGBxxMBxxKB * @param flow 156165(xxxxxx) */ public String changeFlowFormat(String flow ...

  2. 1.nginx学习

    我们平时访问的网络网站就是WEB网络服务,允许用户通过浏览器访问互联网中的各种资源服务 WEB服务器会通过HTTP或者HTTPS的方式将请求内容传递给客户端 目前常见的WEB服务有IIS, Nginx ...

  3. 不一样的纯H5C3动画爱心

    最近抖音很火的让你会计算机的朋友给你做个爱心突然火了,我也不出意外的收到了朋友的邀请,自己做肯定太麻烦了于是乎百度第一步,惊呆了!网上全都是一个爱心,变着法的火焰爱心,换汤不换药,那我们肯定是要整点不 ...

  4. JMeter自定义HTTP组件

    JMeter是一个优秀的开源项目,我们可以在jmeter的官网了解到如何使用和如何二次开发:https://jmeter.apache.org/ 因工作需要,最近做了一个JMeter自定义的http组 ...

  5. winform窗体全局快捷键

    4.使用ShortcutKeys组合键 this.toolStripMenuItem1.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Wind ...

  6. X活手环的表盘自定义修改

    文章用到的所有工具及软件成品 前言 前几天我在某宝买了一个智能手环,无奈软件中的表盘太少,所有我想着修改一下app中的资源文件. 反编译APK 这里反编译APK用apktool工具就可以. apkto ...

  7. 【数据库】union和union all合并结果操作

    一.含义 UNION 操作符用于合并两个或多个 SELECT 语句的结果集. UNION 内部的 SELECT 语句必须拥有相同数量的列.列也必须拥有相似的数据类型.同时,每条 SELECT 语句中的 ...

  8. 【Hadoop学习】中:HDFS、shell操作、客户端API操作、数据流、1NN、2NN原理、DataNode配置

    一.概述 1.背景.定义.使用场景(一次写入.不支持修改) 2.优(容错)缺点(延迟.不支持小文件.不支持修改) 3.组成架构 NameNode:Master,管理命名空间.配置策略 DataNode ...

  9. mysql下载及环境配置

    目录 mysql简介 mysql下载 启动mysql 系统mysql服务的启动 mysql虚拟环境配置 (可以直接看这个) 卸载说明 mysql简介 为什么是mysql? 虽然数据库软件有很多 但是操 ...

  10. [编程基础] C++多线程入门9-async教程和示例

    原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 9 asy ...