以下是《C++ Primer 中文版(Edition 4)》 7.2.1非引用形参 -- 2 const 形参 章节中的一段话:
在调用函数时,如果该函数使用非引用的非const形参,则既可以给该函数传递const实参也可传递非const实参:
const int i = 3, j = 6;
int k = rgcd(3, 6); // ok: k initialized to 3
这种行为源于const对象的标准初始化规则。因为初始化赋值了初始化式的值,所以可用const对象初始化非const对象,反之亦然。
如果将形参定义为非引用的const类型:
void fcn(const int i) { /* fcn can read but not write to i */ }
则在函数中,不可以改变实参的局部副本。由于实参仍然是以副本的形式传递,因此传递给函数的既可以是const对象也可以是非const对象。
尽管函数的形参是const,但是编译器却将fcn的定义是为其形参被声明为普通的int型:
void fcn(const int i) { /* fcn can read but not write to i */ }
void fcn(int i) { /* ... */ } // error: redefines fcn(int)
这种用法是为了支持对C语言的兼容,因为在C语言中,具有const形参或非const形参的函数并无区别。
“编译器却将fcn的定义是为其形参被声明为普通的int型"
这句话的意思并不是说在void fcn(const int i)中可以对形参i进行修改,而是说编译后的汇编结果,对于形参int i和const int i的处理的汇编代码是一样的。
如果在void fcn(const int i)中对形参i进行修改,编译仍然会报错,这是编译检查,与真正的编译阶段是不同的。
//a1.c
int foo(const int arg)
{
int val1 = arg << 1;
return val1;
}
//a2.c
int foo(int arg)
{
int val1 = arg << 1;
return val1;
}
gcc -S a1.c 和 gcc -S a2.c 生成汇编文件,对比可以发现a1.s和a2.s的汇编是相同的。同样将a1.c, a2.c分别命令为a1.cpp, a2.cpp 使用g++ -S 编译出的结果也是一样的。
"因为在C语言中,具有const形参或非const形参的函数并无区别"
//a1.s
.file "a1.c"
.text
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $16, %esp
movl 8(%ebp), %eax
addl %eax, %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
//a2.s
.file "a2.c"
.text
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $16, %esp
movl 8(%ebp), %eax
addl %eax, %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits