这个函数指针的前向声明在 C 中有效吗?
Is this forward declaration of a function pointer valid in C?
我正在尝试确定以下前向声明在 ANSI-C 中是否有效:
第一个文件:
extern void * fptr; // opaque forward declaration.
int main (void) {
fptr = NULL; // set the function pointer to NULL
}
第二个文件:
typedef int (*fptr_t)(int);
fptr_t fptr; // real declaration of the function pointer
对我来说,这应该是无效的,因为 fptr
如果用两种不同的类型声明,但 gcc
和 clang
都不会给出任何警告。
我会对 C11 标准的精确点更感兴趣,这些点可以得出它为什么有效(或无效)的结论。
编辑:在 C11 标准中,6.2.7:2 说:
All declarations that refer to the same object or function shall have
compatible type; otherwise, the behavior is undefined.
但我找不到如何确定 void*
是否与 fptr_t
兼容。
C99:
6.2.7 Compatible type and composite type
clause 2:
All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.
6.7.5.1 Pointer declarators
clause 2:
For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.
如果不深入研究标准,很容易看出 void
和一个函数是不兼容的类型。
我敢打赌这在 C11 中不会改变。长期以来,C 隐含地支持不同的代码和数据空间以及不同大小和表示的代码和数据指针,删除此功能并将语言限制在更小的机器子集上是很奇怪的。因此,请谨慎投票。有证据更好。
不,它是无效的,因为本质上您是将常规指针(NULL
、void*
)存储到一个实际上是函数指针的内存位置。你只是把它从编译器中隐藏起来,链接器并不关心,但最终你会有未定义的行为,因为这两种指针类型不一定兼容。当然它可能适用于许多系统,但可能不是全部。
有关函数指针与 void 指针的更多信息,请参见此处:can void* be used to store function pointers? - 虽然这与您介绍的情况略有不同,但答案仍然相关。
应该可以,但无效。在第一个文件中,您声明标识符 fptr
将在另一个编译单元中定义,并且它将是 void *
。在第二个文件中,您定义了标识符,但它现在是指向函数的指针。编译后的文件一般不会保留对象的类型(只保留地址)所以:
- 编译器不知道其他源声明了不同的类型,因此无法发出任何警告
- 链接器不需要控制,所以标准不需要警告,一个常见的实现不控制类型,所以也没有警告
在常见的实现中,所有指针都具有相同的表示,并且将指向函数的指针转换为指向 void 的指针不会改变表示,因此 别名 将给出预期的结果.
但它仍然是未定义的行为(*),因为 6.2.5 Types § 27 声明(强调我的):
A pointer to void shall have the same representation and alignment requirements as a
pointer to a character type.39) Similarly, pointers to qualified or unqualified versions of
compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements
as each other. All pointers to union types shall have the same representation and
alignment requirements as each other. Pointers to other types need not have the same
representation or alignment requirements.
(*) 由于未通过通用实现进行测试,因此当前版本的编译器检测并优化 UB 的风险很小。但我永远不会在生产代码中这样做...
void *
是对象类型指针,不同于函数类型指针。它们不是兼容的类型。
但是:
Portabilitiy issues J.5.7. Function pointer casts
A pointer to an object or to void may
be cast to a pointer to a function, allowing data to be invoked as a
function (6.5.4).
A pointer to a function may be cast to a pointer
to an object or to void, allowing a function to be inspected or
modified (for example, by a debugger) (6.5.4).
那为什么不完全隐藏模块中的指针,并外部化函数来操作它呢?这将避免别名问题。
我正在尝试确定以下前向声明在 ANSI-C 中是否有效:
第一个文件:
extern void * fptr; // opaque forward declaration.
int main (void) {
fptr = NULL; // set the function pointer to NULL
}
第二个文件:
typedef int (*fptr_t)(int);
fptr_t fptr; // real declaration of the function pointer
对我来说,这应该是无效的,因为 fptr
如果用两种不同的类型声明,但 gcc
和 clang
都不会给出任何警告。
我会对 C11 标准的精确点更感兴趣,这些点可以得出它为什么有效(或无效)的结论。
编辑:在 C11 标准中,6.2.7:2 说:
All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.
但我找不到如何确定 void*
是否与 fptr_t
兼容。
C99:
6.2.7 Compatible type and composite type
clause 2:
All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.
6.7.5.1 Pointer declarators
clause 2:
For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.
如果不深入研究标准,很容易看出 void
和一个函数是不兼容的类型。
我敢打赌这在 C11 中不会改变。长期以来,C 隐含地支持不同的代码和数据空间以及不同大小和表示的代码和数据指针,删除此功能并将语言限制在更小的机器子集上是很奇怪的。因此,请谨慎投票。有证据更好。
不,它是无效的,因为本质上您是将常规指针(NULL
、void*
)存储到一个实际上是函数指针的内存位置。你只是把它从编译器中隐藏起来,链接器并不关心,但最终你会有未定义的行为,因为这两种指针类型不一定兼容。当然它可能适用于许多系统,但可能不是全部。
有关函数指针与 void 指针的更多信息,请参见此处:can void* be used to store function pointers? - 虽然这与您介绍的情况略有不同,但答案仍然相关。
应该可以,但无效。在第一个文件中,您声明标识符 fptr
将在另一个编译单元中定义,并且它将是 void *
。在第二个文件中,您定义了标识符,但它现在是指向函数的指针。编译后的文件一般不会保留对象的类型(只保留地址)所以:
- 编译器不知道其他源声明了不同的类型,因此无法发出任何警告
- 链接器不需要控制,所以标准不需要警告,一个常见的实现不控制类型,所以也没有警告
在常见的实现中,所有指针都具有相同的表示,并且将指向函数的指针转换为指向 void 的指针不会改变表示,因此 别名 将给出预期的结果.
但它仍然是未定义的行为(*),因为 6.2.5 Types § 27 声明(强调我的):
A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.39) Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.
(*) 由于未通过通用实现进行测试,因此当前版本的编译器检测并优化 UB 的风险很小。但我永远不会在生产代码中这样做...
void *
是对象类型指针,不同于函数类型指针。它们不是兼容的类型。
但是:
Portabilitiy issues J.5.7. Function pointer casts
A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).
A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4).
那为什么不完全隐藏模块中的指针,并外部化函数来操作它呢?这将避免别名问题。