Q: 常量和变量的本质区别是什么?
A: 常量是不可改变的"变量", 变量是可改变的"常量". 我中有你你中有我是世界的本质,常量和变量只是为了方便表达特定含义的代名词,它们都是一种实体存在。
我们可能会关注常量和变量在编译器或者运行期表达方式的不同,常量的不可改变特性有优化和保护的可能。
Q: const int i = 1和int i = 1有何差异?
A: const是写给编译器看的, 为防止修改i的代码出现,而汇编代码不受const影响。
const int i = 1;
int i = 1;
对应的汇编代码是一样的:
movl $0x1, %esi
Q: const变量有何种方式可以修改?
A: 我们可以绕过普通的变量赋值手法, 找到变量的地址,用地址解引用的方式绕过编译器的"小脑袋瓜"。
/*
Xi Chen(511272827@qq.com)
cxsjabcabc
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
const int i = 1;
int *p;
// modify i = 2
p = &i;
*p = 2;
printf("%d %d\n", i, *p);
return 0;
}
运行结果:
1 2
虽然我们透过i的地址修改了i的数值, 但是输出i还是最初的数值。这不难理解,编译器早知道i的常量,不管后面怎么间接修改i, printf方式输出i都只会拿最初的数值,但*p就不一样了。
pushq %rbp
movq %rsp, %rbp
leaq 0x35(%rip), %rdi
movl $0x1, %esi // 直接拿常量1
movl $0x2, %edx // 直接拿常量2
xorl %eax, %eax
callq 0x100000f8e
xorl %eax, %eax
popq %rbp
retq
Q: 既然编译器知道const变量不能被修改, 为何不建立一个特别的区域保护const变量避免间接修改呢?
A: 程序区段布局模型决定了如上的代码没办法保护,位于堆栈区域本身可读写,如下放入全局区域才有机会做到。
/*
Xi Chen(511272827@qq.com)
cxsjabcabc
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
const int i = 1;
int main(int argc, char *argv[])
{
int *p;
// modify i = 2
p = &i;
*p = 2;
printf("%d %d\n", i, *p);
return 0;
}
运行:
Bus error: 10
i位于全局区域,而且是只读区域,代码*p = 2修改全局只读区域将被操作系统挡掉。
0x3e(%rip)即是全局只读i的地址:
作者: 陈曦
环境: MacOS 10.14.5 (Intel i5)
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Linux 3.16.83 (Ubuntu)
转载请注明出处