将函数指针转换为 uintptr_t / intptr_t 无效吗?
Is conversion of a function pointer to a uintptr_t / intptr_t invalid?
Microsoft extensions to C and C++:
To perform the same cast and also maintain ANSI compatibility, you can cast the function pointer
to a uintptr_t
before you cast it to a data pointer:
int ( * pfunc ) ();
int *pdata;
pdata = ( int * ) (uintptr_t) pfunc;
C 的基本原理,修订版 5.10,2003 年 4 月:
Even with an explicit cast, it is invalid to convert a function pointer to an object pointer
or a pointer to void, or vice versa.
C11:
7.20.1.4 Integer types capable of holding object pointers
是否意味着pdata = ( int * ) (uintptr_t) pfunc;
无效?
作为史蒂夫·萨米特 :
The C standard is written to assume that pointers to different object types, and especially pointers to function as opposed to object types, might have different representations.
虽然pdata = ( int * ) pfunc;
会导致UB,但似乎pdata = ( int * ) (uintptr_t) pfunc;
会导致IB。这是因为“Any pointer type may be converted to an integer type”和“An integer may be converted to any pointer type”和uintptr_t
是整数类型。
根据定义
int (*pfunc)();
int *pdata;
,作业
pdata = (int *)pfunc;
pdata = (int *)(uintptr_t)pfunc;
是,IMO,等效。在数据指针大小与函数指针大小相同或大于函数指针的平台上,两种赋值都将按需要工作。但是在数据指针比函数指针小的平台上,两次赋值都会不可避免地刮掉函数指针的一些位,导致数据指针无法转换回后面的原始函数指针。
特别是,我认为这两个赋值是等价的,尽管在第二个赋值中存在 (uintptr_t)
转换。我相信 cast 完全没有完成任何事情。
在数据指针小于函数指针且类型uintptr_t
与数据指针大小相同的平台上,在赋值
pdata = (int *)(uintptr_t)pfunc;
,转换为 (uintptr_t)
会刮掉 pfunc
的一些值。
在数据指针小于函数指针且类型uintptr_t
与函数指针大小相同的平台上,在赋值
pdata = (int *)(uintptr_t)pfunc;
,转换为 (int *)
会刮掉 pfunc
的一些值。
在这两种情况下,pdata
最终只会得到 pfunc
原始值的一小部分。
(这里我忽略了带有填充位等的架构的可能性。在一些奇怪的假设平台上,函数指针大于数据指针,但额外的位总是 0,这两个分配将再次工作。)
(我也忽略了 int *
与 void *
大小不同的可能性。我不确定这是否会影响答案,是否通过 [=尝试从 int (*)()
转换为 int *
时,24=] 或多或少是不必要的或必需的。)
转换为 uintptr_t
仅在定义了此类型时有效,在使用古老编译器的遗留系统上可能并非如此。但是请注意,uintptr_t
必须对任何对象指针都足够大,尤其是 char *
或 void *
,但可能比函数指针小。这种架构在今天很少见,Microsoft 编译器可能不再支持它们,但它们在 16-bit world(MS/DOS、Windows 1、2 和 3.x)中很常见,其中中型模型有 32 位分段代码指针和 16 位数据指针。
另请注意,C 标准允许 int *
和 void *
具有不同的大小和表示形式,尽管没有 Microsoft 编译器支持此类外来目标。
在具有现代编译器的当前系统上,所有数据指针和代码指针几乎总是具有相同的大小和表示形式。这实际上是 POSIX 兼容性的要求,因此使用中间转换到 (uintptr_t)
的建议是有效的。
为了完全的可移植性,如果目标是通过不透明的 void *
传递函数指针,您始终可以分配适当函数指针类型的对象,用 pfunc
初始化它并传递它的地址:
// setting up the void *
int (*pfunc)();
void *pdata = malloc(sizeof pfunc);
memcpy(pdata, &pfunc, sizeof pfunc);
// using the void *
int (**ppfunc)() = pdata;
(*ppfunc)(); // equivalent to (**ppfunc)();
Is conversion of a function pointer to a uintptr_t / intptr_t
invalid?
没有。它可能有效。可能是未定义的行为。
函数指针到 ìnt*
的转换未定义。也不是任何 object 指针。也不 void *
.
pdata = ( int * ) pfunc;
是 未定义的行为 。
允许将函数指针转换为整数类型,但有限制:
Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type. C17dr 6.3.2.3 6
也允许指针类型的整数。
An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation. C17dr 6.3.2.3 6
void *
到整数到 void *
被定义。 对象指针to/fromvoid*
已定义。那么 optional (u)intptr_t
类型足以 round-trip 成功。然而我们关心的是一个函数指针。通常足够的函数指针比 int *
.
更宽
因此将函数指针转换为 int *
只有通过整数类型才有意义,越宽越好。
VS 可能会通过 可选 类型 uintptr_t
进行推荐,并且在其他平台上 如果信息无损 可能就足够了。然而 uintmax_t
可能会减少信息损失,尤其是在指向整数步骤的函数指针中,所以我迂腐地建议:
pdata = ( int * ) (uintmax_t) pfunc;
无论采取何种步骤,代码都可能成为特定于实现的代码,值得保护。
#ifdef this && that
pdata = ( int * ) (uintmax_t) pfunc;
#else
#error TBD code
#endif
将解决方案从问题迁移到答案:
这是微软的回答:
Q: How exactly "cast the function pointer to a uintptr_t
before you cast it to a data pointer" leads to maintaining "ANSI compatibility"?
A: Without the cast to uintptr_t
it’s possible that the code will fail to compile with other compilers, even if they use the same pointer model. For example: https://gcc.godbolt.org/z/9EjTe1s4x - if you add the uintptr_t
it compiles without warnings/errors.
Microsoft extensions to C and C++:
To perform the same cast and also maintain ANSI compatibility, you can cast the function pointer to a
uintptr_t
before you cast it to a data pointer:int ( * pfunc ) (); int *pdata; pdata = ( int * ) (uintptr_t) pfunc;
C 的基本原理,修订版 5.10,2003 年 4 月:
Even with an explicit cast, it is invalid to convert a function pointer to an object pointer or a pointer to void, or vice versa.
C11:
7.20.1.4 Integer types capable of holding object pointers
是否意味着pdata = ( int * ) (uintptr_t) pfunc;
无效?
作为史蒂夫·萨米特
The C standard is written to assume that pointers to different object types, and especially pointers to function as opposed to object types, might have different representations.
虽然pdata = ( int * ) pfunc;
会导致UB,但似乎pdata = ( int * ) (uintptr_t) pfunc;
会导致IB。这是因为“Any pointer type may be converted to an integer type”和“An integer may be converted to any pointer type”和uintptr_t
是整数类型。
根据定义
int (*pfunc)();
int *pdata;
,作业
pdata = (int *)pfunc;
pdata = (int *)(uintptr_t)pfunc;
是,IMO,等效。在数据指针大小与函数指针大小相同或大于函数指针的平台上,两种赋值都将按需要工作。但是在数据指针比函数指针小的平台上,两次赋值都会不可避免地刮掉函数指针的一些位,导致数据指针无法转换回后面的原始函数指针。
特别是,我认为这两个赋值是等价的,尽管在第二个赋值中存在 (uintptr_t)
转换。我相信 cast 完全没有完成任何事情。
在数据指针小于函数指针且类型uintptr_t
与数据指针大小相同的平台上,在赋值
pdata = (int *)(uintptr_t)pfunc;
,转换为 (uintptr_t)
会刮掉 pfunc
的一些值。
在数据指针小于函数指针且类型uintptr_t
与函数指针大小相同的平台上,在赋值
pdata = (int *)(uintptr_t)pfunc;
,转换为 (int *)
会刮掉 pfunc
的一些值。
在这两种情况下,pdata
最终只会得到 pfunc
原始值的一小部分。
(这里我忽略了带有填充位等的架构的可能性。在一些奇怪的假设平台上,函数指针大于数据指针,但额外的位总是 0,这两个分配将再次工作。)
(我也忽略了 int *
与 void *
大小不同的可能性。我不确定这是否会影响答案,是否通过 [=尝试从 int (*)()
转换为 int *
时,24=] 或多或少是不必要的或必需的。)
转换为 uintptr_t
仅在定义了此类型时有效,在使用古老编译器的遗留系统上可能并非如此。但是请注意,uintptr_t
必须对任何对象指针都足够大,尤其是 char *
或 void *
,但可能比函数指针小。这种架构在今天很少见,Microsoft 编译器可能不再支持它们,但它们在 16-bit world(MS/DOS、Windows 1、2 和 3.x)中很常见,其中中型模型有 32 位分段代码指针和 16 位数据指针。
另请注意,C 标准允许 int *
和 void *
具有不同的大小和表示形式,尽管没有 Microsoft 编译器支持此类外来目标。
在具有现代编译器的当前系统上,所有数据指针和代码指针几乎总是具有相同的大小和表示形式。这实际上是 POSIX 兼容性的要求,因此使用中间转换到 (uintptr_t)
的建议是有效的。
为了完全的可移植性,如果目标是通过不透明的 void *
传递函数指针,您始终可以分配适当函数指针类型的对象,用 pfunc
初始化它并传递它的地址:
// setting up the void *
int (*pfunc)();
void *pdata = malloc(sizeof pfunc);
memcpy(pdata, &pfunc, sizeof pfunc);
// using the void *
int (**ppfunc)() = pdata;
(*ppfunc)(); // equivalent to (**ppfunc)();
Is conversion of a function pointer to a
uintptr_t / intptr_t
invalid?
没有。它可能有效。可能是未定义的行为。
函数指针到 ìnt*
的转换未定义。也不是任何 object 指针。也不 void *
.
pdata = ( int * ) pfunc;
是 未定义的行为 。
允许将函数指针转换为整数类型,但有限制:
Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type. C17dr 6.3.2.3 6
也允许指针类型的整数。
An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation. C17dr 6.3.2.3 6
void *
到整数到 void *
被定义。 对象指针to/fromvoid*
已定义。那么 optional (u)intptr_t
类型足以 round-trip 成功。然而我们关心的是一个函数指针。通常足够的函数指针比 int *
.
因此将函数指针转换为 int *
只有通过整数类型才有意义,越宽越好。
VS 可能会通过 可选 类型 uintptr_t
进行推荐,并且在其他平台上 如果信息无损 可能就足够了。然而 uintmax_t
可能会减少信息损失,尤其是在指向整数步骤的函数指针中,所以我迂腐地建议:
pdata = ( int * ) (uintmax_t) pfunc;
无论采取何种步骤,代码都可能成为特定于实现的代码,值得保护。
#ifdef this && that
pdata = ( int * ) (uintmax_t) pfunc;
#else
#error TBD code
#endif
将解决方案从问题迁移到答案:
这是微软的回答:
Q: How exactly "cast the function pointer to a
uintptr_t
before you cast it to a data pointer" leads to maintaining "ANSI compatibility"?A: Without the cast to
uintptr_t
it’s possible that the code will fail to compile with other compilers, even if they use the same pointer model. For example: https://gcc.godbolt.org/z/9EjTe1s4x - if you add theuintptr_t
it compiles without warnings/errors.