【问题标题】:C99: cast callbacks with different number of argumentsC99:使用不同数量的参数强制转换回调
【发布时间】:2020-04-30 08:55:49
【问题描述】:

在下面的示例中,我在指向应该接收参数的函数的指针中对不带参数的函数进行了 CAST。假设它给出了预期的结果,这个过程是否可能导致一些故障? 在线测试:https://onlinegdb.com/SJ6QzzOKI

typedef void (*Callback)(const char*);
Callback cb;

void inserisce_cb(void* c) {
    cb=c;
}

void esegue_cb(){
    cb("pippo");
}

void scriveTitolo(const char* titolo) {
    Uart_Println(titolo);
}

void scriveTitolo2() {
    Uart_Println("pluto");
}

void main(){
    inserisce_cb(scriveTitolo);
    esegue_cb();
    inserisce_cb(scriveTitolo2);
    esegue_cb();
}

【问题讨论】:

  • 一般来说,对于 c++ 中的函数指针,你应该使用 std::function,它基本上是相同的,但是有类型检查等等。
  • 您的示例无法编译,因为不允许从 void 隐式转换为函数指针。
  • 在 C 中,void*pointer 与任何指向数据对象的指针兼容。这并不一定意味着它可以用来存储函数指针。
  • Oleg Andriyanov:为 c++ 兼容性添加了演员表
  • Oleg Andriyanov 和 camp0 :编辑问题:限制为 C 语言

标签: c function pointers casting c99


【解决方案1】:

将指向函数的指针转换为指向函数的另一个指针是由 c 标准定义的,但根据 C 6.3.2.3 8,使用生成的指针调用具有不兼容类型的函数不是:

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

声明void scriveTitolo2() { … } 定义了一个没有参数类型列表的函数(它使用标识符列表的旧C 样式,该列表为空)并且不接受任何参数。 Callback 指针指向具有参数类型列表并接受 const char * 参数的函数。这些与 C 2018 6.7.6.3 15 不兼容:

为了使两种函数类型兼容,... 如果一种类型具有参数类型列表,而另一种类型由包含(可能为空)标识符列表的函数定义指定,则两者的参数数量应一致,...

由于参数个数不一致,所以不兼容。

上面只讲了从void (*)() 转换为void (*){const char *) 并使用结果调用函数的问题。还有一个单独的问题是函数指针被传递给inserisce_cb,它接受void * 类型的参数,它是一个指向对象类型的指针。 C 标准没有定义将指向函数类型的指针转​​换为指向对象类型的指针的行为。为了解决这个问题,应该将inserisce_cb 声明为采用指向函数类型的指针,例如void inserisce_cb(Callback c)

如果可以更改scriveTitolo2,则可以通过将其更改为采用未使用的const char * 参数并将其定义更改为void scriveTitolo2(const char *) 来解决兼容性问题。

(请注意,最好使用现代 C 风格声明 scriveTitolo2,如 void scriveTitolo2(void) { … },而不是不使用 void。这与问题无关,因为它不会使函数类型兼容,但这种声明格式在许多情况下为编译器提供了更多信息。)

【讨论】:

    【解决方案2】:

    对 Eric 回答的其他想法,这也适用于 C99:

    如果您使用与函数的参数列表不兼容的参数列表调用函数,则这是根据 C99 §6.5.2.2 (6) 未定义行为。

    它可能会起作用,具体取决于您的编译器的 ABI。有些编译器让被调用函数清理堆栈,其他编译器让调用者清理堆栈。前一种情况很可能会崩溃,后者……谁知道呢。

    您可以使用忽略的参数声明您的scriveTitolo2

    void scriveTitolo2(const char*) {
        /* ... */
    }
    

    每个人都很高兴:你和编译器。

    【讨论】:

    • 如果第二个函数具有相同数量的参数但类型(和大小)不同,例如:void scriveTitolo2(unsigned char t) { if (t==1) printf("paperino");否则 printf("冥王星"); } //可以吗?
    • 我不这么认为,类型必须兼容。请阅读标准的章节。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-23
    • 1970-01-01
    • 2010-10-07
    • 1970-01-01
    • 1970-01-01
    • 2019-08-26
    • 1970-01-01
    相关资源
    最近更新 更多