将函数指针转换为具有继承C结构体的函数。

5

我正在寻找答案,是否在C语言中,以下示例函数的强制类型转换和调用是有效的(如果无效,请提供类似行为的示例)。

示例:

#include <stdio.h>
#include <stdint.h>

typedef struct {
    uint32_t a;
} parent_t;

typedef struct {
    parent_t parent;
    uint32_t b;
} child_t;


typedef void (*funct_ptr_t)(parent_t* pParent, uint32_t x);

void test_function(child_t* pChild, uint32_t x);

int main()
{ 
    funct_ptr_t func = (funct_ptr_t)test_function;
    funct(NULL, 0U);

    return 0;
}

void test_function(child_t* pChild, uint32_t x) {
    // do something
}

@EDIT 由于上述方法无效,我想询问第二种方法。
typedef void (*funct_ptr_t)(void* pChild, uint32_t x);

void test_function(child_t* pChild, uint32_t x);

int main()
{ 
    funct_ptr_t func = (funct_ptr_t)test_function;
    child_t child;
    func(&child, 0U);

    return 0;
}

void test_function(child_t* pChild, uint32_t x) {
    // do something
}

这至少看起来很危险 - 函数需要一个指向 child_t 对象的指针,但可能会收到一个指向 "only" parent_t 的指针。如果函数尝试访问 b 成员,则肯定会引发未定义行为 - UnholySheep
相反的做法是安全的,因为该函数不会尝试访问parent前缀之外的内容。 - Barmar
1个回答

2

这是无效的。

一个函数只能通过兼容的函数指针类型进行调用。要使两个函数指针类型兼容,相应的参数必须具有兼容的类型,并且返回类型必须是兼容的。

test_function 的类型为void (*)(child_t*, uint32_t ),但正在通过类型为void (*)(parent_t*, uint32_t )的函数指针进行调用。这意味着该函数正在通过不兼容的类型进行调用,这将触发未定义行为

如果您尝试接受第一个参数的不同类型的指针,则处理此问题的最简单方法是更改函数和函数指针的第一个参数的类型为void *,然后在函数内部执行必要的转换。

另一种选择是将第一个参数的类型更改为parent_t *并在函数内部执行转换。 这将起作用,因为指向结构体的指针可以转换为指向其第一个成员的指针,因此您可以这样做:

void test_function(parent_t* pChild, uint32_t x);

int main()
{ 
    funct_ptr_t func = (funct_ptr_t)test_function;
    child_t c;
    funct((parent_t *)&c, 0U);

    return 0;
}

void test_function(parent_t* p, uint32_t x) {
    child_t *p_Child = (p_Child *)p;
    // do something
}

C 2018 6.2.5 28 规定所有指向结构类型的指针应具有相同的表示和对齐要求,注释49指出这意味着可以将它们作为函数参数进行交换。在C标准中没有正式规定“可互换性”,但似乎旨在覆盖兼容性问题,并且从未在标准中阐明。 - Eric Postpischil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接