如何将多个结构传递给一个函数(ANSI C)

时间:2011-08-05 16:15:00

标签: c arrays struct buffer typedef

我正在编写一个旨在以大约3-7 MHz运行的嵌入式应用程序,因此速度非常重要。该设备也只有32K的RAM。 动态内存分配不是和选项。那说......

我正在编写一个缓冲程序,它需要具有不同队列长度的循环缓冲区

  • 4x 1024+ int(+如果可用则意味着比这更多)
  • 4x 256 int
  • 1x 256 int(使用与上述不同的应用程序)
  • 1x 2048+ int(+如果可以,则意味着比这更多)

我已在以下代码中实现了它。您将注意到我创建了2个缓冲区作为大小为1的数组,这是因为我想使用相同的ReadBuff和WriteBuff函数访问所有缓冲区。我的问题是,当您将结构指针传递给函数时,编译器期望指针数据类型是相同的。然而,将指向BuffType2数据类型的指针传递给期望BuffType1的函数在C中有些非法,即使结构的结构完全相同(队列大小除外)也可能正常工作。我能看到解决这个问题的唯一方法是在创建我需要的各种结构数组后动态分配队列的大小。

你知道我已经设计了从尾部读取并写入头部的缓冲区,并且设计成当头部和尾部索引通过缓冲区大小时溢出为0,这也是一件好事。 / p>

这是一些代码

/***************************************************************\
    Macro Definitions
\***************************************************************/ 
// The following sizes MUST be 2^n
#define BUFF_TYPE_1_SIZE    (0h400) /*  1024    */
#define BUFF_TYPE_2_SIZE    (0h100) /*  256 */
#define BUFF_TYPE_3_SIZE    (0h100) /*  256 */
#define BUFF_TYPE_4_SIZE    (0h800) /*  2048    */

#define BUFF_TYPE_1_Q_MASK  (BUFF_TYPE_1_SIZE-0h1)  /* all ones */
#define BUFF_TYPE_2_Q_MASK  (BUFF_TYPE_2_SIZE-0h1)  /* all ones */
#define BUFF_TYPE_3_Q_MASK  (BUFF_TYPE_3_SIZE-0h1)  /* all ones */
#define BUFF_TYPE_4_Q_MASK  (BUFF_TYPE_4_SIZE-0h1)  /* all ones */

//  Error Codes
#define ERROR_BUFF_EMPTY    (-1)    /*  The buffer is empty */
#define ERROR_BUFF_DNE      (-2)    /*  The buffer does not exist   */

//  Test for Buffer Empty
#define BUFF_EMPTY      (Buffer.Head == Buffer.Tail)

// Test for data in buffer
#define BUFF_NOT_EMPTY  (Buffer.Head != Buffer.Tail)

//  Test for Buffer Full
#define BUFF_FULL (((Buffer.Head + 1) & Buffer.Mask) == Buffer.Tail)

/***************************************************************\
    Structure Definitions
\***************************************************************/ 
// Buffers(queues) - These need to be global to allow use in interrupts
typedef struct BuffType1
    {
    int Head        = 0;
    int Tail        = 0;
    int Mask        = BUFF_TYPE_1_Q_MASK;
    char Full   = false;
    char Empty  = true;
    int Q[BUFF_TYPE_1_SIZE];
    };
typedef struct BuffType2
    {
    int Head        = 0;
    int Tail        = 0;
    int Mask        = BUFF_TYPE_2_Q_MASK;
    char Full   = false;
    char Empty  = true;
    int Q[BUFF_TYPE_2_SIZE];
    };
typedef struct BuffType3
    {
    int Head        = 0;
    int Tail        = 0;
    int Mask        = BUFF_TYPE_3_Q_MASK;
    char Full   = false;
    char Empty  = true;
    int Q[BUFF_TYPE_3_SIZE];
    };
typedef struct BuffType4
    {
    int Head        = 0;
    int Tail        = 0;
    int Mask        = BUFF_TYPE_4_Q_MASK;
    char Full   = false;
    char Empty  = true;
    int Q[BUFF_TYPE_4_SIZE];
    };

/***************************************************************\
    Global Variables
\***************************************************************/ 
// FIFO Ring buffers - These need to be global to allow use in interrupts
struct BuffType1 MyBuff1[4];    
struct BuffType2 MyBuff2[4];    
struct BuffType3 MyBuff3[1];    
struct BuffType4 MyBuff4[1];    

/***************************************************************\
    Functions
\***************************************************************/ 

