【问题标题】:Casting A Function Pointers Return Type in C在 C 中强制转换函数指针返回类型
【发布时间】:2018-11-23 19:05:59
【问题描述】:

我有一个返回 void 指针 (void*) 的后端函数,并且我有几个指向它的前端函数指针。然而,每个前端函数指针都需要返回不同的类型(尽管总是一个指针)。因为每个前端函数指针总是具有相同的唯一返回类型,所以我认为可以将每个前端指针的返回类型强制转换为各自的返回类型。

希望我下面的代码能比我解释得更好:

//Backend function definition
void* addElement(void *memBlock, size_t origSize, size_t additionalSize){
    return realloc(memBlock, origSize+additionalSize);
}

//Front end function pointers
int*(*addInt)(void*, size_t, size_t)=addElement;
char*(*addChar)(void, size_t, size_t) addChar=addElement;

我的问题有两个:

  1. 这与每次调用函数时只对返回值进行类型转换有什么不同,如下所示?

    int *var=(int *)addElement(...);

  2. 如果不同,它会起作用吗?阅读this discussionthis one 后,似乎归结为兼容性问题。由于原始函数返回 void* ,如果我没记错的话它应该是兼容的。

【问题讨论】:

  • 在实践中这很有可能奏效。但是不保证能正常工作。
  • (int*(*)(void*, size_t, size_t)) addInt=(int*(*)(void*, size_t, size_t))addElement; -- 你在这里尝试什么?强制转换返回 r-value 并且您不能分配给 r 值。这甚至可以编译吗?
  • @AjayBrahmakshatriya:我认为 OP 正试图将 addInt 声明为指向函数的指针,该函数返回指向 int 的指针。如您所知,正确的语法是int *(*addInt)(void *, size_t, size_t) = …。 RHS 看起来在语法上是正确的——我没有通过编译器运行它。
  • @JonathanLeffler RHS 在我看来也不错。 OP 实际上在左侧的类型周围有括号。所以我认为他是在铸造然后分配。但正如你所说,它一定是一个语法错误的声明。
  • @AjayBrahmakshatriya 修复了语法。你假设它是一个语法错误的声明是正确的。感谢您的帮助。

标签: c casting function-pointers


【解决方案1】:

根据标准 C,这都是未定义的行为。被调用的函数指针的类型必须与函数的定义类型匹配(() 可以匹配任何参数列表除外)。

标准中的“兼容”一词有非常严格的含义,基本上是“相同”。尽管void * 可以隐式转换为int *,但它们不是兼容的类型

对于要定义行为的代码,您可以按照第 1 点中的建议进行操作。您可以将其包装在宏或包装函数中。

【讨论】:

    【解决方案2】:

    C11 标准 §6.2.5 Types ¶28 说:

    指向void 的指针应具有与 指向字符类型的指针。48) 同样,指向合格或不合格版本的指针 兼容类型应具有相同的表示和对齐要求。全部 指向结构类型的指针应具有相同的表示和对齐要求 作为彼此。所有指向联合类型的指针都应具有相同的表示形式和 对齐要求彼此。指向其他类型的指针不必相同 表示或对齐要求。

    这意味着,特别是,指向函数的指针不必具有与任何指向对象类型的指针相同的表示。有,或者至少是,指向函数的指针大于指向对象的指针的机器(我指的不是小型、中型、紧凑型、大型、巨大的模型和 80x86 芯片——我相信 IBM AS/400,后来的 iSeries ,具有比任何指向对象的指针更大的函数指针)。此外,正如Ajay Brahmakshatriyacomment 中指出的那样,一些 ABI(应用程序二进制接口)可能对不同的函数类型有不同的调用约定,因此将函数指针转换为“错误”类型,然后通过转换后的指针调用它也可能最终造成严重破坏。这尤其适用于指针、浮点和其他类型。通常(但绝不保证),返回指针的函数会以相同的方式返回指针,而不管返回的指针类型如何。

    §6.3.2.3 Pointers ¶7-8 说:

    指向对象类型的指针可以转换为指向不同对象类型的指针。如果 结果指针未正确对齐68) 对于引用的类型,行为是 不明确的。否则,当再次转换回来时,结果将等于 原始指针。当指向对象的指针转换为指向字符类型的指针时, 结果指向对象的最低寻址字节。的连续递增 结果,直到对象的大小,产生指向对象剩余字节的指针。

    指向一种函数的指针可以转换为指向另一种函数的指针 键入并再次返回;结果应与原始指针比较。如果一个转换 指针用于调用类型与引用类型不兼容的函数, 行为未定义。

    在编写代码时,您将通过强制转换类型调用函数,这会导致未定义的行为。它可能会起作用,但它可能会停止工作,并且除了重写代码以避免未定义的行为之外,您别无选择。

    这意味着您不应该编写问题中所示的前端函数指针。不过,您可以使用简单的封面函数来完成您想要的工作:

    static int *addIntFunc(void *mem, size_t old_size, size_t extra_size)
    {
        return addElement(mem, old_size, extra_size);
    }
    
    int *(*addInt)(void *, size_t, size_t) = addIntFunc;
    

    您可能无法有效地内联该函数;函数指针必须指向函数。

    或者,实际上,您可以简单地使用addIntFunc(可能重命名为addInt)来获得所需的效果,然后内联函数可能会很有用。

    【讨论】:

    • 除了表示之外,还有一个问题是 ABI 可能对不同类型使用不同的传递(和返回参数)方式。
    • 是的,不同类型的参数或不同的返回类型在 ABI 中可能存在差异。我不知道 ABI 中不同的指针类型作为返回值的处理方式不同,在这种情况下,参数列表在所有情况下似乎都是相同的(鉴于问题中的数据有限)。但是,总的来说,这是一个问题,我在答案中添加了注释。谢谢!
    猜你喜欢
    • 2012-06-07
    • 1970-01-01
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    • 2021-01-14
    • 1970-01-01
    • 2020-06-01
    • 1970-01-01
    相关资源
    最近更新 更多