我有一组函数,全部声明为
static
和 fastcall
。它们中的大多数都使用指向结构体的指针,该结构体或多或少起到了 C++ 中 this
的作用。有些函数不需要结构中的任何内容,但为了统一起见,我还是想向它们传递指针。编译器会注意到该参数未被使用并省略为其分配寄存器吗?
我写了这个废话程序来测试这个。它在函数中包含一些无意义的代码,并多次调用它,否则编译器只会内联整个函数调用,从而使测试变得毫无用处。 (我知道这是 C 和 C++ 的奇怪组合......愚蠢的代码,但仍然可以证明这个问题)
#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;
struct demo
{
int a;
char ch;
};
static void __fastcall func(struct demo* d)
{
for(int i = 0; i < 100; i++) {
std::vector<int> a;
std::sort(begin(a), end(a));
printf("THis is a test");
printf("Hello world\n");
}
}
int main()
{
// void*p = (void*)&func;
struct demo d;
func(&d);
func(&d);
func(&d);
func(&d);
func(&d);
func(&d);
func(&d);
//printf((const char*)p);
}
正如那里所写,函数调用编译为:-
func(&d);
00F61096 call func (0F61000h)
func(&d);
00F6109B call func (0F61000h)
func(&d);
00F610A0 call func (0F61000h)
func(&d);
00F610A5 call func (0F61000h)
func(&d);
00F610AA call func (0F61000h)
func(&d);
00F610AF call func (0F61000h)
func(&d);
00F610B4 call func (0F61000h)
这表明编译器如果不使用参数确实会省略该参数。 但是如果我取消注释这两行以获取函数的地址,那么情况就会发生变化,它会生成以下内容:-
00C71099 lea ecx,[esp]
00C7109C call func (0C71000h)
func(&d);
00C710A1 lea ecx,[esp]
00C710A4 call func (0C71000h)
func(&d);
00C710A9 lea ecx,[esp]
00C710AC call func (0C71000h)
func(&d);
00C710B1 lea ecx,[esp]
00C710B4 call func (0C71000h)
它发送指针的地方。
我的假设是,在第一种情况下,编译器能够证明,如果它生成了函数的自定义调用约定,则不可能有任何用户可见的效果,但在第二种情况下,如果您采用指向函数的指针,则函数可以从另一个单独编译的模块调用,该模块无法知道是否需要该参数。尽管在这种情况下,寄存器是否设置并不重要,但一般来说,编译器需要坚持确切的调用模式,因此我假设它生成了最通用的代码,并且如果可以的话,不会使用自定义调用约定不能证明它可以看到所有可以调用该函数的代码。
无论如何,要回答这个问题,编译器不会总是在寄存器中传递未使用的参数,但我当然不会编写任何依赖于此处任何特定行为的代码,因为在其他地方更改不相关的代码(获取函数的地址),改变了这种行为,我所看到的任何标准都无法保证。
首先,如果您使用正确的编译器标志(例如 -Wall),编译器应该警告您未使用的函数参数。我建议将不使用的参数保留在参数列表之外,尽管我我猜他们会被优化掉。然而,为了确保这一点,您可以做的是编译代码两次,一次带参数,一次不带参数,然后在二进制文件之间执行
diff
操作,看看它们是否匹配。我认为这应该取决于您使用的编译器和优化标志。
干杯,
安迪
您可以在函数定义中省略参数。这将向编译器提示未使用特定参数,它将利用它并优化代码
void func(struct demo *) { ... }