最近在考虑下半年找工作的事情,看了不少面试题目,其中还是蛮有收获的,把基础好好复习了一遍。比如这个题目,static、const现形式,static和const类型的变量在写程序的时候也写了很多,不过对编译器内部对其实现知之甚少。所以借这次机会好好百度谷歌了一番。

static实现形式

我们都知道,static变量只能初始化一次,这个是怎么实现的呢?小齐的网易博客里面作者写的很清楚: 代码如下:

 int main(){
for (int i(); i > ; --i)
{
fun(i) ;
} return ;
} void fun(int i)
{
static int n = i ;
int *p = &n ;
cout << n << " " ; ++n ;
}

因为static变量只能被初始化一次,所以第12行的初始化语句只会被执行一次,但是以后每次都会执行++n的操作,所以输出的结果是:

10 11 12 13 14 15 16 17 18 19

之后作者在VS下面对上述程序进行了DEBUG调试,发现第一次n被赋值之前内存如下:

0042E058  00 00 00 00  ....

0042E05C  00 00 00 00  .... // 中间这个为n的内存地址

0042E060  00 00 00 00  ....

当初始化语句执行完毕,内存内容如下:

0042E058  01 00 00 00  ....

0042E05C  0A 00 00 00  ....// n

0042E060  00 00 00 00  ....

作者继续执行,当for循环执行的时候,再次对static n初始化的时候,却发现n值不会等于i,而是继续保留原来的值。由此作者推断上面的那个0x01是不是就是标志n已经被初始化的标志位。即当要对n初始化的时候,就会检查上面的0042E058单元中对应的标志位是否是1,若是1,则说明已经初始化,若不是1,则进行初始化。

鉴于此,作者改进了fun函数,如下:

void fun(int i)
{
static int n = i ;
int *p = &n ;
cout << n << endl ;
++n ; p--;
*p=;
}

即每次修改0042E058内容清零,使得可以反复对n初始化,做了上述改动之后,函数执行的结果如下:

10 9 8 7 6 5 4 3 2 1

得到证实了,即即当要对static初始化的时候,就会检查上一单元中对应的标志位是否是1,若是1,则说明已经初始化,若不是1,则进行初始化。

之后作者又做了实验,他设定了两个static变量:

void fun(int i)
{
static int n1 = i ;
static int n2 = i ;
int *p = &n1 ;
cout << n1 << endl ;
++n1 ; p--;
*p=;
}

观察单元发现,当执行static赋值之前,内存单元内容如下:

0042E058  00 00 00 00  ....

0042E05C  00 00 00 00  .... // n1

0042E060  00 00 00 00  .... // n2

当执行完static int n1 = i 语句之后,内存的值变成这样了:

0042E058  01 00 00 00  ....

0042E05C  0A 00 00 00  ....

0042E060  00 00 00 00  ....

接着我们再单步static int n2 = i,则执行内存的值变成这样:

0042E058  03 00 00 00  ....

0042E05C  0A 00 00 00  ....

0042E060  0A 00 00 00  ....

这样就很明显了,编译器分别用一位来表示一个static变量是否已经始化。

const实现形式就比较简单了,作者想使用地址的形式改变const常量的值,可是没有成功,作者查看汇编代码,原来是编译器直接将常量类型的换成对应的数值了。这样在执行的时候直接使用数值替换。

至于const类型的变量,大家知道,在c语言中,下面的语句:

const int conVal=;
int *cPointer=&conVal;

是完全可以编译通过的,而且如果后面你通过*cPointer来改变conVal的值,也是大大的可以的,完全没有任何问题,编译器顶多弄一条警告而已。但是如果是在C++编译环境下面的话,再写上面两句话……结果%>_<%啊哦,那是一个错误“invalid conversion from 'const int*' to 'int*' [-fpermissive]|”。这时候如果我们还是想改变他们的值,那该怎么办呢?const_cast就出场了。

const int constant = ;
const int* const_p = &constant;
int* modifier = const_cast<int*>(const_p);
*modifier = ;

编译运行成功O(∩_∩)O~。但是问题又来了,

cout << "constant: "<< constant <<endl;//
cout << "const_p: "<< *const_p <<endl;//
cout << "modifier: "<< *modifier <<endl;//7
cout << "constant: "<< &constant <<endl;//0x22fef4
cout << "const_p: "<< const_p <<endl;//0x22fef4
cout << "modifier: "<< modifier <<endl;//0x22fef4

等等,这谁能告诉我,到底发生了什么事情?同一个地址的东西,我换个方式读取,值就不一样?不过这也证实了一件事情,在C++确实不愧是多了两个+,constant变量必须是constant的,那就是不变,不论怎么cast,我就是不变。

IBM的C++指南:“*modifier = 7;”为“未定义行为(Undefined Behavior)”。所谓未定义,是说这个语句在标准C++中没有明确的规定,由编译器来决定如何处理。

好吧,这指南都说了,未定义行为,由编译器决定处理方式,那咱就不多说什么了吧。不过上面这种使用方式真的只能是纯属测试行为,我们在实际中用到const_cast一般是有两种情况:

定义了一个const变量,但是却需要把这个变量传递到一个没有const修饰的参数的函数中去:

void aTest(int* a){
//do something, this will not change the *a
} int main(){
const int val=;
aTest(const_casr<int *>(&val));
return ;
}

定义了一个普通变量,中间是用了const指针,但是现在确又想把它变回来了……

