【问题标题】:array of function pointers with/out typedef and explicit argument declaration带有/输出 typedef 和显式参数声明的函数指针数组
【发布时间】:2017-04-26 14:32:15
【问题描述】:

我在使用函数指针时遇到了一种奇怪的行为。假设我有这段代码(我将简称为 不带 typedef

// without typedef
void* func1(void*);
void* func2(void*);

int main() {
    void* (*func_pntr[])(void*) = {func1, func2};
    return 0;
}

void* func1(void* arg) {
    return (void*)0;
}

void* func2(void* arg) {
    return (void*)0;
}

这段代码的工作方式是常见的,这里并不神秘。我们还可以编写此代码的 typedef 等效项(比如说 typedef1

// typedef1
typedef void*(*func_t)(void*);

func_t func1(void*);
func_t func2(void*);

int main() {
    func_t (*func_pntr[])(void*) = {func1, func2};
    return 0;
}

func_t func1(void* arg) {
    return (void*)0;
}

func_t func2(void* arg) {
    return (void*)0;
}

在这种情况下,我认为理解代码没有问题。我写了第三个版本的代码(叫它typdef2

// typedef2
typedef void*(*func_t)(void*);

void* func1(void*);
void* func2(void*);

int main() {
    func_t func_pntr[] = {func1, func2};
    return 0;
}

void* func1(void* arg) {
    return (void*)0;
}

void* func2(void* arg) {
    return (void*)0;
}

这仍然有效!我想知道可以省略函数指针数组的参数这一事实。同样,如果我用 func_t 代替 void* 编写 typedef2 来进行函数声明/定义,编译器 (GCC) 将无法编译。

有人知道这是怎么发生的吗?更准确地说,我用gcc -std=c89 -Wall test.c 编译,看看这是否与方言问题有关。

【问题讨论】:

  • 你为什么使用空指针?你在这里失去了很多类型安全。当我看到很多强制转换/空指针时,我认为程序员做错了什么
  • 两个顶级案例不等价。第一个func1(例如)是一个返回void *的函数。第二个函数返回一个指向函数的指针。而且你在任何地方都没有“省略论点”。在最后一种情况下,数组函数的参数是 func_t 类型的一部分。
  • @EdHeal:使用 void* 不是我提出的问题的中心问题
  • 我认为您发现这是使用 void 指针的设计中的错误。我将其作为评论留下
  • 这是useful site C gibberish ↔ English 处理此类问题

标签: c gcc function-pointers


【解决方案1】:

如果将三个示例合并到一个程序中,对函数重新编号,那么您可以快速确定问题中第二个代码块中的函数返回的类型(指向函数的指针)与其他两个代码块不同。例如:

// without typedef
void* func1(void*);
void* func2(void*);

extern int arr_func_1(void);
extern int arr_func_2(void);
extern int arr_func_3(void);

int arr_func_1(void) {
    void* (*func_pntr_1[])(void*) = {func1, func2};
    for (int i = 0; i < 2; i++)
        func_pntr_1[i](0);
    return 0;
}

void* func1(void* arg) {
    return (void*)((char *)arg + 16);
}

void* func2(void* arg) {
    return (void*)((char *)arg + 16);
}

// typedef1
typedef void*(*func1_t)(void*);

func1_t func3(void*);
func1_t func4(void*);

int arr_func_2(void) {
    func1_t (*func_pntr_2[])(void*) = {func3, func4};
    for (int i = 0; i < 2; i++)
        func_pntr_2[i](0);
    // func_pntr_2[0] = func1;  // Type mismatch!
    return 0;
}

func1_t func3(void* arg) {
    return (void*)((char *)arg + 16);
}

func1_t func4(void* arg) {
    return (void*)((char *)arg + 16);
}

// typedef2
typedef void*(*func2_t)(void*);

void* func5(void*);
void* func6(void*);

int arr_func_3(void) {
    func2_t func_pntr_3[] = {func5, func6};
    for (int i = 0; i < 2; i++)
        func_pntr_3[i](0);
    // func_pntr_3[0] = func3;  // Type mismatch!
    func_pntr_3[0] = func1;     // OK
    return 0;
}

void* func5(void* arg) {
    return (void*)((char *)arg + 16);
}

void* func6(void* arg) {
    return (void*)((char *)arg + 16);
}

int main(void)
{
    arr_func_1();
    arr_func_2();
    arr_func_3();
    return 0;
}

这在相当严格的编译选项下编译干净:

gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
    -Wold-style-definition so-4018-8770.c -o so-4018-8770 

循环可防止有关未使用参数的警告。

如果取消注释标记为“类型不匹配!”的两行,则会出现错误:

so-4018-8770.c: In function ‘arr_func_2’:
so-4018-8770.c:34:20: error: assignment from incompatible pointer type [-Werror=incompatible-pointer-types]
     func_pntr_2[0] = func2;  // Type mismatch!
                    ^
so-4018-8770.c: In function ‘arr_func_3’:
so-4018-8770.c:56:20: error: assignment from incompatible pointer type [-Werror=incompatible-pointer-types]
     func_pntr_3[0] = func4;  // Type mismatch!
                    ^
cc1: all warnings being treated as errors

在问题的第二段代码中,函数中的(void *) 类型转换是虚假的;返回的类型是void *(*)(void *),尽管显示了演员表——我保留在上面的复合代码中。你也可以像这样修改arr_func_2()

int arr_func_2(void) {
    func1_t (*func_pntr_2[])(void*) = {func3, func4};
    for (int i = 0; i < 2; i++)
    {
        func1_t fp = func_pntr_2[i](0);
        if (fp != 0)
        {
            (*fp)(0);   // Equivalent
            fp(0);      // Equivalent
    }
    // func_pntr_2[0] = func1;  // Type mismatch!
    return 0;
}

标记为“等效”的两行彼此等价——旧式(标准前)但通过函数指针调用函数的显式表示法和新式。我仍然喜欢旧样式,因为它与您声明函数指针的方式相匹配,并且可以省去查找名为 fp 的函数。

您无法在其他两个函数中进行并行更改。

不要尝试运行上面的代码片段;函数返回一个非空指针,因此程序尝试执行地址 0x00000010 处不存在的函数,这不是幸福的秘诀。

【讨论】:

    【解决方案2】:

    一些程序员老兄的解释不那么详细,更简洁:

    两个顶级案例不等价。第一个func1(例如)是一个返回void *的函数。在第二个函数中,函数返回一个指向函数的指针。而且你在任何地方都没有“省略论点”。在最后一种情况下,数组函数的参数是 func_t 类型的一部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-12
      • 2021-06-27
      • 2017-04-18
      相关资源
      最近更新 更多