C 常量
前言 - 引言
每次都有点长, 不如来点短的. 轻松的, 当微型小说看的 ......
C++ const 是常量(编译器语法糖 or 直接崩溃), 运行时不可改变(话说程序世界没有不可改变).
在 C 中 const 语义是不推荐变动的变量, 但不是不能改变, 更不是常量. 看论证例子:
#include <stdio.h> int main(void) { const int pi = 1314; int * ptr = (int *)π *ptr = 520; printf("pi = %d\n", pi); return 0; }
输出结果如下: (如果是 .cc 在 cl 中输出是 1314, 在 g++ 段错误[时间 2018/09/19])
$ gcc -g -Wall const.c ; ./a.out pi = 520
上面代码, 常在 "C 中没有常量" 论证中出现 (像 1, "你好", '\0' 这些是字面量, 多数会编译到代码区)
本篇文章主题是 C 中构建常量. 总有办法 ~ 达到效果. 能够想到强加意义就是, 真不希望变量被有
心人改动. 开始之前赠送一个不错的语法, 目前时间点在最新的 cl 和 gcc 中对此不再有 warning
#include <stdlib.h>
/* Free a block allocated by `malloc', `realloc' or `calloc'. */ extern void free (void *__ptr) __THROW; void buf_del(buf_t b, void * m, size_t sz) { if (b->size != sz) return free(m); ... }
return free(m); 语法源自标准手册, 在 C 中 void 也是个类型. 整体而言节约了几行代码 ~
写起来更紧凑些吧 :)
正文 - 思路
基础面试常会碰到这样一个问题, C 程序中有那些存储变量的区域? (多数问程序分布区域, 挺多
的, 需要查询专业书籍, 再重温几遍高级程序员自我修养). 多数会说 普通变量在栈上, 初始化的 static
或 全局变量在初始化全局区, 未初始化 static 或 全局变量在未初始化全局区, 字符串数字字面量在代
码区. 这时候有些人会忽略 reigster 变量尝试保存在寄存器区(加分项, 自我感觉这个问题挺好, 大家都
会, super 难, 还能方便大佬引申). 同样 register 引出了我们的话题
const register int hoge = 110;
不妨修改验证一下
1 #include <stdio.h> 2 3 int main(void) { 4 const register int hoge = 110; 5 (int)hoge = 520; 6 printf("hoge = %d\n", hoge); 7 return 0; 8 }
$ gcc -g -Wall register.c register.c: In function ‘main’: register.c:5:15: error: lvalue required as left operand of assignment (int)hoge = 520;
提示左值不可修改错误. ((int)hoge 已经被强转成左值). 不妨再来一个
1 int main(void) { 2 const register int hoge = 110; 3 int * ptr = (int *)&hoge; 4 *ptr = 520; 5 printf("hoge = %d\n", hoge); 6 return 0; 7 }
$ gcc -g -Wall register.c register.c: In function ‘main’: register.c:5:5: error: address of register variable ‘hoge’ requested int * ptr = (int *)&hoge;
提示 register variable 变量没有地址, 无法转换错误.
通过 register 构建 int, unsigned const 常量是没有问题. 那 float, double char * or struct nuion ...
且看下文 ~
话说 register 申请放入寄存器, 不在五行之中. 毕竟空间有限, 这种奥妙玄幻用不了太多. 对
于 char * 如何处理呢. 有的朋友会抛出
const char * const heoo = "hello, 世界";
这种字面量表示方式出发点是 "字面量不可变". 但恰恰编译器很多, 也有很多参数配置. 有些编译
器特定配置或默认配置是可以改变字符串字面量的. 所以这种做法是不可控的, 未定义的.
因而构造另一种修真套路, 宏(本质还是构建左值 lvalue)
#include <stdio.h> #ifndef const_str_hoge inline static const char * const const_str_hoge(void) { return "白龙马"; } #define const_str_hoge const_str_hoge() #endif int main(void) { printf("const_str_hoge = %s\n", const_str_hoge); return 0; }
$ gcc -g -Wall chas.c ; ./a.out const_str_hoge = 白龙马
是不是感觉大江随浪飘, 修真无岁月. 同样对于 float 也可以是这样
#ifndef const_float_pi inline static float const_float_pi(void) { return 3.14; } #define const_float_pi const_float_pi() #endif
再补充一个 struct
#include <stdio.h> struct version { int major; // 主版本号 int minor; // 副版本号 int micro; // 子版本号 }; #ifndef const_version inline static const struct version const_version(void) { return (struct version){.major = 1, .minor = 2, .micro = 3}; } #define const_version const_version() #endif int main(void) { printf("version = { major = %d, minor = %d, micro = %d}\n", const_version.major, const_version.minor, const_version.micro); return 0; }
$ gcc -g -Wall struct.c ; ./a.out version = { major = 1, minor = 2, micro = 3}
单纯的运行时中是无法修改这些特殊构造出来的常量. 而且通过编译器无法编译通过,
也会让有心人放弃犯错误 :) 好的制度应该考虑让想犯错的如何难犯糊涂 ~
后记 - 展望
错误是难免的, 欢迎交流 ~
<<你一定要是个孩子>> - https://music.163.com/#/song?id=487379098