c语言中的只读变量,C语言之不变量(const)

我们知道,数据分为两种,一种为只读,一种为可读可写,为了防止一些不变的数据被程序意外的修改,有必要对它进行保护。这就是 const 的作用。在单片机中,不变的数据(比如代码,比如用户一些固定不变的数据)一般存放在 FLASH 中,而 FLASH 在一般情况下是只读的(事实上可以通过操作修改 FLASH,这种方式称之为 IAP ,即你可以不断更新程序的原因),而一些可变的数据一般是放在 RAM 中的,这些数据可能被指针意外的修改。所以就需要通过一种方式来将这些数据存放在 FLASH 中以防意外修改,而最好的保护就是硬件水平上的支持,这就是 const 的作用,它可以将你的数据放在 FLASH 中,但它的作用不只是如此。

const: 限定一个变量不允许改变,产生静态作用,const 在一定程度上可以提高程序的安全性和可靠性。

const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。

声明为 const 的变量是不能被用户改变的(意思就是说你不允许你通过代码去修改这个值),因为编译器会将该变量放在只读区,比如在 KEIL 开发平台下,声明为 const 的变量放在 FLASH 区,这样即使你使用取地址符 & 获取声明为 const 变量地址,并通过指针进行修改,虽然编译器不报错,但也是无法进行修改的,因为 STM32 进行 FLASH 编程是有条件的。

f936f2cf9841c8385c12160fc7b011d6.png

你会发现虽然 p 获取了 N 的地址,但因为 N 存放在 FLAH(0x0800 0000 开始地址是 FLASH)中,所以即使通过指针的方式也是无法间接改变N的值的。

eddab0ad3bb3ca582c485281beffe82f.png

可以看到运行 *p = 4 的代码后,N 并没有发生改变。但是编译器确实也没报错。但是如果你直接使用 N = 4; 的话,肯定是报错的,因为你的N已经被申明为 const 了。

17b7937562f78c6b209fb93c348109ac.png

《C语言深度剖析》中关于 const 的介绍发现和 KEIL 情况不一样。

2ea0f4e391198b2ea420ddc19ced9688.png

在 KEIL 中进行相关代码的编写,编译,最后可以看到如下结果:

49c9e997d147aa44877e0c1e6c609602.png

这是仿真模式下两个地址的内容,一个存放在 FLASH,一个存放在 RAM 中,并且当修改 FLAH 的内容之后(因为是软件仿真模式,可以直接修改值),复位重新运行,你会发现 RAM 的内容对应改变了(重新运行后,进入 main 函数之前,有一段拷贝代码,就是函数外申明的一些变量的初始化过程),这就说明,在 STM32、KEIL 环境下,并不是《C语言深度剖析》中说的只有一份内存,而是每一个都有一个,申明为 const 情况跟使用宏定义的方式是一样的。

以下是 Watch 中的内容:

c17c05762f76e0d1f7cf3bcf5edd49dd.png

但其实上面的结论是在使用 & 将 N 的地址获取后的结果(从上图可以看到 p 的值),实际上代码中如果没有获取 N 的地址时,情况又不一样了。

内存情况:

feaec72ce861a0143844cd02b276fb12.png

在删去获取 N 地址后的内存情况,可以发现 N 的值为 0x2000470,和 FLAH 地址 0x08000000 一样。

f377e36c097bba5eda3cd8d065bd33d6.png

这像一个地址。但通过 Memory 查看这个地址发现存放的不是 5。

7e48e66f4a8588c16dc94b5f1bef1cc8.png

根据 ARM 内核的知识可以知道,0x08000000 地址存放的其实是栈顶指针,也就是说 N 存放的是栈顶指针吗?显然不是。

然后对 .map 地址映射文件进行搜索,你会发现,根本没有 N 的地址。这样说来,N 在内存的位置对用户是不可见的,而是由编译器自动处理了。

那么有没有办法找到这个拷贝源头呢。之前我说过,先前能找到拷贝的源头纯属偶然,有没有什么方法可以找到呢?这其中的难点就是进入 main 函数之前的那段拷贝代码不是我们用户自己写的,而是 C 编译器自动处理的,怎么办?

这个时候就需要请出一个关键人物:数据观察点(关于数据观察点,将有专门的一小节详细说明,感兴趣的可以关注我)。

我们知道,不管如何,因为 FLASH 存放着变量初始值,然后在程序运行的时候才将 FLASH中的值初始化到 RAM 中去,也就是我们使用的 RAM 变量,那么必然存在通过总线进行数据传输的过程,所以可以通过数据观察点的功能实现对地址的监控,虽然我们不知道 FLASH 的地址,但是我们知道 RAM 的地址,所以只要对变量i进行监控,就可以通过内核的寄存器找到 FLASH 地址了。如下:

14538c7898c6fa62ab17d3418f66026e.png

这里的 0x20000000 就是 i 的 RAM 地址,最终可以找到 FLASH 的位置:

cc0176993a83ef144f7d34193629b3b1.png

由此可以知道,FLAH 中也是有多个相同副本存在的。

因此可以得出结论,在 STM32、KEIL 的环境下,《C语言深度剖析》对于两者的说法在这里不适用。

看了那么多,没有足够的基础是很难知道我在讲什么,下面用一张图进行说明,希望可以解答你的疑惑。

3f16dd43cde37e9420f26cbffff2445d.png

希望你在看完这张图之后,再回过头看看前面的那些话,对你应该会有帮助的。

喜欢本系列 C 语言文章的可以关注我哦!

举报/反馈

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值