在C/C++中,宏,枚举,常量都可以表示“常量”,即不变的值。究竟三者有什么区别,本质是什么。下面来证实一下:
1先上个最简单程序,宏的
test_macro.c
#include<stdio.h>
#define ONE 1
#define TWO 2
#define THREE 3
int main(void)
{
printf("%d\n",ONE);
printf("%d\n",TWO);
printf("%d\n",THREE);
return 0;
}
将其预编译
gcc -E test_macro.c -o test_macro.i
得到下面结果:
//以上省略
# 2 "test_macro.c" 2
int main(void)
{
printf("%d\n",1);
printf("%d\n",2);
printf("%d\n",3);
return 0;
}
可见在预编译阶段宏已被替换
再将test_macro.i 进行编译
gcc -S test_macro.i -o test_macro.s
得:
.file "test_macro.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
movl $.LC0, %eax
movl $1, 4(%esp) #ONE
movl %eax, (%esp)
call printf
movl $.LC0, %eax
movl $2, 4(%esp) #TWO
movl %eax, (%esp)
call printf
movl $.LC0, %eax
movl $3, 4(%esp) #THREE
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
所有的宏都成了立即数
2第二个程序测试枚举
test_enum.c
#include<stdio.h>
enum
{
ONE = 1,
TWO = 2,
THREE = 3
};
int main(void)
{
printf("%d\n",ONE);
printf("%d\n",TWO);
printf("%d\n",THREE);
return 0;
}
将其预编译:
gcc -E test_enum.c -o test_enum.i
得:
# 2 "test_enum.c" 2
enum
{
ONE = 1,
TWO = 2,
THREE = 3
};
int main(void)
{
printf("%d\n",ONE);
printf("%d\n",TWO);
printf("%d\n",THREE);
return 0;
}
不管预编译时期的事
将其编译
gcc -S test_enum.i -o test_enum.s
得:
.file "test_enum.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
movl $.LC0, %eax
movl $1, 4(%esp) #ONE
movl %eax, (%esp)
call printf
movl $.LC0, %eax
movl $2, 4(%esp) #TWO
movl %eax, (%esp)
call printf
movl $.LC0, %eax
movl $3, 4(%esp)#THREE
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
可见在编译时期,枚举被替换成立即数
再看个枚举的例子:
test_enum2.c
#include<stdio.h>
enum Player
{
CURRY,
DURANT,
JAMES,
WESTBROOK,
};
int main(void)
{
Player p1 = CURRY;
Player p2 = DURANT;
Player p3 = JAMES;
p3 = WESTBROOK;
return 0;
}
将其编译(在gcc编译不过的,c++的语法)
g++ -S test_enum2.c
得:
.file "test_enum2.c"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl %ebp
.cfi_def_cfa_offset 8
movl %esp, %ebp
.cfi_offset 5, -8
.cfi_def_cfa_register 5
subl $16, %esp
movl $0, -4(%ebp)
movl $1, -8(%ebp)
movl $2, -12(%ebp)
movl $3, -12(%ebp)
movl $0, %eax
leave
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
可见枚举CURRY,DURANT,JAMES在编译期被替换成了1,2,3,占一个int类型的大小
3.下面看常量的代码:
test_const1.c
#include<stdio.h>
void main()
{
int value1 = 10;
const int nConst1 = 5;
int* pConst1 = (int*)&nConst1;
*pConst1 = 6;
printf("%d\n",nConst1);
}
预编译:gcc -E test_const1.c -o test_const1.i
# 2 "test_const1.c" 2
void main()
{
int value1 = 10;
const int nConst1 = 5;
int* pConst1 = (int*)&nConst1;
*pConst1 = 6;
printf("%d\n",nConst1);
}
不管预编译的事
将其编译 : gcc -S test_const1.i -o test_const1.s 得:
.file "test_const1.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $10, 28(%esp) #变量value1
movl $5, 24(%esp) #常量nConst1
leal 24(%esp), %eax
movl %eax, 20(%esp)
movl 20(%esp), %eax
movl $6, (%eax) #通过指针pConst来改变常量nConst1的值
movl 24(%esp), %edx
movl $.LC0, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
由此可知声明个常量其实跟变量在编译后没有本质的区别,只是在编译器期间,编译器会检查是否有对const类型有修改,若有修改则编译器报错,cosnt完全由编译器控制其不被修改。我们甚至可以使用指针将常量类型去除,从而达到修改常量的值。
而宏是在预编时被替换成表达式,而枚举也在编译时期被展开成了相应的立即数。
可以这样说,宏在预编译时期确定,枚举在编译时期确定。而const常量可以算是在运行时确定。
有时候我们改了一个宏的值,就可能造成怎个项目被重新编译一遍。如果不想浪费这么多的编译时间,有时我们就会推荐使用枚举和常量。
const常量不能算是真正的常量,算个限制型的变量吧。
说的不一定完全正确啊,请多多指正。