【问题标题】:Array of jump tables in CC中的跳转表数组
【发布时间】:2019-05-02 23:34:59
【问题描述】:

我正在尝试优化对我制作的一些跳转表的访问,它们如下:

int (*const usart_ctrl_table[USART_READ_WRITE_CLEAR])() =
{zg_usartCtrlRead, zg_usartCtrlWrite, zg_usartCtrlClr};

int (*const usart_frame_table[USART_READ_WRITE_CLEAR])() =
{zg_usartFrameRead, zg_usartFrameWrite, zg_usartFrameClr};

int (*const usart_trig_ctrl_table[USART_READ_WRITE_CLEAR])() =
{zg_usartTrigctrlRead, zg_usartTrigctrlWrite, zg_usartTrigctrlClr};

如您所见,这些函数用于在硬件级别访问 usart 外围设备,并按读/写/清除的顺序排列在表中。

我想要做的是有另一个跳转表的跳转表,这样我可以在启动时初始化所有 usart 的寄存器,或者如果需要,稍后只需更改单个寄存器。

<datatype> (*usart_peripheral_table[<number of jump tables>])() = 
{usart_ctrl_table, usart_frame_table, usart_trig_ctrl_table};

这样我可以将该表暴露给我的中间件层,这将有助于在更改 HAL 时保持标准,并且我可以使用定义来索引该表,即

fn_ptr = usart_peripheral_table[CTRL_TABLE]
fn_ptr[WRITE](bitmask);
fn_ptr[READ](buffer);

正如您可能已经猜到的那样,我正在努力弄清楚如何构建这张表。我认为这是两件事之一:

  1. 另一个简单的指针数组,因为即使是跳转表本身也只是一个指针数组。因此我的初始化将是:

    const int* (*usart_peripheral_table[<number of jump tables])() = 
    {usart_ctrl_table, usart_frame_table, usart_trig_ctrl_table};
    

