C语言之const
鱼鹰 鱼鹰谈单片机 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的更多相关文章
- C语言进阶——const 和 volatile 分析09
const只读变量: const修饰的变量是只读的,本质还是一个变量 const修饰的局部变量在栈上分配空间 const修饰的全局变量在全局函数区分配资源空间 const只在编译器有用,在运行期无用 ...
- 陈正冲老师讲c语言之const关键字
1.const 关键字也许该被替换为 readolny const是constant的缩写,是恒定不变的意思,也翻译为常量.常数等.很不幸,正是因为这一点,很多人都认为被const修饰的值是常量.这是 ...
- C语言关键字const作用及其应用
只要学过C语言的,都有知道const这个关键字,知道是用来定义常量的,如果一个变量被const修饰,那么它的值就不能再被改变,那么还有什么其他作用呢? 一.const常用作用 1.修饰局部变量 con ...
- C语言之const和define
const修饰的是只读变量,不是常量,其值在编译时不能被使用,因为编译器在编译时不知道其存储的内容.编译器通常不为普通const只读变量分配存储空间,而使将他们保存在符号表中,这使得他成为一个编译期间 ...
- c语言,const被绕过
注意在现在的gcc中, const可能会被用指针绕过. linux: #include "stdlib.h" #include <stdio.h> int t1() { ...
- C语言,const
const意味着“只读” ubunto下的实验 1). 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的.如果你曾花很多时间清理 ...
- c语言 - 关键字const的作用
const修饰的数据类型是指常类型,常类型的变量或对象的值是不能被更新的. 1.const char * p1; //表示p1指向了的字符串不可更改 2.char const ...
- C语言:const关键字用法
参考博客:https://www.cnblogs.com/bianchengzhuji/p/10335837.html const是constant的简写,是不变的意思.但并不是说它修饰常量,而是说它 ...
- C语言:const详解
希望定义这样一种变量,它的值不能被改变,在整个作用域中都保持固定.例如,用一个变量来表示班级的最大人数,或者表示缓冲区的大小.为了满足这一要求,可以使用const关键字对变量加以限定:const in ...
随机推荐
- Spring Boot CommandLineRunner的使用
1. 说明 程序在启动完成的时候需要去处理某些业务,因此Spring Boot程序中需要去实现CommandLineRunner接口. 2. CommandLineRunner方法执行顺序 程序启动后 ...
- 基础python规范
一.注释 合理的代码注释应该占源代码的 1/3 左右,Python 语言允许在任何地方插入空字符或注释,但不能插入到标识符和字符串中间. 在 Python 中,通常包括 3 种类型的注 ...
- varnish CLI管理
命令:varnishadm [-t timeout] [-S secret_file] [-T address:port] [-n name] [command [...]] ./varnishadm ...
- 数据库优化SQL
sql优化规则: 1.对于查询,尽量不要使用全表扫描,尽量在where子句以及order by所对应的字段建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放 ...
- Linux Centos7 离线安装docker 【官网翻译和注释】
Centos7的Docker安装 需要一个维护版本的centos7,所以6不行. 卸载旧版本 旧版本的docker被称为 docker or docker-engine 如果存在请删除它们. sudo ...
- Spring Boot 获取Bean对象实体
一.实现 ApplicationContextAware 接口 package com.zxguan; import org.springframework.beans.BeansException; ...
- js判断变量是否为整数
//返回false则不为整数数字,返回ture则反之 var isIntNumber=function(val){ if (isNaN(val) || Math.floor(val) != val) ...
- iview-admin部署linux nginx报500错误的问题记录
遇到个新服务器部署iview-admin之后 在nginx配置文件有个user配置项 这里需要配置为root或者可以读取本地文件的用户 站点配置如下 server { listen ; server_ ...
- 前端vue项目执行npm install 报错cd() never called()
前端我刚开始接触Vue,从GitHub上下载了代码程序,但缺少一些插件,用vscode打开并下载插件执行报错cd() never called! 解决的方式 1.执行cmd命令行不要再vscode里执 ...
- Swagger学习(二、配置swagger)
基于上一篇 其实只是在SwaggerConfig.class中配置docket,apiInfo @Configuration //变成配置文件 @EnableSwagger2 //开启swagger2 ...