c++ - 这个函数铸造安全吗?
问题描述
这是标准的 Arduino 库。在第 92 行https://github.com/arduino/ArduinoCore-megaavr/blob/master/cores/arduino/WInterrupts.cpp用户提供的函数void(*)(void)
被转换为void(*)(void *)
(voidFuncPtrParam is void(*)(void *)
)
这是如何运作的?在第 138 行,始终使用参数调用用户提供的函数void *
,无论它是void(*)(void)
还是void(*)(void *)
。你甚至可以安全地做到这一点吗?
解决方案
这个函数铸造安全吗?
演员表本身总是安全的。您始终可以将函数指针转换为任何函数类型。
WInterrupts.cpp#L138)处的调用确实是未定义的行为,因为它通过指针调用void (void)
函数。void (void*)
这是如何运作的?
该代码是专门为 megaavr 编写的,可以在使用 megaavr 编译器编译的 megaavr 上运行。由于参数是如何传递给函数的,所以附加参数只是在调用方的寄存器中设置,而在被调用方则被忽略。
你甚至可以安全地做到这一点吗?
安全代码会做一个蹦床,可能会牺牲性能。
static void trampoline_call_user_func(void *param) {
// let's be super super safe and use memcpy instead of cast to void*
void (*userFunc)(void);
memcpy(&userFunc, ¶m, sizeof(fptr));
userFunc();
}
void attachInterrupt(uint8_t pin, void (*userFunc)(void), PinStatus mode) {
// let's be super super safe and use memcpy instead of cast to void*
void *param;
static_assert(sizeof(void*) >= sizeof(userFunc), "");
memcpy(¶m, &userFunc, sizeof(userFunc));
attachInterruptParam(pin, trampoline_call_user_func, param, NULL);
}
但是在任何现代架构上,只需通过void*
“将起作用”传递函数指针(尽管函数指针不需要能够转换为void*
),只是:
static void trampoline_call_user_func(void *param) {
// let's be super super safe and use memcpy instead of cast to void*
void (*userFunc)(void) = param;
userFunc();
}
void attachInterrupt(uint8_t pin, void (*userFunc)(void), PinStatus mode) {
attachInterruptParam(pin, trampoline_call_user_func, userFunc, NULL);
}
推荐阅读
- python - 使用 shift() 比较 Pandas Dataframe 中的行
- sql-server - 如何在数据集中查找度量
- python - 无法使用 beautifulsoup 和 requests 进行网页抓取
- python - 无法使用 mysql.connector 从 SQL 获取结果到 Python
- mysql - 如何通过拆分table1中teacher_id的值来编写查询以获取table2中的teacher_name
- r - 创建多个变量的多图分布到一个
- shell - 使用 docker -c 选项在 shell 脚本中将 if 语句作为单行运行
- python - 我想要一个可配置的 python 可调用。类与函数工厂?
- python - 如何在 xarray 中获得 JJAS 而不是 JJA 的季节?
- javascript - Amchart:禁用自然时间缩放