但是这似乎不起作用。然后我想:

  1. 指向指针的指针数组。于是我尝试了各种连击:

     const int**(*usart_perip...
    
    
     const int**(usart_perip...
    
    
    const int** (*usart_peripheral_table[<number of jump tables])() = 
    {&usart_ctrl_table, &usart_frame_table[0], usart_trig_ctrl_table};
    

似乎没有任何效果。在将该变量分配给指针到指针数组之前,是否需要将较低跳转表的地址存储在另一个指针中?即

    int* fn_ptr = usart_ctrl_table;


    <dataytype>(*const usart_periph[<number>])() = {fn_ptr};

在此先感谢,任何帮助将不胜感激。

MM25

编辑:

const int** (*const peripheral_table[1])() =
{&usart_ctrl_table[0]};


const int** (*const peripheral_table[1])() =
{usart_ctrl_table};

以上都给出错误“从不兼容的指针类型初始化”,我尝试过的所有其他组合也是如此

【问题讨论】:

  • 关于“什么是正确的方法” - 你回答了你的问题。您需要多个并行表。没关系。因此,最好的方法可能是定义一个指针,该指针可以配置为在运行时指向这些表中的一个或另一个。问:当您尝试这种方法时,究竟遇到了什么问题?问:您能否显示相关代码,以及确切的错误信息(如果适用)?
  • 添加了错误编辑

标签: c function-pointers jump-table


【解决方案1】:

您可能会发现为函数指针定义 typedef 会使您的代码更易于阅读和维护(尽管我也看到有人反对这样做):

#include <stdio.h>
#include <stdlib.h>

#define UART_RWC 3U

typedef int (*uart_ctl_func)(void);

int uart_read(void)
{
  printf("Read.\n");
  fflush(stdout);
  return 0;
}

int uart_write(void)
{
  printf("Write.\n");
  fflush(stdout);
  return(0);
}

int uart_clear(void)
{
  printf("Clear.\n");
  fflush(stdout);
  return 0;
}

uart_ctl_func uart_ctl_jump_table[][UART_RWC] = {
  { uart_read, uart_write, uart_clear },
  { uart_read, uart_write, uart_clear }
};

int main(void)
{
  uart_ctl_jump_table[0][1](); // Write.
  uart_ctl_jump_table[1][0](); // Read.
  uart_ctl_jump_table[1][2](); // Clear.

  return EXIT_SUCCESS;
}

下一步可能是将跳转表设为struct,因此您最终会编写Uart_ctl_table.frame.read(),或者至少为常量定义一个enum

#include <stdio.h>
#include <stdlib.h>

#define UART_RWC 3U

typedef int (*uart_ctl_func)(void);

int uart_read(void)
{
  printf("Read.\n");
  fflush(stdout);
  return 0;
}

int uart_write(void)
{
  printf("Write.\n");
  fflush(stdout);
  return(0);
}

int uart_clear(void)
{
  printf("Clear.\n");
  fflush(stdout);
  return 0;
}

typedef struct {
  uart_ctl_func read;
  uart_ctl_func write;
  uart_ctl_func clear;
} uart_ctl_set_t;

typedef struct {
  uart_ctl_set_t ctrl;
  uart_ctl_set_t frame;
  uart_ctl_set_t trig;
} uart_ctl_table_t;

const uart_ctl_table_t uart_ctl_table = {
  .ctrl = { uart_read, uart_write, uart_clear },
  .frame = { uart_read, uart_write, uart_clear },
  .trig = { uart_read, uart_write, uart_clear }
};

int main(void)
{
  uart_ctl_table.ctrl.write(); // Write.
  uart_ctl_table.frame.read(); // Read.
  uart_ctl_table.trig.clear(); // Clear.

  return EXIT_SUCCESS;
}

【讨论】:

  • 绝对同意,虽然我通常只是等到我真正了解我在用特定实验做什么之后再进行类型定义......只是为了确保在我玩耍时它都在我面前有想法。
【解决方案2】:

只需添加一个*,就像定义数组时添加的[]

int zg_usartCtrlRead();
int zg_usartCtrlWrite();
int zg_usartCtrlClr();
int zg_usartFrameRead();
int zg_usartFrameWrite();
int zg_usartFrameClr();
int zg_usartTrigctrlRead();
int zg_usartTrigctrlWrite();
int zg_usartTrigctrlClr();

int (*const usart_ctrl_table[])() =
{zg_usartCtrlRead, zg_usartCtrlWrite, zg_usartCtrlClr};

int (*const usart_frame_table[])() =
{zg_usartFrameRead, zg_usartFrameWrite, zg_usartFrameClr};

int (*const usart_trig_ctrl_table[])() =
{zg_usartTrigctrlRead, zg_usartTrigctrlWrite, zg_usartTrigctrlClr};

int (* const * const usart_peripheral_table[])() = 
{usart_ctrl_table, usart_frame_table, usart_trig_ctrl_table};

用法:

usart_peripheral_table[1][2](5, 1, 3, 5, 6);

顺便说一句,函数声明() 上的空参数列表表示未指定数量和类型的参数。如果您不希望将任何参数传递给您的函数,请执行 (void)

这个:

const int* (*usart_peripheral_table[<number of jump tables])();

是一个函数指针数组,它接受未指定数量的参数并返回一个指向常量整数的指针。

这个:

const int** (*usart_peripheral_table[<number of jump tables])() 

是一个函数指针数组,它接受未指定数量的参数并返回一个指向常量整数的指针。

您也可以使用二维数组:

int (* const usart_peripheral_table_2d[][3])() = {
    {
        zg_usartCtrlRead, zg_usartCtrlWrite, zg_usartCtrlClr, 
    }, {
        zg_usartFrameRead, zg_usartFrameWrite, zg_usartFrameClr,
    }, {
        zg_usartTrigctrlRead, zg_usartTrigctrlWrite, zg_usartTrigctrlClr,
    },
};

但也许您想编写将返回指向函数数组的指针的访问器函数。再简单不过了!

#include <stddef.h>

int (*usart_ctrl_table_get(size_t idx))() {
    return usart_ctrl_table[idx];
}

int (*usart_frame_table_get(size_t idx))() {
    return usart_frame_table[idx];
}

int (*usart_trig_ctrl_table_get(size_t idx))() {
    return usart_trig_ctrl_table[idx];
}

int (* const (* const usart_peripheral_table_indirect[])(size_t))() = {
    usart_ctrl_table_get, 
    usart_frame_table_get,
    usart_trig_ctrl_table_get,
};

使用示例:

int main() {
    usart_peripheral_table_indirect[2](1)();
}

【讨论】:

  • 您答案的第一部分似乎是正确的方法。然而,我很困惑。为什么我需要添加第二个 const 关键字,为什么我不能在 const 前面添加另一个 asterix 运算符 ie ...(**const ... 你能否进一步解释一下为什么这也会创建一个二维数组,如图所示在您的“使用”示例中?谢谢:)
  • re:第二次编辑:哈哈哈......也许我想制作一个指针表,指向一个指针,该指针指向另一个指针,然后还指向指向另一个指针的月亮! XD
  • 在大多数情况下,数组“衰减”或“应转换”(读作:神奇地转换)为指向数组第一个成员的指针。如果您真的不明白,请在网上获取一些好文章。我的usart_peripheral_table 是一个指向函数指针的数组,a[b] 运算符等于*(a + b),其余的来自指针算术(在 C 中也是一个很好的主题)。
  • 是的,我绝对理解衰减,这就是为什么我最初认为它要么是另一个简单的指针数组,要么是指向指针的指针数组......很高兴我至少在正确的轨道上。非常感谢您的回答,我会更仔细地研究并应用我所学的:)
猜你喜欢
  • 2012-04-13
  • 1970-01-01
  • 1970-01-01
  • 2014-09-19
  • 2013-06-08
  • 1970-01-01
  • 2011-06-25
  • 2015-12-03
  • 2020-05-07
相关资源
最近更新 更多