1
  • Function 1.
  • It is a pointer function.
char *abc(unsigned int a, unsigned int b)
{
  //do something here ...
}
  • Function 2
  • Leveraged the function 1 into function 2.
  • I am trying to store the abc function into an array, however I am getting the error as : error: assignment to expression with array type.
fun2()
{
 unsigned int x, y;
  x= 5, y=6;
 char *array1;
 char array2;
 for(i=0; i<3; i++)
{
  array2[i] = abc(x, y);
}
}
typo_code
  • 13
  • 4
  • 3
    `array2` is a char, but you're writing `array2[i]` as if it's an array of 3 things. `x` and `y` are uninitialized, and `array2` is not used. It's very unclear to me what you're trying to do. – Paul Hankin Jul 10 '21 at 13:29
  • declaring array2 as `char *array2[3];` will allow the part of your code that you're talking about to compile. – Paul Hankin Jul 10 '21 at 13:33
  • @PaulHankin , I am not sure as to how I should define the array2 such that it is capable of storing abc(x, y). Like the array2 should be able to take 3 values actually. x and y have been initialized, i.e., it can take any value of x and y. But mainly I am confused on how I how i can initialize array2 so that `array2[i]= abc(x, y);` – typo_code Jul 10 '21 at 13:34
  • `abc(x, y`) is a `char *`, so you need an array of `char*`. Hence `char *array2[3]` which is an array of 3 `char*` – Paul Hankin Jul 10 '21 at 13:37

1 Answers1

0

You can't store the invocation of a function in C since it would defeat many existing popular optimizations involving register parameters passing - see because normally parameters are assigned their argument values immediately before the execution flow is transferred to the calling site - compilers may choose to use the registers to store those values but as it stands those registers are volatile and so if we were to delay the actual call they would be overwritten at said later time - possibly even by another call to some function which also have its arguments passed as registers. A solution - which I've personally implemented - is to have a function simulate the call for you by re-assigning to the proper registers and any further arguments - to the stack. In this case you store the argument values in a flat memory. But this must be done in assembly exclusively for this purpose and specific to your target architecture. On the other hand if your architecture is not using any such optimizations - it could be quite easier but still hand written assembly would be required.

In any case this is not a feature the standard (or even pre standard as far as I know) C has implemented anytime.

For example this is an implementation for x86-64 I've wrote some time ago (for MSVC masm assembler):

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

And your code will look something like this:

#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));
}

You'll probably not need that for your specific case you will get away with simply storing each argument and calling the function directly - i.e. (plus this method won't be x86-64 specific):

//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]));

But mine implementation gives you the flexibility to store different amount of arguments for each call.

Note consider this guide for running above examples on msvc (since it requires to add asm file for the assembly code).

I love such noob questions since they make you think about why x-y feature doesn't actually exist in the language.

AnArrayOfFunctions
  • 3,452
  • 2
  • 29
  • 66