首页 > 解决方案 > 如何按照以下代码示例将指针函数 abc(x, y) 存储到数组中?

问题描述

char *abc(unsigned int a, unsigned int b)
{
  //do something here ...
}
fun2()
{
 unsigned int x, y;
  x= 5, y=6;
 char *array1;
 char array2;
 for(i=0; i<3; i++)
{
  array2[i] = abc(x, y);
}
}

标签: arrayscfunctionpointers

解决方案


您不能将函数的调用存储在 C 中,因为它会破坏许多现有的涉及寄存器参数传递的流行优化 - 请参阅,因为通常参数在执行流程转移到调用站点之前立即分配其参数值 - 编译器可能会选择使用寄存器来存储这些值,但就目前而言,这些寄存器是易失性的,因此如果我们要延迟实际调用,它们将在稍后的时间被覆盖 - 甚至可能通过对某个函数的另一个调用,该函数也将其参数作为寄存器传递. 一个解决方案——我个人已经实现了——是让一个函数通过重新分配给适当的寄存器和任何进一步的参数来模拟你的调用——给堆栈。在这种情况下,您将参数值存储在平面内存中。但这必须专门为此目的在汇编中完成,并且特定于您的目标体系结构。另一方面,如果您的架构没有使用任何此类优化 - 它可能会更容易,但仍然需要手写汇编。

无论如何,这不是标准(甚至据我所知甚至是预标准)C 随时实现的功能。

例如,这是我前段时间写的 x86-64 的实现(用于 MSVC masm 汇编器):

PUBLIC makeuniquecall

.data

makeuniquecall_jmp_table    dq  zero_zero, one_zero, two_zero, three_zero ; ordinary

makeuniquecall_jmp_table_one    dq  zero_one, one_one, two_one, three_one ; single precision

makeuniquecall_jmp_table_two    dq  zero_two, one_two, two_two, three_two ; double precision

.code

makeuniquecall PROC
;rcx - function pointer
;rdx - raw argument data
;r8 - a byte array specifying each register parameter if it's float and the last qword is the size of the rest
push    r12
push    r13
push    r14

mov r12, rcx
mov r13, rdx
mov r14, r8

; first store the stack vars

mov rax, [r14 + 4] ; retrieve size of stack

sub rsp, rax

mov rdi, rsp
xor rdx, rdx
mov r8, 8
div r8
mov rcx, rax
mov rsi, r13
;add    rsi, 32

rep movs qword ptr [rdi], qword ptr [rsi]

xor r10,r10
cycle:
mov rax, r14
add rax, r10
movzx rax, byte ptr [rax]
test rax, rax
jnz jmp_one
lea rax, makeuniquecall_jmp_table
jmp qword ptr[rax + r10 * 8]
jmp_one:
cmp rax, 1
jnz jmp_two
lea rax, makeuniquecall_jmp_table_one
jmp qword ptr[rax + r10 * 8]
jmp_two:
lea rax, makeuniquecall_jmp_table_two
jmp qword ptr[rax + r10 * 8]

zero_zero::
mov rcx, qword ptr[r13+r10*8]
jmp continue
one_zero::
mov rdx, qword ptr[r13+r10*8]
jmp continue
two_zero::
mov r8, qword ptr[r13+r10*8]
jmp continue
three_zero::
mov r9, qword ptr[r13+r10*8]
jmp continue
zero_one::
movss xmm0, dword ptr[r13+r10*8]
jmp continue
one_one::
movss xmm1, dword ptr[r13+r10*8]
jmp continue
two_one::
movss xmm2, dword ptr[r13+r10*8]
jmp continue
three_one::
movss xmm3, dword ptr[r13+r10*8]
jmp continue
zero_two::
movsd xmm0, qword ptr[r13+r10*8]
jmp continue
one_two::
movsd xmm1, qword ptr[r13+r10*8]
jmp continue
two_two::
movsd xmm2, qword ptr[r13+r10*8]
jmp continue
three_two::
movsd xmm3, qword ptr[r13+r10*8]
continue:
inc r10
cmp r10, 4
jb  cycle

mov r14, [r14 + 4] ; retrieve size of stack

call    r12

add rsp, r14

pop r14
pop r13
pop r12

ret

makeuniquecall ENDP

END

您的代码将如下所示:

#include <stdio.h>

char* abc(unsigned int a, unsigned int b)
{
    printf("a - %d, b - %d\n", a, b);
    return "return abc str\n";
}

extern makeuniquecall();

main()
{
    unsigned int x, y;
    x = 5, y = 6;

#pragma pack(4)

    struct {
        struct { char maskargs[4]; unsigned long long szargs; } invok;
        char *(*pfunc)();
        unsigned long long args[2], shadow[2];
    } array2[3];

#pragma pack(pop)

    for (int i = 0; i < 3; i++)
    {
        memset(array2[i].invok.maskargs, 0, sizeof array2[i].invok.maskargs); // standard - no floats passed

        array2[i].invok.szargs = 8 * 4; //consider shadow space

        array2[i].pfunc = abc;

        array2[i].args[0] = x;
        array2[i].args[1] = y;
    }

    //now do the calls

    for (int i = 0; i < 3; i++)
        printf("%s\n", ((char *(*)())makeuniquecall)(array2[i].pfunc, array2[i].args, &array2[i].invok));
}

对于您的特定情况,您可能不需要它,您只需存储每个参数并直接调用函数即可逃脱 - 即(加上此方法不会特定于 x86-64):

//now do the calls

for (int i = 0; i < 3; i++)
    printf("%s\n", array2[i].pfunc(array2[i].args[0], array2[i].args[1]));

但是我的实现使您可以灵活地为每个调用存储不同数量的参数。

请注意,请考虑本指南以在 msvc 上运行上述示例(因为它需要为汇编代码添加 asm 文件)。

我喜欢这样的菜鸟问题,因为它们让您思考为什么 xy 功能实际上并不存在于该语言中。


推荐阅读