int variable = ;
const int* const_p2 = &variable;
int* modifier2 = const_cast<int*>(const_p2);
*modifier2 = ;
cout << "variable:" << variable << endl;

其实对于const变量,是没有运行时检查的,只有在编译器编译的时候才会进行检查。而且在某些情况下,是会直接使用const变量进行替换的,如数组长度等情况。某些同学可能还会问const char* str="abdd"这个语句中,里面的字符串为什么有写保护呢?其实这个嘛,可以理解为这个字符串是放在常量区的,放在一个只读属性的页面上面,所以在改写的时候是会出错的。这个跟前面的const定义是没有关系的,因为就算没有const修饰,这个字符串也是不能修改的。

内存中的static、const实现形式的更多相关文章

  1. OC中extern,static,const的用法

    1.const的作用: const仅仅用来修饰右边的变量(基本数据变量p,指针变量*p). 例如 NSString *const SIAlertViewWillDismissNotification; ...

  2. const(每个对象中的常量), static const(类的编译时常量)

    1 每个对象中的常量 --- const数据成员 const限定,意味着“在该对象生命周期内,它是一个常量”. 关键字const 使被限定的量为常量 在该类的每个对象中,编译器都为其const数据成员 ...

  3. c中常用的关键字static const volatile

    在C语言中,关键字static有三个明显的作用:1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变.2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数 ...

  4. C++中类中常规变量、const、static、static const(const static)成员变量的声明和初始化

    C++类有几种类型的数据成员:普通类型.常量(const).静态(static).静态常量(static const).这里分别探讨以下他们在C++11之前和之后的初始化方式. c++11之前版本的初 ...

  5. PC逆向之代码还原技术,第一讲基本数据类型在内存中的表现形式.浮点,指针寻址公式

    目录 代码还原技术 一丶简介代码还原 二丶代码还原中的数据类型表现形式 1.整数类型 2.无符号整数 3.有符号整数 4.浮点数数据类型 5.浮点编码 4.Double类型解析. 三丶浮点汇编 1.浮 ...

  6. static const vs. extern const

    在实现文件(.m文件)中使用static const来定义“只在编译单元内可见的常量”(只在.m文件内可见),由于此类常量不在全局符号表中,所以无须为其名称加类名前缀(一般以k开头). 在头文件中使用 ...

  7. iOS—— static和const联合使用;使用static const 与 #define

    static和const联合使用:   static将一个全局变量变成局部变量   const将一个局部变量变成局部常量 // 定义了一个局部常量      static const CGFloat ...

  8. static const readonly

    C#中的static 和Java中的static 简单,两者用法完全是一致的.从两方面讨论: 1. 变量是属于类的,不是实例级别的.只能通过类名调用,不能通过实例调用. 2. 如果在定义时就赋值了,那 ...

  9. C语言 const, static, static const 的区别

    基本定义: const  就是只读的意思,只在声明中使用;static 一般有2个作用,规定作用域和存储方式. 对于局部变量, static规定其为静态存储方式, 每次调用的初始值为上一次调用的值,调 ...

随机推荐

  1. 3D场景优化

    一) 有效的性能评测 对于任何一个3D应用程序来说,追求场景画面真实感是一个无止尽的目标,其结果就是让我们的场景越来越复杂,模型更加精细,这必然给图形硬件带来极大的负荷以致于无法达到实时绘制帧率.因此 ...

  2. CakeDC(cakephp company)Git workflow--适合于较大团队大型项目开发

    CakeDC Git workflow是一个项目开发和版本发布的工作流,在这个工作流程中开发和版本发布周期是基于几个关键阶段(key phases): Development: 所有活跃的开发活动都由 ...

  3. LA 4725 (二分) Airport

    题意: 有W.E两个跑道,在每个时刻每个跑道的飞机都从0开始编号,而且每个时刻都有Wi和Ei架飞机到达这两个跑道.而且每个时刻只能选择一个跑道的一架飞机起飞.问如何选择才能使得飞机的最大编号最小.(每 ...

  4. codevs 4927 线段树练习5

    赶在期末考试之前把这道傻逼题调了出来. #include<iostream> #include<cstdio> #include<cstring> #include ...

  5. zipline

    history 多只股票时会返回某几只股票停牌没数据 if not symbol(stock) in data: 聚宽 多只股票如果某几只没有发行 600485: nan 多只股票如果某几只停牌 60 ...

  6. 使用dev http client调试restful API开发

    安装chrome 插件:dev http client, 使用VPN打开 google网站,

  7. ActiveMQ之二--JMS消息类型

    1.前言 //发送文本消息 session.createTextMessage(msg); //接受文本消息 public void onMessage(Message msg) { TextMess ...

  8. win7 下配置resin的一些tip

    一.如何查看jdk安装目录: 通过不同方法搜索javac看看, javac.exe 是java的编译器: 可用的搜索方法: 1.cmd 控制台:  where javac 2.开始菜单的搜索: 一直到 ...

  9. MySQL修改root密码的几种方法

    方法1: 用SET PASSWORD命令 首先登录MySQL. 格式:mysql> set password for 用户名@localhost = password('新密码'); 例子:my ...

  10. selenium-grid2 远程并发控制用例执行

    今天闲来无事,随意看了一下selenium,突然注意到grid这个功能以前都是,在读有关selenium的文档时候知道有这么个grid远程控制的功能,但一直没有去试过.所以呢,今天就简单的做了这么个小 ...