1 符号表
2 代码
每个变量都标了号, 防止看混了.
- int _1_cpp_i_ = 1;
- const int _2_cpp_c_i_ = 1;
- static int _2_cpp_s_i_ = 1;
- void _4_cpp_v_func_i_i_(int, int)
- {
- return;
- }
- int _5_cpp_i_func_i_i_(int, int)
- {
- return 1;
- }
- extern "C" {
- int _6_c_i_ = 1;
- const int _7_c_c_i_ = 1;
- static int _8_c_s_i_ = 1;
- void _9_c_v_func_i_i_(int, int)
- {
- return;
- }
- int _0_c_i_func_i_i_(int, int)
- {
- return 1;
- }
- }
然后编译并查看, 注意这里使用 g++
然后看一下, 下表 4 代表的段, 是那个段:
随便百度一下: rodata 的意义同样明显, ro 代表 read only, 即只读数据(const)
额外补一些:
常量不一定就放在 rodata 里, 有的立即数直接编码在指令里, 存放在代码段 (.text) 中
对于字符串常量, 编译器会自动去掉重复的字符串, 保证一个字符串在一个可执行文件 (EXE/SO) 中只存在一份拷贝
rodata 是在多个进程间是共享的, 这可以提高空间利用率
但是这只是编译器说这段不能修改, 其实真正强制这个段的内容不能更改的是操作系统在分配页的时候, 给页加上的属性.
如果使用 gcc 编译:
使用 gcc 和使用 g++ 编译结果居然一样, 可见, gcc 编译 的时候也使用了和 g++ 一样的标识符修饰规则.
而 extern "C" 的作用, 现在变成了, 按照以前 gcc 的形式修饰标识符(因为现在 gcc 标识符修改规则已经变了)
3 综上
static 和 const 修饰的全局变量, 默认只在本文件中可见.(注意通常是 cc 文件, 因为. h 文件被 include 进去了, 不存在可不可见了)
4 从 elf 符号表角度分析
符号表中的每一项表示一个符号的信息. 记录在结构体中
- struct Elf32_Sym
- {
- Elf32_Word st_name; /* 符号名, 是在字符串表中的下表 */
- Elf32_Addr st_value; /* 符号对应的只, 可能是个地址, 具体跟富豪有关 */
- Elf32_Word st_size; /* 符号大小 */
- unsigned char st_info; /* 符号绑定信息 */
- unsigned char st_other; /* 其他, 目前为 0, 没有使用 */
- Elf32_Section st_shndx; /* 符号所在的段 */
- };
readelf -s 只是将信息汇总展示而已.
其中于本文有关的就是 st_info 和 st_shndx.
前者指明了该标志服是否在文件外可见, 后者指明了标识符所在的段, 间接指明是否可以修改.
注意, 符号表是编译器和连接器之间的约定. 连接器在连接符号的时候, 如果遇到符号的 st_info 字段表明尽在文件内可见, 那么连接器报错, 标识找不到标识符(但其实他找到了. 恩, 真的找到了.)
5 补充
const 定义的全局变量不能被其他文件访问, 必须加 extern 才能被连接.(注意是 cc 文件中, 不能在 .h 文件. 上面说了 .h 文件会被展开)
来源: http://www.bubuko.com/infodetail-2543578.html