C-08\变量类别和名称粉碎机制
全局变量
定义:在所有函数外部定义的变量称为全局变量,一般以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.0的watch窗口使用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\变量类别和名称粉碎机制的更多相关文章
- Revit API取得变量的内参名称
与取得元素变量的内参名称类别有个BuiltInParameter //取得内参名称 [Transaction(TransactionMode.Manual)] [Regeneration(Regene ...
- GNU C++的符号改编机制介绍(函数的名称粉碎格式解析)
转载:http://blog.csdn.net/roland_sun/article/details/43233565 众所周知,强大的C++相较于C增添了许多功能.这其中就包括类.命名空间和重载这些 ...
- Lua中如何实现类似gdb的断点调试—08支持通过包名称添加断点
在前一篇中我们支持了通过函数名称来添加断点,我们同时也提到了在Lua中一个函数的名称的并不是确定的.准确的说,Lua中的函数并没有名称,所谓名称其实是保存这个函数值的变量的名称. 于是通过函数名称添加 ...
- Python变量的本质与intern机制
变量的存储 a = 'abc' 理解:①先在内存中生成一个字符串‘abc’ ②可以把比变量名a看做一个便利贴,然后将a贴到‘abc’中 ③注意顺序,是生成‘abc’,然后再创建a指向‘abc’ ...
- Android-Java-静态成员变量&成员变量&局部变量(内存图&回收机制)
静态成员变量(回收机制) StaticDemo 和 MyDemo package android.java.oop13; class MyDemo { /** * 定义一个静态变量 */ public ...
- Java并发编程实战 第15章 原子变量和非阻塞同步机制
非阻塞的同步机制 简单的说,那就是又要实现同步,又不使用锁. 与基于锁的方案相比,非阻塞算法的实现要麻烦的多,但是它的可伸缩性和活跃性上拥有巨大的优势. 实现非阻塞算法的常见方法就是使用volatil ...
- Java基础以及变量和运算符、包机制、javadoc生成
目录 注释.标识符.关键字 注释 标识符 关键字 标识符注意点 数据类型 强类型语言 弱类型语言 Java的数据类型 基本类型(primitive type) 数值类型 boolean类型 什么是字节 ...
- Python-变量、变量作用域、垃圾回收机制原理-global nonlocal
变量实现原理决定了Python使用的垃圾回收机制为变量引用计数,当这个对象引用计数为0时候,则会自动执行__del__函数回收资源, del方法只是把变量指向的对象引用计数减一而已并删除这个变量 表达 ...
- eclipse点击一个变量使相同名称变量高亮显示的方法
preferences->java->Editor->Mark Occurences 选择最上的复选框,下面的就有很多了. 其中的Local variables就是变量的高亮显示.
- 《Java并发编程实战》第十五章 原子变量与非堵塞同步机制 读书笔记
一.锁的劣势 锁定后假设未释放.再次请求锁时会造成堵塞.多线程调度通常遇到堵塞会进行上下文切换,造成很多其它的开销. 在挂起与恢复线程等过程中存在着非常大的开销,而且通常存在着较长时间的中断. 锁可能 ...
随机推荐
- 安卓APP和小程序渗透测试技巧总结
安卓APP和小程序渗透测试技巧总结 免责声明: 安卓7以上抓取https流量包 证书信任 首先安装OpenSSL,此步骤不再赘述,可以参考百度. 然后安装模拟器(我使用的是夜神模拟器). 导出需要的证 ...
- 2022春每日一题:Day 36
题目:[JLOI2013]删除物品 直接做显然比较复杂,这个题是说对顶栈,但是可以把两个栈拼在一起,记录一下栈顶的下标,然后这样这题就可以转化为线性上的操作查询了,用树状数组简单维护一下就ok了(某个 ...
- Go语言核心36讲29
在上篇文章中,我们主要说的是互斥锁,今天我和你来聊一聊条件变量(conditional variable). 前导内容:条件变量与互斥锁 我们常常会把条件变量这个同步工具拿来与互斥锁一起讨论.实际上, ...
- python面试题常用语句
一.比较与交换1.比较并输出大的 print(a if a>b else b) 2.交换两个元素 a,b = b,alist1[i],list[j]=list1[j],list[i] 二.排序 ...
- devexpress中searchLookUpEdit赋值不显示
给searchLookUpEdit进行赋值的时候使用 string str="123"; searchLookUpEdit1.EditValue = str; 一直不显示或者显示为 ...
- ArcObjects SDK开发 002 写第一个ArcObjects SDK程序
1.开发环境 基于ArcObjects SDK开发,开发环境一般选用Visual Studio,开发语言使用C#,开发包使用ArcObjects SDK for .Net.UI有的使用Winform, ...
- ArcGISServer 10.4 虚拟机 安装 新建站点失败 Failed to configure the server machine ''. Server machine '' is not a local
在通过 VMware 创建的虚拟机上(win7 64位)安装ArcServer 10.4,新建站点时出现下面的错误. Failed to configure the server machine ' ...
- day24 JDBC批处理(通用泛型查询方法 & 下划线转驼峰命名法)
批处理 public static Integer addBatch(String[] sqls){ init(); try { //设置关闭自动提交 conn.setAutoCommit(false ...
- 【Java EE】Day05 JDBC概念、对象、控制事务
一.基本概念 1.概念 Java Database Connectivity:Java数据库连接 2.本质 SUN公司提供的操作所有关系型数据库的规则,是一套接口 各厂商实现此接口,提供相应的驱动ja ...
- JAVA学到方法写了一个四则运算计算器,请教一下有什么需要改进的
package method; /* * 四则运算计算器 * */ import java.util.Scanner; public class Demo07 { public static void ...