栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

万物皆“指针”

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

万物皆“指针”

万物皆“指针”
  • 前言
  • 测试
  • 总结
  • 最后

前言

众所周知,C/C++的知识的精髓是指针,指针的本质是内存地址,可无论是普通变量,还是类的成员变量,谁还能没有一个内存地址呢,既然普通变量有内存地址,那我们也能像对指针变量那样对普通变量进行操作,也就是指针的 * 操作和 -> 操作。

测试

打开compiler explorer,其网址如下:https://gcc.godbolt.org/

定义一个普通的变量a,写一个func_1函数,借用指针变量p,通过 * 操作,来给变量a赋值,再写一个函数func_2,不借助指针变量,直接对变量a的地址进行 * 操作来给变量赋值

int a = 0;

void func_1()
{
   int* p = &a;
   *p = 1;
}

void func_2()
{
    *(int*)&a =1;
}

对比一下汇编,func_1函数 用了三条指令,func_2就简洁多了,直接对变量a的内存地址写:1,仅一条指令就完成了赋值,如你所见,普通变量也能做指针 * 操作,只要知道变量a的地址,就可以进行指针的 * 操作,还省掉了指针的开销,更简单直接。

当然还可以把func_2写的更极端一些,假设a的内存地址是:ox1234我们就可以把func_2函数改写成这样子

我们再写一个函数func_3,用最常规的方式给变量a赋值

int a = 0; //a address = 0x1234

void func_1()
{
   int* p = &a;
   *p = 1;
}

void func_2()
{
    *(int*)&a =1;
}

void func_3()
{
    a =1;
}

可以发现func_2函数和func_3函数汇编指令完全相同

所以如你所见,你最熟悉的变量读写都等同于对变量地址的指针 * 操作,正如变量的定义所言,变量不过是内存地址的别名,同样的道理,不借助指针变量,我们也可以通过指针的 -> 操作,对类的成员变量赋值

class A{
    public:
    int x;
} a;

void func_c1()
{
    a.x = 1;
}

void func_c2()
{
    (&a)->x = 1;
}

总结

1、指针操作不是指针变量的专利,普通变量甚至立即数也可以做指针(* 、->)操作

注意不能出现下面的表达式,因为&a在这里是常量,不能单独出现在等式左边,同时要注意引用&与取地址&的区别,参考:指针变量的传值、传址和传引用

&a = (int*)0x1234

也不要出现下面的表达式,很显然 " * " 的操作数必须是指针

* 0x1234 = 1

2、计算机的世界里面,万物皆有地址,所以万物皆可指针,你既可以循规蹈矩通过变量名或规定的函数接口读写变量,也可能无视规则,通过指针操作,随意随时随地的读写变量
3、除了0x1234,指针操作可以读写任意的内存地址,除了内存管理单元MMU,没人能制止这种读写行为,这也是早期 “游戏修改器” 的工作原理

最后

正是指针操作的灵活性,让他成为大神和黑客的最爱,很多大神仅仅通过一个栈变量(a)的地址,配合指针操作,就可以试探、回溯出整个函数的调用轨迹。同时指针操作的不可控性也是大规模编程的噩梦,试想一下,你得到了某个“私有变量”的内存地址,就意味着你可以通过指针操作不受任何限制的读写这个变量,这时,你就不会再理会它“禁止访问”的私有属性了,你也不再愿意循规蹈矩的通过成员函数来读写它。

所以暴露任何数据、函数的内存地址都是巨大的风险,因为这些地址都可以用来做违规、不受控的指针操作。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/675616.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号