“自表达式为甚么无法被重写?有魔力吗?是的,有魔力”
明确提出难题
甚么是自表达式?自表达式是:值无法变动的表达式。假如这般单纯、简练的表述,你都能抽出小毛病,那可能将就吗是在:华而不实了。明确提出难题的人,可能将须要在他们身上找找其原因了。
但你与否考虑过三个难题:是甚么监督机制,在确保自表达式,无法发生改变那个本意呢?要晓得,市售大部份的Coolpix,都是复本、可写、可改的,想企图制止 CPU 的写操作方式,巴藏县呀!
是开发人员的自我管理?却是编译器的坚不可摧?瞧瞧他们的用 CPU 的视点,阐释三个自表达式另一面的故事情节。
标识符预测
关上 Compiler Explorer,表述三个自表达式a,再表述三个表达式b;为的是避免C++强化自表达式 a 的FAT,他们特地在表述自表达式 a 的这时候加之了URL:volatile;再者 volatile 的组织工作基本原理,请查阅“CPU眼中的volatile”。
接著,写三个表达式func1,用以加载并回到自表达式a的值;接着,再写三个函数func2,用以加载并回到表达式b的值:
狡蛛属,千万别重视编订命令的具体内容涵义,他们只较为两者的差别,很或许除a、b的物理地址相同外,三个表达式的编订命令完全一致!C++并没对表达式和自表达式作任何人的界定和特殊处置。
难道,他们又要得到三个耸人听闻的结论:表达式与自表达式,本质上没任何人区别?且慢,他们再看看写操作方式,先给表达式b赋值,编译通过,没难题。
再给自表达式a,赋相同的值:1
inf func1() { a = 1; return 0; }虽然他们并没企图发生改变自表达式a的值,但他们的标识符,却是会被C++无情的拒绝!
看来,自表达式的涵义不仅仅是:它的值不可发生改变,原来它是彻底的拒绝写操作方式呀。即便是你并不打算发生改变它的值。看来 const URL还真能保护自表达式的值,不会被重新写入。
为的是解除C++层面的禁止,他们须要为自表达式a稍微换个马甲,帮助它绕过C++的检查:
如你所见,对自表达式a作三个向普通 int 类型的转换,这样就可以通过编译了。
再较为一下表达式func1和表达式func2的编订命令,如你所见:除a、b的物理地址相同外,它们的编订命令是完全一致的!
好了,这般看来:自表达式a和表达式b,在读、写操作方式上面,都是完全一致的。排除C++在语法层面,对自表达式的保护,他们能否认为:自表达式与表达式的本质是完全一致的呢?
到底与否相同,他们实际运行一下就晓得了。写三个表达式main,先调用一下表达式func2,一切正常;接着他们再调用一下表达式func1:
如你所见,在调用表达式func1的这时候程序出错,回到值:139意味着:段错误(segmentation fault)这是为甚么呢?让他们分别打印:自表达式a和表达式b的物理地址:
如你所见,虽然a、b是依次表述的,但是物理地址的距离,却超过了:8K 字节。它们或许不在同三个内存页里面,如“CPU眼中的程序运行”所说,程序运行前,标识符中的全局表达式、自表达式会被拷贝到数据段。只是那个数据段还会被细分成:只读数据段和复本、写数据段,因此,它们所在的内存页的读、写属性可能将是相同的。
他们猜:表达式b所在的内存页,在MMU映射表中的属性是:复本、可写的;自表达式a所在的内存页,在MMU映射表中属性是:只读的。因此,当他们强行对a进行:写操作方式时,就会触发CPU异常,导致程序崩溃!
因为,内存页的读写属性,不仅对自表达式a所在的内存有效,甚至对整个4KB的内存页,都是有效的。所以,即使企图对a周围的内存,进行写操作方式也是不被允许的:
夸张的说:现代操作方式系统和编程语言的实现,都离不开MMU那个好帮手。更多的MMU知识,还可以查阅“CPU眼中的虚拟内存”。
总结
自表达式并不仅仅是:无法发生改变初值的表达式,也是:不允许对其二次写入的表达式。除此之外,它跟普通表达式一样,也是某个物理地址的别名。C++可以通过对标识符的阐释,制止明显的、针对自表达式的写操作方式。但由于自表达式,跟表达式一样,也只是物理地址的别名。所以开发人员很容易通过指针、或类型转换的方式,逃过C++的检查。真正确保自表达式不被写入的安全阀是:MMU,它能从物理上制止对特定内存的读写。假如自表达式所在的内存页是不复本写的,例如:read only数据段,那么写操作方式会被MMU制止,并产生CPU异常。但假如自表达式所在的内存页是复本、写的,例如:表达式内部表述的临时的“栈”自表达式,由于“堆栈”本身是复本、可写的,所以在逃过C++检查后,“栈”自表达式也是可以顺利写入的。
热点难题
Q1:假如我通过cast强行转换成非const表达式呢?
A1:效果是一样的,你或许可以绕过C++的检查,但在真正作写操作方式的这时候,会被MMU察觉到。
Q2:单片机,例如:STM32,没MMU,它能制止对自表达式的写操作方式吗?
A2:虽然单片机可能将没MMU,但它仍然有可能将制止程序对自表达式的写操作方式。因为,单片机在编译完程序后,往往会通过专门的设备把程序中的:自表达式、表达式,烧写在:ROM上面。
由于ROM的写入过程较为特定,须要配合特定的设备和总线操作方式;CPU无法通过常规的内存读、写命令(例如:MOV命令)来重写ROM上的信息,所以,标识符对自表达式的写操作方式,即使可以顺利运行,但也很难真正重写ROM上的自表达式。