2016-03-31 2 views
3

Я программирования для встраиваемых, с ограниченными ресурсами устройств в C.Структуры, внутренние структуры и размера в 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, но память для этих структур все равно будет выделена.

Есть ли способ избежать этого? Чтобы обозначить внутренние структуры как необязательные или, возможно, извлечь размер этих внутренних структур и вычесть их из выделения памяти?

Я мог бы создать две отдельные структуры, по одному на тип объекта, но я бы предпочел не делать этого, так как мне придется корректировать код везде, чтобы работать с двумя типами потоков.

+0

Внутренние структуры нельзя сделать опциональными; если потребление памяти является проблемой, единственное решение, как вы указали: определить 'appBucket' и' appContract' как отдельные структуры и использовать указатели для них внутри 'UjThread'. – Codor

+0

Из любопытства, какой это странный процессор? Многоядерный, но не может позволить себе выделить 92 байта? – Lundin

+0

@ Lundin Я использую 500-мегагерцовый 8-ядерный процессор с 64 КБ ОЗУ для кода и данных, так что это не неслыханно. Это довольно современный продукт, но с [заметным наследием] (http://www.wikiwand.com/en/Inmos) :) –

ответ

1

Рассмотрим реализацию одного наследования, который работает в С.

Определить базовую структуру, которая содержит все элементы, общие для обоих объектов. Обратите внимание, что я изменил тип члена 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

Помимо очевидного - используя две структуры, я вижу только две другие возможности. Либо используйте указатель на отдельно выделенный appContract, либо если некоторые из необходимых вам данных являются взаимоисключающими, используйте объединение.

0

Если ваши необязательные поля находятся в начале вашего 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, расчет фактического размера более сложный, чем в моем примере. Но вы получаете идею - просто вычтите размер необязательных полей как из размера распределения, так и из полученного указателя.

0

Если стек имел фиксированный размер, вы можете использовать идиоматических 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); 
} 
Смежные вопросы