【问题标题】:Structures, internal structures and size in CC中的结构、内部结构和大小
【发布时间】:2016-03-31 09:55:52
【问题描述】:

我正在用 C 语言为嵌入式、资源受限的设备编程。

我有一个结构,如下所示:

typedef struct UjThread{
    struct{
        UInt32 runInstr;            
        UInt8* mailbox;     
    }appBucket;

    struct{
        UInt32 appId;                   
        UInt32 numInstr;            
        UInt32 allocMem;            
        UInt32 eepromStartAddr; 
    }appContract;

    UInt16 spBase;      //we use an empty ascending stack
    UInt16 spLimit; //also used for "isPtr"
    UInt16 localsBase;
    UInt32 stack[];

}UjThread;

我为每个对象启动一个线程并分配所需的内存(此结构为 92 字节,但我没有显示所有字段)。 但是,有些对象不会使用内部的 appContract 和 appBucket 结构,但仍会为这些结构分配内存。

有没有办法避免这种情况?要将内部结构指定为可选,或者可能提取这些内部结构的大小并将其从内存分配中减去?

我可以创建两个单独的结构,每种类型的对象一个,但我不希望这样做,因为我必须在任何地方调整我的代码以使用这两种类型的线程。

【问题讨论】:

  • 内部结构不能是可选的;如果内存消耗是一个问题,唯一的解决方案是如您所指出的:将appBucketappContract 定义为单独的结构,并在UjThread 内使用指向它们的指针。
  • 出于好奇,那是什么奇怪的处理器?多核却负担不起分配 92 字节?
  • @Lundin 我碰巧使用 500MHz 8 核 CPU 和 64kB RAM 来存储代码和数据,所以这并非闻所未闻。这也是一个相当现代的产品,但有notable heritage :)
  • @Lundin,带有 4KB RAM 的 Atmega128 嵌入式设备的美妙世界 :-)
  • @SvenAkkermans 除非我弄错了,否则这是一个单核 8 位 MCU。如果你的内存这么少,为什么要尝试实现像线程一样臃肿的东西?没有任何好处,它只会使程序变慢并消耗更多内存。要模拟多任务,只需制作一个调度程序。

标签: c embedded structure memory-efficient


【解决方案1】:

除了显而易见的 - 使用两个结构,我只看到其他两种可能性。 要么使用指向单独分配的 appContract 的指针,要么如果您需要的某些数据是互斥的,请使用联合。

【讨论】:

    【解决方案2】:

    考虑这种在 C 中工作的单继承实现。

    定义一个包含两个对象共有的所有元素的基本结构。请注意,我已将 stack 成员的类型更改为指针,因为在此设计中必须单独分配。

    typedef struct ThreadBase{
        UInt16 spBase;      //we use an empty ascending stack
        UInt16 spLimit; //also used for "isPtr"
        UInt16 localsBase;
        UInt32 *stack;
    }ThreadBase;
    

    然后声明另一个包含基础对象的结构作为第一个成员并附加额外的东西。

    typedef struct ThreadExtra{
        ThreadBase base;
    
        struct{
            UInt32 runInstr;            
            UInt8* mailbox;     
        }appBucket;
    
        struct{
            UInt32 appId;                   
            UInt32 numInstr;            
            UInt32 allocMem;            
            UInt32 eepromStartAddr; 
        }appContract;
    }ThreadExtra;
    

    现在您可以为只需要基本内容的线程定义一个 ThreadBase 对象。您可以为需要更多的线程定义一个 ThreadExtra 对象。但是您可以将 ThreadExtra 对象强制转换为 ThreadBase,因为 ThreadBase 是 ThreadExtra 的第一个成员。因此,在不处理 ThreadExtra 元素的通用代码中,您可以将所有 Thread 对象视为 ThreadBase 对象。

    【讨论】:

      【解决方案3】:

      如果您的可选字段位于struct 的开头,您可以调整已分配对象的地址,使可选字段驻留在未分配的内存中。使用offsetof 宏来确定强制数据的开始位置:

      offsetof(UjThread, spBase) // in bytes
      

      按此数量调整分配大小:

      UjThread *newThread;
      if (NoOptionalFields())
      {
          size_t sizeReduce = offsetof(UjThread, spBase);
          size_t size = sizeof(UjThread) - sizeReduce;
          newThread = (void*)((char*)malloc(size) - sizeReduce);
      }
      else
      {
          newThread = malloc(sizeof(UjThread));
      }
      

      要释放内存,别忘了把指针调回来:

      if (NoOptionalFields())
      {
          size_t sizeReduce = offsetof(UjThread, spBase);
          free((char*)newThread + sizeReduce);
      }
      else
      {
          free(newThread);
      }
      

      顺便说一句,由于您的struct 中有一个“灵活数组成员”,因此实际大小计算比我的示例中更复杂。但是你明白了 - 只需从分配大小和结果指针中减去可选字段的大小。

      【讨论】:

        【解决方案4】:

        如果堆栈具有固定大小,则可以使用惯用的 C 样式单继承:

        typedef struct {
          int a;
        } Base;
        
        typedef struct {
          Base base;
          int b;
        } Derived;
        
        void useBase(Base *);
        
        void test(void) {
          Base b;
          Derived d;
          useBase(&b);
          useBase(&d.base); // variant 1
          useBase((Base*)&d); // variant 2
        }
        

        唉,堆栈没有固定的大小,所以有点惯用的,如果不必要地摇摇晃晃的变体 2 将不起作用,但变体 1 会:

        typedef struct {
          int a[];
        } Small;
        
        typedef struct {
          int b;
          Small small;
        } Large;
        
        void useBase(Base *);
        
        void test(void) {
          Small s;
          Large l;
          useBase(&s);
          useBase(&l.small);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-02-17
          • 1970-01-01
          • 2012-12-12
          • 2019-04-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-08-14
          相关资源
          最近更新 更多