C 常量

前言 - 引言

  每次都有点长, 不如来点短的. 轻松的, 当微型小说看的 ......

C++ const 是常量(编译器语法糖 or 直接崩溃), 运行时不可改变(话说程序世界没有不可改变). 

在 C 中 const 语义是不推荐变动的变量, 但不是不能改变, 更不是常量.  看论证例子:

#include <stdio.h>

int main(void) {
    const int pi = 1314;
    int * ptr = (int *)&pi;
    *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

       

posted on 2018-09-19 19:40  喜欢兰花山丘  阅读(361)  评论(4编辑  收藏  举报