繁体   English   中英

C:将数组传递给指针函数

[英]C: Passing array to pointer function

我不确定这个问题是否曾经问过,但是我找不到任何类似的主题。

我正在努力处理以下代码。 想法是在以后的任何时间扩展r而不编写大量的if-else语句。 函数( func1func2 ...)采用零或一参数。

void func1() {
    puts("func1");
}

void func2(char *arg){
    puts("func2");
    printf("with arg %s\n", arg);
}

struct fcall {
    char name[16];
    void (*pfunc)();
};

int main() {
    const struct fcall r[] = {
        {"F1", func1},
        {"F2", func2}
    };
    char param[] = "someval";

    size_t nfunc = RSIZE(r); /* array size */
    for(;nfunc-->0;) {
        r[nfunc].pfunc(param);
    }
    return 0;
}

上面的代码假定所有函数都采用字符串参数,而事实并非如此。 声明的指针函数原型没有任何数据类型,以防止出现不兼容的指针类型警告。

将参数传递给不带任何参数的函数通常会导致参数太少 但是在这种情况下,编译器不会提前“看到”这一点,这也让我相信没有做任何优化来排除这些未使用的地址不会被压入堆栈。 (我没有看过实际的汇编代码)。

某种程度上感觉不对,这通常是缓冲区溢出或未定义行为的秘诀。 分别调用不带参数的函数会更好吗? 如果是这样,这会造成多大的损失?

这样做的方法是typedef一个带有1个参数的函数,因此编译器可以验证是否传递了正确数量的参数,并且没有传递绝对不兼容的内容(例如,按值构造)。 并且在初始化数组时,请使用此typedef强制转换函数类型。

void func1(void) { ... }

void func2(char *arg) { ... }

void func3(int arg) { ... }

typedef uintptr_t param_t;    
typedef void (*func_t)(param_t);

struct fcall {
    char name[16];
    func_t pfunc;
};

const struct fcall r[] = {
    {"F1", (func_t) func1},
    {"F2", (func_t) func2}
    {"F3", (func_t) func3}
};

...

r[0].pfunc((param_t) "foo");
r[1].pfunc((param_t) "bar");
r[2].pfunc((param_t) 1000);

在这里, param_t被定义为uintpr_t 这是一个足够存储指针值的整数类型。 有关详细信息,请参见此处: 什么是uintptr_t数据类型 需要注意的是, param_t的调用约定应与您使用的函数参数兼容。 通常对于所有整数和指针类型都是如此。 下面的示例将起作用,所有类型转换在调用约定方面都相互兼容:

// No problem here.
void ptr_func(struct my_struct *ptr) {
    ...
}
...
struct my_struct struct_x;
((func_t) &ptr_func)((param_t) &struct_x);

但是,如果您要传递一个float或double参数,则它可能无法按预期工作。

// There might be a problem here. Depending on the calling
// conventions the value might contain a complete garbage,
// as it might be taken from a floating point register that
// was not set on the call site.
void float_func(float value) {
    ...
}
...
float x = 1.0;
((func_t) &float_func)((param_t) x);

在这种情况下,您可能需要定义如下函数:

// Problem fixed, but only partially. Instead of garbage
// there might be rounding error after the conversions.
void float_func(param_t param) {
    float value = (float) param;
    ...
}
...
float x = 1.234;
((func_t) &float_func)((param_t) x);

浮点数首先被转换为整数类型,然后返回。 结果,该值可能会四舍五入。 一个明显的解决方案是获取x的地址并将其传递给函数float_func2(float *value_ptr)进行修改。 该函数将取消引用其指针参数并获取实际的float值。

但是,当然,作为硬核C程序员,我们不想变得显而易见,因此我们将采取一些丑陋的诡计。

// Problem fixed the true C-programmer way.
void float_func(param_t param) {
    float value = *((float *) &param);
    ...
}
...
float x = 1.234;
((func_t) &float_func)(*((param_t *) &x));

与将指针传递给float相比,此示例的不同之处在于,在将参数传递到寄存器而非堆栈上的体系结构(如x86-64)上,足够聪明的编译器可以使float_func仅使用寄存器来完成其工作,无需从内存中加载参数。

一种选择是,所有函数都接受char *参数,并且您的调用代码始终传递一个。 不需要参数的函数不必使用它们收到的参数。

为了保持整洁(并避免未定义的行为),如果必须具有不接受参数的某些函数和必须接受参数的某些函数,请使用两个列表并分别注册/调用每种类型的函数。

如果行为未定义,则无法确定会造成多大的损害。

可能炸毁地球。 否则可能不会。

所以只是不要这样做,好吗?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM