c 中的 'void *ptr[N](int)' 和 'void (*ptr)[N](int)' 有什么区别?[复制]

What is the differnce between `void *ptr[N](int)` and `void (*ptr)[N](int)` in c? [duplicate]

提问人:pushpa 提问时间:9/5/2023 最后编辑:wohlstadpushpa 更新时间:9/5/2023 访问量:83

问:

假设我有 和void (*ptr[3])(int)void (*ptr)[3](int)

第一个按预期工作。 但是第二个会抛出一个错误。

我都尝试了,但无法找出问题所在。 错误内容如下:

"..错误:将“PTR”声明为函数数组无效 (*ptr)3;”

C 函数 指针运 算符优先级

评论

2赞 John Bollinger 9/5/2023
请注意,问题标题与问题正文开头的声明不同。它是一个函数 (int) 数组,返回指向 void 的指针。(这仍然是不允许的。你在这里到底想达到什么目的?void *ptr[N](int)
0赞 Ted Lyngmo 9/5/2023
@JohnBollinger 好渔获。我什至没有注意到标题中有不同的声明。

答:

5赞 Ted Lyngmo 9/5/2023 #1

void (*ptr)[3](int)与声明为指向函数数组的指针相同,这是不允许的。不能有函数数组。ptr

typedef void(functype)(int);
functype ptr[3];              // error

您最需要的是一个函数指针数组,这是您的工作声明 创建的。void (*ptr[3])(int)

typedef void(functype)(int);
functype* ptr[3];
//      ^

使用示例:

void a(int x) {}
void b(int x) {}
void c(int x) {}

int main(void) {
    typedef void(functype)(int);

    functype* ptr[3] = {a,b,c};
}

评论

0赞 John Bollinger 9/5/2023
你可能是对的,他们想要一个函数指针数组,但我想他们可能想要一个返回指向数组的指针的函数。
0赞 Ted Lyngmo 9/5/2023
@JohnBollinger 真的。我已经澄清了我实际回答的内容,以免在我猜错的情况下造成太多混乱。
0赞 tstanisl 9/5/2023
void (*ptr)[3](int)是“指向函数数组的指针”,而不是“函数数组”
0赞 tstanisl 9/5/2023
考虑typeof(void(int))* ptr[3];
0赞 Ted Lyngmo 9/5/2023
@tstanisl 谢谢,更新。 也是一个不错的 C23 补充,但我现在会坚持使用老派。typeoftypedef
2赞 Lundin 9/5/2023 #2

无论谁想出了指向函数或数组的指针的语法,背后都没有太多的理由——很可能它只是偶然发生的。这里有一些规则:

  • 函数实际上并不存在于表达式中,只有函数指针才存在。 C17 6.3.2.1 第 4 节:

    函数指示符是具有函数类型的表达式。除非它是运算符的操作数或一元运算符,否则类型为“函数返回类型”的函数指示符将转换为类型为“指向函数返回类型”的表达式。sizeof&

  • 声明/初始化与表达式中的运算符优先级“协调”。这意味着它始终意味着“3 个 int 指针的数组”,而不是“指向 3 个 int 数组的指针”,就像表达式总是表示“访问第 4 个元素然后取消引用”一样。int*p[3]*p[3]

由于上述原因,编译器在面对非法语法时可能很难产生合理的诊断。在这种情况下,错误和错误在 gcc 和 clang 中给出了相同的诊断消息,有点像“将'p'声明为函数数组”。尽管目前尚不清楚后者如何被视为“函数数组”。事实上,前者是函数数组,后者是指向函数数组的指针 - 两者都是不允许的。int p [3] (void);int (*p) [3] (void);

正如我们所知道的,几乎没有理解这种混乱,包括那些为 gcc 和 clang 编写编译器诊断的人。少数假装这样做的人是那些愿意花费大量时间按照“顺时针螺旋”解析不必要的晦涩表情的人,直到他们的眼睛看起来像顺时针螺旋......别这样。

这里的 #1 规则是在使用函数指针时始终使用 typedef,没有例外。

这里有一个小小的常见问题解答:

int ptr1 (void);           // OK. Function
int (ptr2) (void);         // Weird but OK. Still a function
int ptr3 [3] (void);       // Invalid declaration - array of functions
int (*ptr4) (void);        // OK. Pointer to function
int (*ptr5 [3]) (void);    // OK. Array of pointers to functions
int (*ptr6) [3] (void);    // Invalid declaration - pointer to array of functions
int ((*(*ptr4)(void))[3]); // Madness, pointer to a function returning a pointer to array of 3 int

强烈推荐的做法:

typedef int func_t (void);   // function type
func_t* func;                // function pointer
func_t* func[3];             // array of function pointers
func_t* const func = f;      // read-only function pointer, can't be reassigned
func_t* const func[3] =      // read-only array of function pointers, can't be reassigned
{ ... };

评论

0赞 Ted Lyngmo 9/5/2023
“使用函数指针时始终使用 typedef” - 这让我感觉不那么笨拙,因为我这样做是因为我认为它们通常很难破译:-)
1赞 Lundin 9/5/2023
@TedLyngmo 你可以像一群非常有经验的程序员一样抛出一个表达式,如果他们真的能做到的话,他们会花相当长的时间来解析它——我真的不希望他们这样做,因为阅读 C 代码不应该是一个 f-in' Mensa 测试。在我自己在这里发布之前,我必须用编译器仔细检查它。然后很明显,如果即使是资深程序员也为此苦苦挣扎,那么从初级到中级的程序员都没有机会。而这个答案末尾的风格,即使是初学者也应该很容易理解。int ((*(*ptr4)(void))[3]);