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

static实现形式

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

 int main(){
     ); i > ; --i)
     {
         fun(i) ;
     }

     ;
 }

 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语言中,下面的语句:

;
int *cPointer=&conVal;

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

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

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

cout <<
cout <<
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(){
  ;
  aTest(const_casr<int *>(&val));
  ;
}

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

;
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. static const vs. extern const

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

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

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

  4. static const readonly

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

  5. 【转】C++ 类中的static,const,及引用类型的初始化

    文档主要来自:http://blog.csdn.net/yjkwf/article/details/6067267 1. static类型 用static可以为类类型的所有对象所共有,像是全局对象,但 ...

  6. typedef,static,const用法

    一.typedef主要功能是定义一个已存在类型的别名,但是和宏并存 宏与typedef区别 1.宏定义只是简单的字符串替换 2.typedef定义的类型是类型的别名,typedef后面是一个整体声明, ...

  7. C/C++中关键字static的用法及作用

    本文将主要从static在C和C++共有的作用及C++特有的作用两个方面进行解析. 在C和C++中共有的作用 隐藏(对变量.函数均可) 当同时编译多个文件时,所有未加static前缀的全局变量或全局函 ...

  8. Java--自定义Class并且在内存中编译,加载,实例化

    本文的目的: 使用者在程序运行期间,可以动态的写Java Class,不需要生成任何.Class文件就可以完全在内存中编译,加载,实例化. 1.需要用到的组件介绍 1)JavaCompiler:用于编 ...

  9. EF如何操作内存中的数据以及加载相关联表的数据:延迟加载、贪婪加载、显示加载

    之前的EF Code First系列讲了那么多如何配置实体和数据库表的关系,显然配置只是辅助,使用EF操作数据库才是每天开发中都需要用的,这个系列讲讲如何使用EF操作数据库.老版本的EF主要是通过Ob ...

随机推荐

  1. c++局域网多播

    转自http://www.51cto.com/specbook/17/35216.htm Visual C++实现局域网IP多播 在局域网中,管理员常常需要将某条信息发送给一组用户.如果使用一对一的发 ...

  2. SQL语句/函数汇总

    1.CHARINDEX(短字符A,长字符B) 说明:返回A在B的位置,从1开始,若B中不存在A,则为0 例如: SELECT CHARINDEX('aaaa','abaaaacded')  ----- ...

  3. 仿浏览器TAB效果

    仿浏览器的Tag标签 这里先上个非常非常简陋的demo,没加CSS,我先把jquery的源码给全部搞通,在专心把这个功能给讲一下 <!doctype html> <html lang ...

  4. Graph cuts图论分割

    Graph cuts是一种十分有用和流行的能量优化算法,在计算机视觉领域普遍应用于前背景分割(Image segmentation).立体视觉(stereo vision).抠图(Image matt ...

  5. PHP基础班初学心得:用JQ实现表单的全选、反选、取消和删除功能

    摘要: 本人刚参加PHP基础班培训,由于之前毫无基础,分享的心得可能不规范,方法也许也"旁门左道",不能保证质量,只作自己总结学习,也希望能帮助到同样是初学者的朋友们,共同进步. ...

  6. Direct3D11学习:(九)绘制基本几何体

    转载请注明出处:http://www.cnblogs.com/Ray1024 一.概述 Direct3D中很多复杂的几何效果都是由基本的几何体组合而成的,这篇文章中,我们来学习集中常见的基本几何体的绘 ...

  7. 两个和尚抬水有水喝,三个和尚抬水没水喝------IT项目管理之组织架构

    说到项目经理岗位,一般的想法是,一个项目只能有一个项目经理,否则责任不明,互相推诿.偏偏IT项目需要有两个甚至三个项目经理.原因何在呢? 典型的IT项目(不包含纯技术或工具类项目)是把用户的需求转化成 ...

  8. HTTP - 内容编码

    HTTP 应用程序有时在发送之前需要对内容进行编码.例如,在把很大的 HTML 文档发送给通过慢速连接上来的客户端之前,服务器可能就会对它进行压缩,这样有助于减少传输实体的时间. 内容编码过程 内容编 ...

  9. BZOJ 1004 Cards(Burnside引理+DP)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1004 题意:三种颜色的扑克牌各有Sr,Sb,Sg张.给出m种置换.两种染色方案在某种置换 ...

  10. c# 利用反射动态给实体类对象赋值

    转:http://blog.sina.com.cn/s/blog_659a572b0100xp5s.html 例子如下 using System; using System.Collections.G ...