繁体   English   中英

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

问题

菜单的主要功能之一是在(子)菜单/菜单页面之间切换。 菜单列在 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转换为intuint8_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM