鱼鹰  鱼鹰谈单片机 2月19日

预计阅读时间: 5 分钟

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

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

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

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

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

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

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

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

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

以下是 Watch 中的内容:

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

内存情况:

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

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

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

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

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

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

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

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

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

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

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

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

-THE END-

C语言之const的更多相关文章

  1. C语言进阶——const 和 volatile 分析09

    const只读变量: const修饰的变量是只读的,本质还是一个变量 const修饰的局部变量在栈上分配空间 const修饰的全局变量在全局函数区分配资源空间 const只在编译器有用,在运行期无用 ...

  2. 陈正冲老师讲c语言之const关键字

    1.const 关键字也许该被替换为 readolny const是constant的缩写,是恒定不变的意思,也翻译为常量.常数等.很不幸,正是因为这一点,很多人都认为被const修饰的值是常量.这是 ...

  3. C语言关键字const作用及其应用

    只要学过C语言的,都有知道const这个关键字,知道是用来定义常量的,如果一个变量被const修饰,那么它的值就不能再被改变,那么还有什么其他作用呢? 一.const常用作用 1.修饰局部变量 con ...

  4. C语言之const和define

    const修饰的是只读变量,不是常量,其值在编译时不能被使用,因为编译器在编译时不知道其存储的内容.编译器通常不为普通const只读变量分配存储空间,而使将他们保存在符号表中,这使得他成为一个编译期间 ...

  5. c语言,const被绕过

    注意在现在的gcc中, const可能会被用指针绕过. linux: #include "stdlib.h" #include <stdio.h> int t1() { ...

  6. C语言,const

    const意味着“只读” ubunto下的实验 1). 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的.如果你曾花很多时间清理 ...

  7. c语言 - 关键字const的作用

    const修饰的数据类型是指常类型,常类型的变量或对象的值是不能被更新的. 1.const char * p1;              //表示p1指向了的字符串不可更改 2.char const ...

  8. C语言:const关键字用法

    参考博客:https://www.cnblogs.com/bianchengzhuji/p/10335837.html const是constant的简写,是不变的意思.但并不是说它修饰常量,而是说它 ...

  9. C语言:const详解

    希望定义这样一种变量,它的值不能被改变,在整个作用域中都保持固定.例如,用一个变量来表示班级的最大人数,或者表示缓冲区的大小.为了满足这一要求,可以使用const关键字对变量加以限定:const in ...

随机推荐

  1. fatal error C1189: #error : Building MFC application with /MD[d] (CRT dll version) requires MFC sha

    调试程序时出现以下问题:d:\program files (x86)\microsoft visual studio 10.0\vc\atlmfc\include\afx.h(24): fatal e ...

  2. JavaSE基础(八)--Java 循环结构

    Java 循环结构 - for, while 及 do...while 顺序结构的程序语句只能被执行一次.如果您想要同样的操作执行多次,,就需要使用循环结构. Java中有三种主要的循环结构: whi ...

  3. 使用pycharm开发web——django2.1.5(二)创建一个app并做一些配置

    这里我学习的呢是刘江老师的站,主要原因在于他这个版本新,还比较细节 网址先留一手,约等于在引用http://www.liujiangblog.com/ 开始正题: 1.在pycharm界面终端命令行里 ...

  4. IO多路复用技术详解

      IO多路复用:I/O是指网络I/O,多路指多个TCP连接(即socket或者channel),复用指复用一个或几个线程.意思说一个或一组线程处理多个TCP连接.最大优势是减少系统开销小,不必创建过 ...

  5. 剑指offer39:平衡二叉树

    1 题目描述 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 2 思路和方法 平衡二叉树,又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1 ...

  6. python 安装virtualenv和wxPython

    有人说 Virtualenv.Fabric 和 PIP 是 Pythoneer 的三大神器 上一节说过了怎么安装PIP,下面继续安装virtualenv 安装wxPython时比较简单 sudo pi ...

  7. Go语言之依赖管理

    Go语言之依赖管理 Go语言的依赖管理随着版本的更迭正逐渐完善起来. 依赖管理 为什么需要依赖管理 最早的时候,Go所依赖的所有的第三方库都放在GOPATH这个目录下面.这就导致了同一个库只能保存一个 ...

  8. Educational Codeforces Round 71 (Rated for Div. 2) Solution

    A. There Are Two Types Of Burgers 题意: 给一些面包,鸡肉,牛肉,你可以做成鸡肉汉堡或者牛肉汉堡并卖掉 一个鸡肉汉堡需要两个面包和一个鸡肉,牛肉汉堡需要两个面包和一个 ...

  9. windows系统 MySQL8.0.12详细安装步骤及基本使用教程

    转载  https://blog.csdn.net/xiezhiming1234/article/details/82860339

  10. springcloud eureka注册中心分布式配置

    最近在学习springcloud,做下笔记以及记下遇到的坑. 1.建立maven工程,结构很简单,一个启动类和一个配置文件,结构如下图所示 2.启动类代码如下,需要添加注册中心注解:EnableEur ...