/*---------------------------------------------------------------
int ReadBuff(struct BuffType1* BufferPtr)
Parameters  :   struct* BufferPtr
                        this is a pointer to the buffer you wish to read
Returns     :   int
                        if empty    - error code
                        if not empty - The value that was popped off the buffer
Description :   This function returns the value at the tail of the
                    buffer or an error if the buffer is empty. The 
                    tail is incremented after the read and overflows
                    automatically
---------------------------------------------------------------*/
int ReadBuff(struct BuffType1* BufferPtr)
    {
    int Value = ERROR_BUFF_EMPTY;   // error
    if(BUFF_EMPTY)
        (*BufferPtr).Empty = true;  // set the empty flag
    else
        {
        (*BufferPtr).Empty = false; // reset the empty flag
        Value = (*BufferPtr).Q[(*BufferPtr).Tail];  //  Read value is at the tail of the buffer
        (*BufferPtr).Tail = ((*BufferPtr).Tail + 1)&((*BufferPtr).Mask) /* increment the tail,
            making sure that if it rolls over its queue size, it rolls over to 0    */
        }
    return Value;
    }

/*---------------------------------------------------------------
int WriteBuff(struct* BufferPtr, int Data)
Parameters  :   struct* BufferPtr
                        The pointer to the buffer you wish to write to
                    int Data
                        The Data you wish to write to the buffer
Returns     :   true    - write was successful
                    false   - the buffer is full and did not write
Description :   This function writes the data to the head of the
                    buffer and returns an error if the buffer is full.
                    if the buffer is full, no data is written. The 
                    head is incremented after the write and overflows
                    automatically
---------------------------------------------------------------*/
char WriteBuff(struct BuffType1* BufferPtr, int Data)
    {
    int Success = false;    // there was an error writing to the buffer
    if (BUFF_FULL) 
        (*BufferPtr).Full = true;   // Indicate buffer is full (next avaliable spot to write is the tail)
    else
        {
        (*BufferPtr).Full = false;
        (*BufferPtr).Q[(*BufferPtr).Head] = Data;
        (*BufferPtr).Head = ((*BufferPtr).Head + 1)&((*BufferPtr).Mask)
        }
    return !((*BufferPtr).Full;);   // Return false if buffer was full and write could not happen
    }

/*---------------------------------------------------------------
void ResetBuff(struct* BufferPtr)
Parameters  :   struct* BufferPtr
                        The pointer to the buffer you wish to write to
Returns     :   nothing
Description :   This function resets the buffer but does not clear
                    anything
---------------------------------------------------------------*/
void ResetBuff(struct BuffType1* BufferPtr)
    {
    (*BufferPtr).Head = (*BufferPtr).Tail = 0;
    (*BufferPtr).Full = false;
    (*BufferPtr).Empty = true;
    (*BufferPtr).Q[0] = 0; //or null if it is defined
    }

/*---------------------------------------------------------------
void NullBuff(struct* BufferPtr)
Parameters  :   struct* BufferPtr
                        The pointer to the buffer you wish to write to
Returns     :   nothing
Description :   This function resets all values in the queue to 0
---------------------------------------------------------------*/
void NullBuff(struct BuffType1* BufferPtr)
    {
    int i;
    for(i=0; i=((*BufferPtr).Mask); i++) // for all values in the buffer
        (*BufferPtr).Q = 0; // clear the value
    }

2 个答案:

答案 0 :(得分:2)

您可以为Q创建一个具有零/一长度数组的缓冲区的结构,然后将其与您要使用的缓冲区的实际结构联合起来。然后你还需要知道你要使用的长度,你可以从你的面具类型或其他结构成员获得。

struct BuffType
{
    int Head;
    int Tail;
    int Mask;
    char Full;
    char Empty;
    int Q[1];
};

struct BuffType1_storage
{
    int Head;
    int Tail;
    int Mask;
    char Full;
    char Empty;
    int Q[BUFF_TYPE_1_SIZE];
};

union BuffType1
{
    struct BuffType b;
    struct BuffType1_storage b1;
}

union BuffType1 MyBuff1[4];

然后,您可以将指针传递给union BuffType1或struct BuffType b成员,并且您知道它背后的内存适用于特定类型。

答案 1 :(得分:0)

暂且考虑性能因素,暂时考虑内存大小以及关注多结构类型输入参数,您是否考虑过创建一个头结构来描述每个缓冲区并指向有问题的缓冲区?这样你就可以传递一种结构类型(缓冲元数据),然后让处理函数自行解决如何继续。

编辑 - 因此,作为示例,您可以采用BuffTypeX类型并将缓冲区信息压缩为元结构,如下所示:

typedef struct buffMetaData
  {
  int Head, Tail, Mask, Full, Empty...
  int *PtrToBuff
  };

你的缓冲区将被定义为适当的直接数组,在初始化期间你必须初始化buffMetaData对象并将它们指向适当的存储位置。

然后你将实现你的处理函数以获取buffMetaData类型,读取内容并根据需要继续处理每个缓冲区(可能基于Mask或buffMetaData字段可以容纳的任何唯一ID标签)。

不可否认,这种方法可能是一个内存占用,因为所有缓冲区都需要事先全部分配,你还需要为堆栈,堆,其他变量保存RAM。