【问题标题】:Why function call only pass the first argument为什么函数调用只传递第一个参数
【发布时间】:2019-10-19 09:27:32
【问题描述】:

我试图通过强制转换调用一个函数(第 15 行),但只有第一个参数被传递,我该如何解决它?

我尝试将浮点值“2”更改为 2.0f 以声明它是浮点数而不是整数,但它仍然无法正常工作。

!请注意,代码很糟糕,因为它是code golf,第15行以后必须是dll形式,这里的代码只是一个测试程序,以避免多次启动目标进程。这是我的实际代码,得分为 58 个字符

DllMain(a,b,c){((int(*)(int,float))927309216)(‭7903472‬,2);}
#include <Windows.h>
#include <stdio.h>
char * sSkelModelStencil = "SkelModelStencil"; //here I reproduce the char like it is in the memory

void SetConsoleFloat(const char *sKey, float fVal) //this is the reproduction of SetConsoleFloat in CShell.dll
{
    printf("arg1:    %s    arg2: %f\n", sKey, fVal); //printing the arguments getting passed into the function
    return;
}

int main()
{
    while (1) {
        SetConsoleFloat("SkelModelStencil", 2); //calling the reproduction function
        ((int(*)())SetConsoleFloat)(sSkelModelStencil,2); //calling the reproduction function via a cast & using SetConsoleFloat addr
        system("PAUSE");
    }
}

【问题讨论】:

  • 请注意,第 15 行包含奇怪的演员表,因为它是代码高尔夫,我不能放太多字符。
  • 这尖叫着未定义的行为,所以很难确定,但我会把钱放在被调用的函数上,期望 float 在 XMM 寄存器中(假设 x86-64)和你的演员表不包括参数列表,因此调用者没有把它放在那里。
  • 如果你必须保持简短,你为什么需要演员?
  • 它必须是dll形式,这是我的实际代码,得分为58 DllMain(a,b,c){((int(*)(int,float))927309216)(‭7903472‬,2);}
  • @ChrisHoffmann 您应该在问题中发表评论。

标签: c call


【解决方案1】:

在某些架构中,参数的传递方式取决于它们的声明方式。例如,特殊寄存器可用于float 参数。重要的是函数类型的声明,而不是参数表达式的声明。

参数签名()(const char *sKey, float fVal) 不同,因此fVal 参数的传递方式与函数期望接收它的方式不同。

【讨论】:

  • tl;dr 将 ((int(*)())SetConsoleFloat) 更改为 ((void(*)(const char*, float))SetConsoleFloat) 并观看它神奇地工作。 ;-)
  • @DanielKamilKozar 他说,出于代码高尔夫的原因,他必须使用短投。不过,我不确定他为什么需要演员阵容。
  • 我可以做到这一点,但遗憾的是,这是一个代码高尔夫挑战,我必须保存角色,但谢谢!我的代码必须是 dll 形式并注入到一个进程中,然后它必须调用一个函数。
  • @ChrisHoffmann :如果不使用正确的参数列表,您将无法正确调用,除非您探索编译器并找出它前面的调用序列(或其他任何东西,真的)导致编译器将所需的值留在被调用函数所需的寄存器中。也许这才是真正的挑战。
【解决方案2】:

首先 - 这是糟糕的代码,不要那样做。

其次 - 在编译器警告打开的情况下编译您的代码,以便编译器可以告诉您哪里可能出错了。当然,您需要一个合适的 C 编译器(如果您使用的是 MSVC,则不需要)。 gcc 会告诉你:

a.c:15:10: warning: function called through a non-compatible type

但是,要回答您的问题:您正在转换为错误的函数类型:您正在使用函数类型 void ();但你需要void (const char*, float)。所以,试试吧:

((void(*)(const char*, float))SetConsoleFloat)(sSkelModelStencil,2);

而不是您现有的第 15 行。将强制转换与函数的类型定义分开也是一个好主意 - 为了清楚起见 - 这样您就可以:

typedef  void (*scf_function_t)(const char*, float);

之前,然后:

((scf_function_t) SetConsoleFloat)(sSkelModelStencil,2);

但同样 - 首先确实没有充分的理由这样做。

【讨论】:

  • 这是一个代码高尔夫挑战,当然它是糟糕的代码。
  • 以简洁性而非清晰性来判断。
  • @Barmar:啊,对不起,我把你误认为是 OP。改写:大多数会在网站上找到这个问题的人不会知道 OP 作为作者的意图是什么,也不会熟悉“代码高尔夫”这个词。在回答问题时,我们需要考虑更广泛的受众。告诉人们不要编写这种代码很重要。
  • 我已经为他的问题添加了一个链接,指向什么是代码高尔夫。
猜你喜欢
  • 2017-01-25
  • 2021-06-29
  • 1970-01-01
  • 2013-12-01
  • 1970-01-01
  • 2020-03-16
  • 1970-01-01
  • 2014-12-30
  • 2014-04-17
相关资源
最近更新 更多