全局变量

定义:在所有函数外部定义的变量称为全局变量,一般以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. Node.js 的学习(四)分别连接MongoDB与MySQL数据库,实现增删查改功能

    一.Node.js 访问MongoDB 数据库 MongoDB 对许多平台都提供驱动可以访问数据库,如C#.Java.Node.js等. 1.1.安装MongoDB访问驱动 命令如下: 全局安装驱动: ...

  2. WIN10使用SSH连接VMWare16 Pro的CentOS8.4(保姆级)

    目录 本机环境 配置连接 本机环境 连接工具用的是MobaXterm,下载地址https://mobaxterm.mobatek.net/download.html VMWare16 Pro Cent ...

  3. 【RPC和Protobuf】之RPC入门

    一,概念 RPC:Remote procedure call(远程过程调用),分布式系统中不同节点之间流行的通信方式 服务端: 注: 1.执行下面的代码之后,会相应的启动一个tcp进程 C:\User ...

  4. 2018 Web开发人员学习路线图

    以下 Web 开发人员学习路线图是来自 Github developer-roadmap 项目,目前已经有繁体版翻译 developer-roadmap-chinese. 主要有三个方向,分别为前端开 ...

  5. 当resource bundle 的多语言文件里包含引号'时

    背景 项目中使用Spring的ReloadableResourceBundleMessageSource这个类来实现多语言,有一次字符串里包含引号'时,解析时出了问题,一起来看一下吧 例子 resou ...

  6. WINDOWS下对NIGNX日志文件进行限制

    首先接到这个任务,发现nginx的日志限制更多的都是在Linux下做的,找了半天,也没找到能直接通过nginx.conf更改体现到日志限制上的. 最后决定直接通过bat脚本,来对nginx的日志进行分 ...

  7. mysql-front连接mysql报错1251解决方法

    客户端不通过服务器的验证,建议升级mysql客户端 出现错误的原因是mysql版本问题,由于mysql8.0版本与mysql5.0版本加密方式不同,导致mysql-front无法通过验证. mysql ...

  8. Iris_data_analysis

    SVM调用实例--鸢尾花 任务描述: 构建一个模型,根据鸢尾花的花萼和花瓣大小将其分为三种不同的品种. 数据集: 每一行数据由4个特征值及1个目标值组成,4个特征值分别为:萼片长度.萼片宽度.花瓣长度 ...

  9. 秒级查询之开源分布式SQL查询引擎Presto实操-上

    @ 目录 概述 定义 概念 架构 优缺点 连接器 部署 集群安装 常用配置说明 资源管理安装模式 安装命令行界面 基于Tableau Web 连接器 使用优化 数据存储 查询SQL优化 无缝替换Hiv ...

  10. 【环境搭建】RocketMQ集群搭建

    前置条件及效果图 条件: 两台服务器,个人是两台腾讯云服务器(其中嫖的朋友一个): 版本: rocketmq-version:4.4.0 rocketmq-console(mq控制台) Java:1. ...