指针的一些有趣现象
ฅ'ω'ฅ♪

指针的一些有趣现象

常量折叠

首先看下面一段代码

#include <bits/stdc++.h>
#define pf(a) printf("%d\n",a) ;
using namespace std ;

signed main()
{

    const int num_1 = 10 ;
    int *ptr_1 = const_cast<int*>(&num_1) ;
    *ptr_1 = 100 ;
    pf( num_1 ) ;
    pf( &num_1 ) ;
    pf( ptr_1 ) ;
    pf( *ptr_1 ) ;
    pf( const_cast<int&>(num_1) ) ;

}

预测一下结果?

结果是

10
7667268
7667268
100

首先发现的问题是

  1. 直接输出num_1,好像因为const而没有被改值;
  2. num_1的地址和ptr_1指向的地址是一样的;
  3. 去除num_1的const限定符之后输出num_1,发现num_1的值发生了变化;

特别说明一下

C++标准转换运算符const_cast

C++提供了四个转换运算符:

  1. const_cast <datatype> (expression)
  2. static_cast <datatype> (expression)
  3. reinterpret_cast <datatype> (expression)
  4. dynamic_cast <datatype> (expression)

在这里不多讨论,详情可以点击《IBM的C++指南

  1. const_cast<int*>
  2. const_cast<int&>

在这之后可以去除num_1的限定修饰

然后通过指针修改了num_1的值

实现原因就在于C++对于指针的转换是任意的

它不会检查类型,即是任何指针之间都可以进行互相转换

而且

ptr_1指向的地址与num_1是一样的,这个事实也辅佐验证了上述行为的合理性。

但是为什么直接输出num_1,还是显示原本的值。

其中的关键是“常量折叠”编译优化技术

不同于宏,此举动也会分配内存空间。

"常量折叠"是 就是在编译器进行语法分析的时候,将常量表达式计算求值,并用求得的值来替换表达式,放入常量表。

所知道的是:

被优化的常量 ,因为不会被修改,所以在运行的时候会从寄存器中直接读取数据。

而强制修改之后,内存中的值发生了改变,但是寄存器中的数据并“跟不上”。

为了佐助这种说法,我们可以给num加上volatile修饰

这样num就是“易变的”,强制每次都从内存中读取数据

volatile const int num=0 ;
int *ptr = (int *) &amp;num ;
*ptr = 1 ;
pf( num ) ;

输出结果是

1

迷途指针

signed main()
{
    int *ptr = (int *)malloc( sizeof(int)*10 ) ;
    pf( ptr ) ;
    for( int i=0 ; i&lt;10 ; ++i )
    {
        ptr[i] = i ;
        pf( ptr[i] ) ;
    }
    free( ptr ) ;
    pf( ptr ) ;
    pf( ptr[9] ) ;
    ptr = NULL ;
    pf( ptr ) ;
}

代码输出是:

857296
0
1
2
3
4
5
6
7
8
9
857296
9
0

也就是 free之后,读取ptr指向的内存,仍然存在!

大家都知道:

  1. 任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的
  2. new 对应的必须 delete

现在看来,free掉的只是内存的地址,ptr所指向的地址却没有变

再尝试以下:

    int *ptr = new int ;
    *ptr = 9 ;
    pf( ptr ) ;
    pf( *ptr ) ;
    free( ptr ) ;
    int *ptt = new int ;
    pf( ptt ) ;
    *ptt = 1000 ;
    *ptr = 10 ;
    pf( ptr ) ;
    pf( ptt ) ;
    pf( *ptr ) ;
    pf( *ptt ) ;

输出:

2036944
9
2036944
2036944
2036944
10
10

非常有趣的是,后一个指针指向了前一个指针同样的地址,而后续的改变大概也可以理解。

所以说,不管是free还是delete,之后都要将指针置NULL

另一个是

不要返回指向栈内存的指针,因为指向一个已经被释放了的内存是相当危险的。

“真空”的内存?

这个其实跟指针的关系并不大,主要是内存分配的问题

首先是:

void fuc()
{
    int *ptr ;
    pf( ptr[-1] );
    ptr[-1] = 100 ;
}

signed main()
{
    int a , b , c=5 ;
    fuc() ;
    pf( c ) ;
}

输出

5

10

这应该非常好理解,只要稍微了解

代码区、常量区、静态区(全局区)、堆区、栈区

就应该可以知道其中原理。

但是问题在于,这种“寻址”十分不稳定,只需简单的语句就可以破坏

void fuc()
{
    int *ptr ;
    for( int i=0 ; i&lt;10 ; ++i ) ;
    pf( ptr[-1] );
    ptr[-1] = 100 ;
}

改为这样之后,程序就会崩溃。

还有这种情况

signed main()
{
    int a=1 , b=2 , c=5 , *ptr=&amp;c;
    //fuc() ;
    pf( ptr[-1] ) ;
}

是相当难正确寻址的!

再不说在不同机器上的情况了。

但是可以看看以下的:

signed main()
{
    int *a=(int *)malloc( sizeof(int)*8);
    a[7] = 11 ;
    a[6] = 12 ;
    a[5] = 13 ;
    a[4] = 14 ;
    a[3] = 15 ;
    int *b=(int *)malloc( sizeof(int)*5);
    for( int i=1 ; i&lt;=5 ; ++i )
        pf( b[ -sizeof(int) -i ] ) ;
}

输出结果

11
12
13
14
15
signed main()
{
    int *a=(int *)malloc( sizeof(int)*8);
    a[7] = 11 ;
    a[6] = 12 ;
    a[5] = 13 ;
    a[4] = 14 ;
    a[3] = 15 ;
    for( int i=1 ; i&lt;=100 ; ++i ) ;
    for( int i=1 ; i&lt;=100 ; ++i ) ;
    puts("");
    int qwq=10;
    int *b=(int *)malloc( sizeof(int)*5);
    for( int i=1 ; i&lt;=5 ; ++i )
        pf( b[ -sizeof(int)-i ] ) ;
}

不会改变结果。

要想破坏这种状态,进入同样的空间即可。

所以其实这种类似真空的状态,要说难以理解其实也不然。

指针与内存的妙处,还有很多,难以详尽

CANCEL

-评论-

Here you can post what you want to say, if you have more information please contact me by the following way.

-昵称-
-QQ-
-邮箱-
想说些什么?
-SUBMIT-

-电联 Phone-

+86 18520664652

-邮箱 Email-

boogieLing_o@163.com

boogieLing_o@qq.com

Your name. OS platform Browser model

What do you want to say?

created time

游說萬乘苦不早,著鞭跨馬涉遠道。

阿凌的貓爬架

幸會,

激活Ubuntu

转到“设置”以激活Ubuntu。

R0's board.