将子程序的地址转换为空指针
Casting an address of subroutine into void pointer
尽管函数指针大小并不总是与不透明指针大小相同,但可以使用 void 指针转换函数位置吗?
我已经搜索过不透明指针和转换函数指针。我发现函数指针和普通指针在某些系统上是不一样的。
void (*fptr)(void) = (void *) 0x00000009; // is that legal???
我觉得我应该这样做
void (*fptr)(void)= void(*)(void) 0x00000009;
它确实工作得很好,虽然我预计会出现一些错误或至少是警告
我正在使用 keil arm 编译器
void (*fptr)(void) = (void *) 0x00000009;
根据标准 C,不合法。
如果左边的指针是值为0的整数常量表达式,或者这样的表达式转换为(void *)
是一个空指针常量,可以赋值给一个函数指针:
void (*fptr)(void) = 0;
这个只适用于空指针常量。它甚至不适用于包含空指针的 void *
类型的变量。简单赋值的constraints (C11 6.5.16.1)包括
- the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
从严格意义上讲,该标准根本不提供将指向 void 的指针转换为指向函数的指针的机制!甚至没有演员。但是,它在大多数常见平台上都可用,作为记录的通用扩展 C11 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).
但 C 标准根本不需要它 - 事实上,在一个平台上使用 C 是可能的,在这个平台上,代码只能从根本不能作为数据访问的内存中执行。
不,问题是你不能在 void*
和函数指针之间切换,它们是不兼容的类型。 void*
仅是 对象指针 的通用指针类型。
在第二种情况下你有一个小的语法错误,应该是 (void(*)(void))
。解决这个问题,我们有:
void (*fptr)(void) = (void *) 0x00000009; // NOT OK
void (*fptr)(void) = (void(*)(void)) 0x00000009; // PERHAPS OK (but likely not on ARM)
关于前者,它根本不是有效的 C(但可能作为非标准编译器扩展得到支持)。具体来说,它违反了简单赋值规则,C17 6.5.16.1。 =
的两个操作数必须是兼容的指针类型。
关于后者,相关部分是C17 6.3.2.3/5
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.
空指针存在一些特殊情况,但这不适用于您的情况。
因此您可以可能 将值 9 转换为函数指针 - 您必须查看 Keil 编译器手册以了解实现定义的行为。从那时起,生成的函数指针将有什么有意义的用途,我不知道。
因为地址9
肯定不是ARM上的对齐地址。相反,它比我希望找到不可屏蔽中断 ISR 或类似中断的地址多了 1 个字节。那么你到底想在这里做什么?从向量 table 中获取 ISR 的地址?
第一个表达式在 C 中是完全合法的,因为 void *
指针类型的赋值和参数传递与任何其他指针类型兼容,如果指针不同,您可能 运行 会遇到麻烦大小大于整数。这不是好的编程风格,显然没有理由将整数文字 9
分配给函数指针。我猜不出你这样做是为了什么。
尽管如此,历史上有一些(好吧,非常少的)案例表明这件事已经完成(例如,将特殊值 SIG_DFL
和 SIG_IGN
赋予 signal(2)
系统调用,可以假设没有人会使用这些值来调用指针取消引用的函数,实际上,您可以在进程的页面零虚拟地址中使用一些不同于零的整数,以避免取消引用指针(所以你不能调用函数,否则你会立即违反分段),同时使用不同于零的值来假设 NULL
指针本身之外的几个值)
但是第二个表达式不合法。以类型标识符开头的表达式是无效的,因此 =
符号右侧的子表达式无效。要进行正确的分配和有效的转换,您必须编写:
void (*ptr)(void) = (void (*)(void)) 0x9; /* wth to write so many zeros? */
(将整个类型标记括在括号中)然后,您可以调用该函数:
(*ptr)();
或简称为:
ptr();
正在写作
void(*ptr)(void) = 9;
也是合法的,而整数到指针的转换几乎每个编译器都会发出警告。您将从那里获得一个可执行文件。
如果整数是 0
,那么编译器将关闭,因为 0
会自动转换为 NULL
指针。
编辑
为了说明我上面第一段提到的简单使用,来自FreeBSD 12.0的文件<sys/signal.h>
:
文件/usr/include/sys/signal.h
139 #define SIG_DFL ((__sighandler_t *)0)
140 #define SIG_IGN ((__sighandler_t *)1)
141 #define SIG_ERR ((__sighandler_t *)-1)
142 /* #define SIG_CATCH ((__sighandler_t *)2) See signalvar.h */
143 #define SIG_HOLD ((__sighandler_t *)3)
所有这些定义都恰好是问题中提到的类型,一个整数值转换为指向函数的指针,以允许 特殊值 表示非 executable/non 回调值。类型 __sighandler_t
定义为:
161 typedef void __sighandler_t(int);
以下。
来自 CLANG:
$ cc -std=c17 -c pru.c
$ cat pru.c
void (*ptr)(void) = (void *)0x9;
你甚至根本没有收到任何警告。
没有演员:
$ cc -std=c11 pru.c
pru.c:1:8: warning: incompatible integer to pointer conversion
initializing 'void (*)(void)' with an expression of type 'int'
[-Wint-conversion]
void (*ptr)(void) = 0x9;
^ ~~~
1 warning generated.
(只是警告,不是错误)
使用零文字:
$ cc -std=c11 -c pru.c
$ cat pru.c
void (*ptr)(void) = 0x0;
甚至根本没有警告。
尽管函数指针大小并不总是与不透明指针大小相同,但可以使用 void 指针转换函数位置吗?
我已经搜索过不透明指针和转换函数指针。我发现函数指针和普通指针在某些系统上是不一样的。
void (*fptr)(void) = (void *) 0x00000009; // is that legal???
我觉得我应该这样做
void (*fptr)(void)= void(*)(void) 0x00000009;
它确实工作得很好,虽然我预计会出现一些错误或至少是警告 我正在使用 keil arm 编译器
void (*fptr)(void) = (void *) 0x00000009;
根据标准 C,不合法。
如果左边的指针是值为0的整数常量表达式,或者这样的表达式转换为(void *)
是一个空指针常量,可以赋值给一个函数指针:
void (*fptr)(void) = 0;
这个只适用于空指针常量。它甚至不适用于包含空指针的 void *
类型的变量。简单赋值的constraints (C11 6.5.16.1)包括
- the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
从严格意义上讲,该标准根本不提供将指向 void 的指针转换为指向函数的指针的机制!甚至没有演员。但是,它在大多数常见平台上都可用,作为记录的通用扩展 C11 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).
但 C 标准根本不需要它 - 事实上,在一个平台上使用 C 是可能的,在这个平台上,代码只能从根本不能作为数据访问的内存中执行。
不,问题是你不能在 void*
和函数指针之间切换,它们是不兼容的类型。 void*
仅是 对象指针 的通用指针类型。
在第二种情况下你有一个小的语法错误,应该是 (void(*)(void))
。解决这个问题,我们有:
void (*fptr)(void) = (void *) 0x00000009; // NOT OK
void (*fptr)(void) = (void(*)(void)) 0x00000009; // PERHAPS OK (but likely not on ARM)
关于前者,它根本不是有效的 C(但可能作为非标准编译器扩展得到支持)。具体来说,它违反了简单赋值规则,C17 6.5.16.1。 =
的两个操作数必须是兼容的指针类型。
关于后者,相关部分是C17 6.3.2.3/5
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.
空指针存在一些特殊情况,但这不适用于您的情况。
因此您可以可能 将值 9 转换为函数指针 - 您必须查看 Keil 编译器手册以了解实现定义的行为。从那时起,生成的函数指针将有什么有意义的用途,我不知道。
因为地址9
肯定不是ARM上的对齐地址。相反,它比我希望找到不可屏蔽中断 ISR 或类似中断的地址多了 1 个字节。那么你到底想在这里做什么?从向量 table 中获取 ISR 的地址?
第一个表达式在 C 中是完全合法的,因为 void *
指针类型的赋值和参数传递与任何其他指针类型兼容,如果指针不同,您可能 运行 会遇到麻烦大小大于整数。这不是好的编程风格,显然没有理由将整数文字 9
分配给函数指针。我猜不出你这样做是为了什么。
尽管如此,历史上有一些(好吧,非常少的)案例表明这件事已经完成(例如,将特殊值 SIG_DFL
和 SIG_IGN
赋予 signal(2)
系统调用,可以假设没有人会使用这些值来调用指针取消引用的函数,实际上,您可以在进程的页面零虚拟地址中使用一些不同于零的整数,以避免取消引用指针(所以你不能调用函数,否则你会立即违反分段),同时使用不同于零的值来假设 NULL
指针本身之外的几个值)
但是第二个表达式不合法。以类型标识符开头的表达式是无效的,因此 =
符号右侧的子表达式无效。要进行正确的分配和有效的转换,您必须编写:
void (*ptr)(void) = (void (*)(void)) 0x9; /* wth to write so many zeros? */
(将整个类型标记括在括号中)然后,您可以调用该函数:
(*ptr)();
或简称为:
ptr();
正在写作
void(*ptr)(void) = 9;
也是合法的,而整数到指针的转换几乎每个编译器都会发出警告。您将从那里获得一个可执行文件。
如果整数是 0
,那么编译器将关闭,因为 0
会自动转换为 NULL
指针。
编辑
为了说明我上面第一段提到的简单使用,来自FreeBSD 12.0的文件<sys/signal.h>
:
文件/usr/include/sys/signal.h
139 #define SIG_DFL ((__sighandler_t *)0)
140 #define SIG_IGN ((__sighandler_t *)1)
141 #define SIG_ERR ((__sighandler_t *)-1)
142 /* #define SIG_CATCH ((__sighandler_t *)2) See signalvar.h */
143 #define SIG_HOLD ((__sighandler_t *)3)
所有这些定义都恰好是问题中提到的类型,一个整数值转换为指向函数的指针,以允许 特殊值 表示非 executable/non 回调值。类型 __sighandler_t
定义为:
161 typedef void __sighandler_t(int);
以下。
来自 CLANG:
$ cc -std=c17 -c pru.c
$ cat pru.c
void (*ptr)(void) = (void *)0x9;
你甚至根本没有收到任何警告。
没有演员:
$ cc -std=c11 pru.c
pru.c:1:8: warning: incompatible integer to pointer conversion
initializing 'void (*)(void)' with an expression of type 'int'
[-Wint-conversion]
void (*ptr)(void) = 0x9;
^ ~~~
1 warning generated.
(只是警告,不是错误) 使用零文字:
$ cc -std=c11 -c pru.c
$ cat pru.c
void (*ptr)(void) = 0x0;
甚至根本没有警告。