如何理解C/C++中的const关键字?

2023-05-29 0 660

const的象征意义是它所表述的统计数据无法透过缓存操作方式修正,但却能透过非缓存操作方式修正,他们上看呵呵const,试试下列的标识符:

const int value = 3;

int main( void )

{

int *p = (int *)&value;

*p = 6; //实际上修正了const表达式value

printf(“a-addr:%d —value:%d/n”, &value, value );

printf(“p-addr:%d —value:%d/n”,p, *p );

}

校对没难题,但运转时能收起,在*p=6的地方性。他们并没隐式修正const表达式value但为什么会在*p=6的地方性收起呢?这表明const对变 量的束缚比他们想像的更为严苛。*p=6或许是一场出访缓存的操作方式,假如出访缓存手忙脚乱,他们具体来说要考量的是:1.该门牌号未被态射进民主化的门牌号内部空间;2. 写了黎贞缓存。依照他们的标识符能窥见1是不可能将的,因而他们考量其原因2,为的是确认吗是其原因2引发的严重错误,他们将标识符更动如下表所示:

const int value = 3;

int main( void )

{

int *p = (int *)&value;

MEMORY_BASIC_INFORMATION mi={0};

size_t n = VirtualQuery(p,&mi,sizeof(mi));

*p = 6;

}

*p = 6的地方性下断点,他们看mi的内容,mi是一个MEMORY_BASIC_INFORMATION结构体,其中Protect字段表示这段缓存的保护属性(关于VirtualQuery和MEMORY_BASIC_INFORMATION结构,且查MSDN),我调试的结果mi的Protect字段为 0x02,即PAGE_READONLY,由此可见,const表述的表达式直接设置了缓存的保护属性,而应用程序所谓的对表达式的修正实际上是修正缓存, 因为表达式总是被放置到缓存中的。设置了const表达式的缓存黎贞属性最终被反映到了页表项上,从而在写黎贞页的时候会导致通用保护异常。这是const 的象征意义,你无论怎样无法“传统”的修正const表达式,但这个表达式吗就无法修正吗?const表达式所限制的仅仅是应用程序,因为它仅仅设置了应用程序的用户门牌号内部空间的虚拟缓存的对应页表项为黎贞,但假如他们找到该const表达式所在的页面,然后再态射到不同的虚拟门牌号或者直接态射到内核内部空间,那么就完全能更动const表达式了,实际上这不是一中标准的做法,也不是推荐的做法,假如仅仅想钻牛角尖的话倒是能一试,这里想说的是,const提供的只 是对该民主化用户内部空间对应的虚拟缓存的写保护,一个表达式是const的唯一象征意义是告诉世界应用程序无法更动此表达式,但别的实体却能更动,比如一个寄存器,在某些能将io态射到虚拟的机器上,我就能将这个寄存器赋给一个表达式,这个表达式是const代表应用程序无法更动寄存器的值,但内核却能,设 备可能将也能。至于怎么寻找const表达式所在的页面这里不讨论,能参考linux的物理缓存和虚拟内存的线性态射,假如这个const表达式所在的页面属于物理缓存的前896M比如是a,那么虚拟门牌号

0xc0000000+a肯定态射了该页面。

最后他们看呵呵应用程序修正const的情况,具体来说声明,const关键字之所以存在,旨在提供一个规范,让程序员不要更动const表达式的,假如更动了就可能将出现大的bug,规范只是规范,假如你不遵守,那么后果是自负,校对器不会强制你修正的,不是校对器不强制你,而是操作方式系统的设计使得它根本就做不到,试看下面的标识符:

int main( void )

{

int i =4;

const int j =3;

int *p1 = (int *)&i;

int *p2 = (int *)&j;

MEMORY_BASIC_INFORMATION mi={0};

SIZE_T n = VirtualQuery(p1,&mi,sizeof(mi));

*p1 = 6; //修正const表达式所在的门牌号指向的缓存,实际上是修正了const表达式i

printf( “value:%d/n”, i );

}

i 吗被修正了吗?实际上i吗被修正了,他们的VirtualQuery函数查p1门牌号的保护属性,查到的mi的Protect字段是0x4,是 PAGE_READWRITE,这样的话,谁也阻止不了你修正p1指向的缓存了,这看起来好像是校对器的失职,但仔细想想校对器也只能做到这了,缓存的页面在操作系统中是按照页面管理的,也是说系统内部是页面对齐的,一个页面的保护属性是一样的(否则就不在页表项中设置保护属性了,一个页表项管一个页 面)。一个页面典型的是4096字节,而他们的const表达式i只有4个字节,非const表达式j也是4个字节,且它们都在main函数的栈帧中(栈的空 间十分有限),系统根本不值得也无法专门为const表达式开辟4096字节的内部空间,按照函数调用栈帧的规范,局部表达式几乎都是顺序排列的,谁也不比谁特殊,系统不会因为栈帧

中有一个const表达式就把它所在的整个4096字节的缓存全部设置为黎贞,因而所查到的mi的保护属性是 PAGE_READWRITE。注意后面的打印,打印出来的i依然是4而不是6,这只是校对器以及const的风格而已,const它确保它表述的表达式i 的值不会变,而不管i所在的缓存是否被修正,这有些拗口,实际上校对器为的是依然遵守const不被修正的约定(事实上你修正了),只好将立即数3打印了出 来,立即数3是const表达式初始化的时候的值。假如说校对器没失职,那么是谁的过错呢?实际上是写程序的人的过错(也是我!),他(我?)这么写表明他根本没明白c语言的最基本的语法…

上面的例子是const表达式在栈中的情形,假如表述成全局表达式的话结果是一样的,但,考量下列标识符:

const int value1 = 3;

int value2 = 4;

int main( void )

{

int *p1 = (int *)&value1;

int *p2 = (int *)&value2;

MEMORY_BASIC_INFORMATION mi={0};

SIZE_T n = VirtualQuery(p,&mi,sizeof(mi));

}

猜 猜看p1和p2会差多少,它们的表述是紧挨着呢,按理说门牌号也应该紧挨着,可是这种情况下它们却没紧挨着,为什么?因为它们没在紧缺的栈中,而是处于全局统计数据区,这里的内部空间就大了去了,因为value1是const的,那么它占据的门牌号所在的页面的保护属性都是黎贞的,再有全局的const表达式也将和 value1公用一个页面,而value2不是const的,因而它无法和value1公用一个页面,因而它们的门牌号相差的不是4而是很多,假如将 value1的const去掉或者将value2加上const,那么它们的门牌号在大多数情况之下吗是相差4。再看:

void ChangeConst( int * ip )

{

*ip=9;

}

int main( void )

{

const int i =3;

ChangeConst((int *)&i);

}

编 译器

虽然看得有些失职,但假如你在调用ChangeConst的时候不把&i(它是const int *)强制转换为int*的话,校对是无法透过的,至于你转换了,出事了(*ip=9),那就谁也别怪了。有一种可贵的精神,假如你想把一件事做成,谁也无 法阻止你,天啊,人家只是一个校对器而已,折磨够了也该休息了。

编写应用程序的时候一定带有内核的思想,反过来也同样,内核的缓存粒度是页面而应用的缓存粒度却是字节,有时候用户内部空间所犯下的严重错误并没错到陷入内核的地步(比如malloc越界等等,malloc越界后,很多时候并不会马上体现出来,只因内核没接手严重错误),那么怎样避免隐含的任何bug,只有靠平时 自己的编程习惯,编写安全,健壮的标识符。

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务