前言 - 引言程序員
每次都有點長, 不如來點短的. 輕鬆的, 當微型小說看的 ......面試
C++ const 是常量(編譯器語法糖 or 直接崩潰), 運行時不可改變(話說程序世界沒有不可改變). ui
在 C 中 const 語義是不推薦變更的變量, 但不是不能改變, 更不是常量. 看論證例子:spa
#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])code
$ gcc -g -Wall const.c ; ./a.out pi = 520
上面代碼, 常在 "C 中沒有常量" 論證中出現 (像 1, "你好", '\0' 這些是字面量, 多數會編譯到代碼區)blog
本篇文章主題是 C 中構建常量. 總有辦法 ~ 達到效果. 可以想到強加意義就是, 真不但願變量被有字符串
心人改動. 開始以前贈送一個不錯的語法, 目前時間點在最新的 cl 和 gcc 中對此再也不有 warning get
#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 也是個類型. 總體而言節約了幾行代碼 ~編譯器
寫起來更緊湊些吧 :)it
正文 - 思路
基礎面試常會碰到這樣一個問題, 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