![](/img/trans.png)
[英]C/C++: using a typedef'd function pointer to *declare* a function
[英]C: Pass an typedef'd enum value to a function as a pointer
概念
我有一个库,它运行一些库代码,然后在函数指针的帮助下调用用户回调函数。 作为一个最小的例子,它可以定义如下:
function_typDef functions[] = {
{ READ_INT , set_led_pwm, (void*)&pwm_value } };
^library func ^user func ^parameter
对于想知道这是做什么的任何人:在这种情况下,它用于菜单系统。 该库提供内部功能,如更改菜单、打印当前菜单或读取用户输入。 每次调用库时,可能会在内部函数之后运行用户回调函数,并以空指针作为参数传递。 上面的示例更改为输入模式,读取用户输入,将其转换为 INT,将此 int 存储在 &pwm_value 中,然后调用 set_led_pwm((void*)&pwm_value)。
问题
菜单的主要功能之一是在(子)菜单/菜单页面之间切换。 菜单列在 menu_menus_type 中,这是一个 typedef 的枚举。
typedef enum menu_menus_type{
MENU_MAIN,
MENU_SUB1,
MENU_COUNT
} menu_menus_type;
要更改菜单,应使用与上述 PWM 值更改相同的概念来更改菜单:
function_typDef functions[] = {
{ READ_INT , set_led_pwm, (void*)&pwm_value },
{ CHANGE_MENU , NULL , (void*) MENU_SUB1 } };
^library func ^user func ^parameter
这在我测试过的微控制器上工作得非常好。 但是,由于内存访问冲突,程序在单元测试期间崩溃。
问题
据我了解,枚举元素在预处理过程中只是被整数替换。 typedef 对此没有任何影响,它只是告诉编译器检查传递的标识符是否是 typedef 枚举的一部分。 正确的?
如果以上是正确的,(void*)MENU_SUB1 应该创建一个指向地址 1 的指针。当与操作系统一起使用时,这很可能不是有效地址,会检测到违规吗?
在实际更改菜单时,指针被取消引用为*(uint8_t*)pointer
。 这不应该返回存储在地址 1 的值吗? 那可以是任何东西,但肯定不是菜单的有效 ID。 为什么这适用于微控制器?
您正在使用的取消引用是错误的。
你不应该取消引用它。
为了得到这个数字,你应该直接转换它,就像你在functions
数组中使用的那样:
//Note that there are no pointer casting, nor derreference.
//All you want is to cast a "void pointer" (pointer) to an "int", so just do it:
int myNumber=(menu_menus_type)pointer;
如果您取消引用它,那么您将访问无效的内存,然后您就会崩溃。
注意:您应该转换为 ACTUAL 类型。 您不应uint8_t
转换为int
、 uint8_t
或任何其他类型,因为编译器可以自由地为enums
使用任何合适的大小,并且如果它选择不同的整数类型,则转换可能不正确。 正确的方法如示例中所示,直接对枚举类型使用强制转换。
指针被取消引用为(uint8_t ) 指针。 这不应该返回存储在地址 1 的值吗?
不,您没有在指针中存储任何内容。 您已将指针的值设置为指向地址1
。 取消引用此地址时,您正在尝试读取该地址中的数据,这不是您想要的。
您想恢复指针的值,这是您首先设置的值。 所以只需要一个演员就足够了。
但是为什么这在微控制器上起作用呢?
因为微控制器可能没有任何内存保护,而在计算机中你有内存保护(由操作系统提供)。
因此,微控制器尝试访问该指针,并且(不存在的)内存保护允许它这样做。 这就是测试崩溃的原因。
但是,通常,在微控制器的低地址中存储了中断表,因此 0x0 及以上的地址在这些设备中通常是有效的,因此读取成功并且不会出错。
但是读取值不是您期望的值。 它将包含地址 0x1 中的数据,而不是值 0x1
,系统将尝试更改为中断处理程序的菜单 ID 地址。
这也不正确。
您正在访问地址“0x1”。 假设一个带有 32 位指针的 uC,指针大小为 4,因此您正在访问从第二个字节而不是第一个字节开始的地址(第一个处理程序将位于地址 0x0,第二个位于地址 0x4,等等)。 .
在访问地址后,您将其转换为uint8_t*
,因此说“在这个地址上,包含一个只有一个字节大小的值。然后取消引用它,所以您正在读取地址 0x1 处的单个字节,这可能是无论如何,但不是地址,只是一个字节。可能,偶然地,它包含一个 0x1,所以你很幸运。
这是“未定义行为”的定义:任何事情都可能发生,甚至可以正常工作,但这种情况只是运气
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.