读完f() vs f(void) in C vs C++做的笔记.
C中f()与f(void)区别
f()表示函数形参列表是未知的, 编译器不会帮你做检查.
可以看到gcc编译下面程序, 没有警告.
$ cat test.c
#include <stdio.h>
void foo() {
printf("foo\n");
}
int main(void) {
foo(1, 2, 3);
}
$ gcc ./test.c -std=c11 -O2 -Wall -Wextra -Werror -o test
$ ./test
foo
f(void)才真正表示函数形参列表为空.
$ cat test.c
#include <stdio.h>
void foo(void) {
printf("foo\n");
}
int main(void) {
foo(1, 2, 3);
}
$ gcc ./test.c -std=c11 -O2 -Wall -Wextra -Werror -o test
./test.c: In function ‘main’:
./test.c:8:3: error: too many arguments to function ‘foo’
8 | foo(1, 2, 3);
| ^~~
./test.c:3:6: note: declared here
3 | void foo(void) {
| ^~~
另外X86-64生成的机器指令也会受到影响. 可以看到下面的程序能够正常执行, faz与baz分别调用foo以及bar.
$ cat ./foo.c
#include <stdio.h>
void foo(void) {
printf("foo\n");
}
void bar(void) {
printf("bar\n");
}
$ cat ./test.c
void foo();
void bar(void);
int faz(void) {
foo();
return 0;
}
int baz(void) {
bar();
return 0;
}
int main(void) {
faz();
baz();
return 0;
}
$ gcc ./test.c ./foo.c -std=c11 -O2 -Wall -Wextra -Werror -o test
$ ./test
foo
bar
观察faz与baz的反汇编结果, 发现调用foo之前需要将eax清零, 这是因为foo的形参列表是未知的,
所以可能foo的定义是变参, 而System V AMD64里调用变参函数时, 需要通过al说明使用了几个向量寄存器传参.
For calls that may call functions that use varargs or stdargs (prototype-less calls or calls to functions containing ellipsis (. . . ) in the declaration) %al is used as hidden argument to specify the number of vector registers used.
$ objdump ./test --disassemble=faz -Mintel
0000000000001170 <faz>:
1170: f3 0f 1e fa endbr64
1174: 48 83 ec 08 sub rsp,0x8
1178: 31 c0 xor eax,eax
117a: e8 31 00 00 00 call 11b0 <foo>
117f: 31 c0 xor eax,eax
1181: 48 83 c4 08 add rsp,0x8
1185: c3 ret
1186: 66 2e 0f 1f 84 00 00 cs nop WORD PTR [rax+rax*1+0x0]
118d: 00 00 00
$ objdump ./test --disassemble=baz -Mintel
0000000000001190 <baz>:
1190: f3 0f 1e fa endbr64
1194: 48 83 ec 08 sub rsp,0x8
1198: e8 33 00 00 00 call 11d0 <bar>
119d: 31 c0 xor eax,eax
119f: 48 83 c4 08 add rsp,0x8
11a3: c3 ret
11a4: 66 2e 0f 1f 84 00 00 cs nop WORD PTR [rax+rax*1+0x0]
11ab: 00 00 00
11ae: 66 90 xchg ax,ax
gcc支持-Wstrict-prototypes选项, 遇到f()时发出警告.
$ gcc ./test.c ./foo.c -std=c11 -O2 -Wall -Wextra -Wstrict-prototypes -Werror -o test
./test.c:1:1: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
1 | void foo();
| ^~~~
cc1: all warnings being treated as errors
C++中f()与f(void)
C++中f()与f(void)是等价的, C++中支持函数重载, 需要name mangling, 没办法支持C中f()的语义.