C:将 typedef 的枚举值作为指针传递给函数

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)。

问题

菜单的主要功能之一是在(子)menus/menu 页面之间切换。菜单列在 menu_menus_type 类型定义的枚举中。

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。这个 return 不应该是存储在地址 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 类型。您不应该转换为 intuint8_t 或任何其他类型,因为编译器可以自由地为 enums 使用任何 suitable 大小,如果它选择不同的大小整数类型,则转换可能不正确。 正确的做法是如示例所示,直接对枚举类型进行强制转换。


the pointer is dereferenced as (uint8_t)pointer. Shouldnt this return the value stored at address 1?

不,您没有在指针中存储任何内容。您已将指针的值设置为指向地址 1。当取消引用此地址时,您正在尝试读取该地址中的数据,这不是您想要的。

你想恢复指针的值,这是你一开始设置的。所以只要演员表就够了。


But why did this work on the microcontroller?

因为微控制器可能没有任何内存保护,而在计算机中你有内存保护(由OS给出)。

因此,微控制器尝试访问该指针,并且(不存在的)内存保护允许它这样做。这就是测试崩溃的原因。

但是,通常,在微控制器的低地址中存储了中断table,因此在这些设备中,0x0 及以上的地址通常是有效的,因此可以成功读取并且不会出现任何错误.

但是读取的值不是您期望的值。它将包含地址 0x1 中的数据,而不是值 0x1

, the system would try to change to menu ID address of interrupt handler.

这也不正确。

您正在访问地址“0x1”。 假设 uC 具有 32 位指针,指针大小为 4,因此您正在访问从第二个字节开始的地址,而不是第一个(第一个处理程序位于地址 0x0,第二个位于地址 0x4,依此类推)。 .

并且在访问该地址后,您将其转换为 uint8_t*,这样说“在该地址,包含一个只有一个字节大小的值。然后取消引用它,因此您正在读取单个地址 0x1 处的字节,可以是任何内容,但不是地址,只是一个字节。 可能,碰巧,它包含一个 0x1,所以你很幸运。

这是"Undefined Behaviour"的定义:任何事情都可能发生,甚至可以正常工作,但那种情况只是运气