Within a struct I need some space where I can put in something. This space have to be able to collect all data types, so I want to define an union. The space is limited to n bytes (unsigned char).
How do I have to define my union, so that it can contain char, int, float and so on?
Have I to do it this way?
#define SIZE (128)
union {
unsigned char uchar[SIZE];
char schar[SIZE];
unsigned int uint[SIZE/sizeof(unsigned int)];
int sint[SIZE/sizeof(int)];
float flt[SIZE/sizeof(float)];
double dbl[SIZE/sizeof(double)];
}memory;
Or is there a possibility to define only the size of the unsigned char array and then to define size of the int array automatically? What does happen, if SIZE isn't divisible by 4?
EDIT: (related to the comments)
I want to build something like an timed event handler. That means, I have a struct containing an array of events. Each event has an execution time and a related function (stored as a pointer). When the timer counter of the event handler matches the event execution time, I call the related function. Within the function I will know, wich arguments are expected, so I don't need to save a tag value. The problem is, that the events are created within a funtion and because I don't want to make the events static (to save memory), I added some memory (ring buffer) to my event handler where all functions can put in some data. Each event will have a variable containing the pointer to the (first) data. The type of data are only the nativ data types, no own structs.
This is my current code:
startSystemClock() will be called at start up
executeSystemEvent() will be called by the interrupt service routine of timer 1 by setting sysEventHandler.execute=TRUE and a while(1)-loop checks this flag and then calls executeSystemEvent()
// typedefs requird for timed events
typedef union __attribute__ ((packed)){
int *i; // pointer, where data is stored
int value; // if there is a pointer assigned, value differs from zero
}systemEventData_u;
typedef union __attribute__ ((packed)){
int value; // if there is a pointer assigned, value differs from zero
void (*voidFct_noData)();
void (*voidFct_data)(systemEventData_u);
}systemEventFct_u;
typedef struct{
int time;
unsigned int id;
systemEventFct_u fct;
systemEventData_u data;
}systemEvent_t;
#define SYSTEM_EVENT_HANDLER_BUFFER_SIZE (10)
#define SYSTEM_EVENT_HANDLER_MEMORY_SIZE (10)
typedef struct{
unsigned int actualCnt;
unsigned int nextEventCnt;
unsigned char execute;
systemEvent_t events[SYSTEM_EVENT_HANDLER_BUFFER_SIZE];
systemEvent_t* write;
// create some persistent memory usable by all functions
int* memWrite;
union __attribute__ ((packed)){
unsigned char uchar[0];
char schar[0];
unsigned int uint[0];
int sint[SYSTEM_EVENT_HANDLER_MEMORY_SIZE];
float flt[0];
double dbl[0];
}memory;
}systemEventHandler_t;
void startSystemClock(){
// initialize event handler
sysEventHandler.actualCnt=0;
sysEventHandler.nextEventCnt=-1;
sysEventHandler.execute=FALSE;
sysEventHandler.write=sysEventHandler.events;
sysEventHandler.memWrite=sysEventHandler.memory.sint;
unsigned int i=SYSTEM_EVENT_HANDLER_BUFFER_SIZE;
systemEvent_t *ptr=sysEventHandler.events;
while(i--){
ptr->fct.value=0;
ptr->data.value=0;
ptr->time=0;
ptr++;
}
// initialize timer 1
TMR1 = 0x00;
T1CON = T3_OFF | T3_IDLE_CON | T3_GATE_OFF | T1_PS_1_8 | T1_SOURCE_INT;
IPC1SET = (INTERRUPT_PRIOR_TIMER1 << _IPC1_T1IP_POSITION) | (INTERRUPT_SUB_PRIOR_TIMER1 << _IPC1_T1IS_POSITION);
IFS0CLR = (1 << _IFS0_T1IF_POSITION);
IEC0SET = (1 << _IEC0_T1IE_POSITION);
PR1 = PR_TIMER1;
T1CONSET = (1 << _T1CON_ON_POSITION);
print_text("timer1 started\n\r");
}
void executeSystemEvent(){
asm("di");
int time=sysEventHandler.actualCnt;
asm("ei");
unsigned int i=SYSTEM_EVENT_HANDLER_BUFFER_SIZE;
unsigned int nextEventCnt=-1;
systemEvent_t *ptr=sysEventHandler.events;
while(i--){
// do not investigate, if there is no function pointer
// no function pointer means no event action
if(ptr->fct.value){
if(time>=ptr->time){
// execute function
if(ptr->data.value){
(*ptr->fct.voidFct_data)(ptr->data);
}else{
(*ptr->fct.voidFct_noData)();
}
ptr->fct.value=0;
}
}
ptr++;
}
// determine next event
// iterate again through whole queue to take added events into account also
i=SYSTEM_EVENT_HANDLER_BUFFER_SIZE;
ptr=sysEventHandler.events;
while(i--){
if(ptr->fct.value){
// get execution time to determine next one
if(ptr->time<nextEventCnt){
nextEventCnt=ptr->time;
}
}
ptr++;
}
asm("di");
sysEventHandler.nextEventCnt=nextEventCnt;
sysEventHandler.execute=FALSE;
asm("ei");
}
void addSystemEvent(systemEvent_t event){
// check, if this event will be the first event to execute
asm("di");
// get event execution time
event.time+=sysEventHandler.actualCnt;
// check, if it will be the next one to execute
if(sysEventHandler.nextEventCnt>event.time){
sysEventHandler.nextEventCnt=event.time;
}
asm("ei");
*sysEventHandler.write=event;
if(++sysEventHandler.write>=sysEventHandler.events+SYSTEM_EVENT_HANDLER_BUFFER_SIZE){
sysEventHandler.write=sysEventHandler.events;
}
}
int * storeSystemEventData(int data){
int *ptr=sysEventHandler.memWrite;
*ptr=data;
if(++sysEventHandler.memWrite>=sysEventHandler.memory.sint+SYSTEM_EVENT_HANDLER_MEMORY_SIZE){
sysEventHandler.memWrite=sysEventHandler.memory.sint;
}
return ptr;
}
To add an event, I write within any function:
systemEvent_t event;
event.fct.voidFct_data=&enablePinChangeInterrupt_wrapper;
event.data.i=storeSystemEventData((int)PUSHBUTTON_CN_BIT);
event.time=10;
addSystemEvent(event);
I know, that the storeSystemEventData-function isn't complete. But for my first purpose, I only need int, so it works.
You don't need to specify the array sizes except for the biggest. Just out-of-bounds access the other types.
#include "stdio.h"
union memory {
unsigned char uchar[128];
char schar[0];
unsigned int uint[0];
int sint[0];
float flt[0];
double dbl[0];
} ;
int main (void)
{
union memory my_mem;
my_mem.schar[5] = 'A';
my_mem.schar[6] = 'B';
my_mem.schar[7] = 'C';
my_mem.schar[8] = 'D';
printf ("%d\n", my_mem.uint[1]);
return 0;
}
C doesn't provide array bounds checking either way, so you're just out of luck if you try to access memory outside the memory object.
What does happen, if SIZE isn't divisible by 4?
I assume you ask the question about divisibility by 4 (as opposed by any other number) because it is a common sizeof(int). When SIZE is indivisible by any of the sizeofs, would end up with the largest array that fits fully inside the size, i.e. the number would be truncated. For example, setting SIZE to 13 when sizeof(int) is 4 would produce
int sint[3];
In other words, the size would be "rounded down" (truncated). If you prefer rounding up, use this expression:
unsigned int uint[(SIZE+sizeof(unsigned int)-1)/sizeof(unsigned int)];
Note, however, that the size of uint[] array may exceed the size of uchar.
is there a possibility to define only the size of the unsigned char array and then to define size of the int array automatically?
You could replace union with an array of chars, and convert void* pointer to int*, float*, etc. This would lead to a different syntax.
Related
I have encountered a problem, and I haven't found an answer in the internet and forums. I hope you can help.
There is an interface requirement to update the LOG parameter. The interface passes in the parameter index and the value to be added, and the interface adds the value to the corresponding parameter. The interface requires simple implementation and no complicated judgments.
My idea is to create a mapping table that records the starting address and data type of each parameter. When the interface is called, the parameter address is obtained according to the parameter index and forced to be converted to the corresponding type, and then the addition operation is performed.
The problem with this solution is that the increase_log_info function is too complicated. How to simplify the increase_log_info function in C language? How parameter types can be mapped directly, rather than through an if...else condition.
Thanks in advance.
Note:
T_LOG_INFO is data structure definition and cannot be changed.
LOG_INFO_INCREASE is an update parameter interface provided
externally and cannot be changed.
Other codes can be changed.
#pragma once
/********************************can not be change, begin**********************************/
/* T_LOG_INFO is data structure definition and cannot be changed. */
typedef struct
{
unsigned short tx_num;
unsigned int tx_bytes;
unsigned short rx_num;
unsigned int rx_bytes;
unsigned char discard_num;
unsigned int discard_bytes;
// There are many parameters behind, not listed
}T_LOG_INFO;
T_LOG_INFO g_log_info;
/* This macro is called very frequently, and efficiency needs to be considered.
** LOG_INFO_INCREASE is an update parameter interface provided externally and cannot be changed. */
//#define LOG_INFO_INCREASE(para_idx, inc_val)
/********************************can not be change, end**********************************/
/********************************an alternative, begin**********************************/
enum
{
LOG_PARA_IDX_TX_NUM,
LOG_PARA_IDX_TX_BYTES,
LOG_PARA_IDX_RX_NUM,
LOG_PARA_IDX_RX_BYTES,
LOG_PARA_IDX_DISCARD_NUM,
LOG_PARA_IDX_DISCARD_BYTES,
LOG_PARA_IDX_MAX
};
enum
{
DATA_TYPE_U8,
DATA_TYPE_U16,
DATA_TYPE_U32
};
typedef struct
{
/* Indicates the offset of this parameter in the structure. */
unsigned char offset;
/* Indicates the data type of the parameter. */
unsigned char data_type;
}T_PARA_MAPPING;
/* This table can also be calculated during system initialization. */
T_PARA_MAPPING g_para_mapping_table[LOG_PARA_IDX_MAX] =
{
{0, DATA_TYPE_U16}, // LOG_PARA_IDX_TX_NUM
{4, DATA_TYPE_U32}, // LOG_PARA_IDX_TX_BYTES
{8, DATA_TYPE_U16}, // LOG_PARA_IDX_RX_NUM
{12, DATA_TYPE_U32}, // LOG_PARA_IDX_RX_BYTES
{16, DATA_TYPE_U8}, // LOG_PARA_IDX_DISCARD_NUM
{20, DATA_TYPE_U32} // LOG_PARA_IDX_DISCARD_BYTES
};
/* How to simplify the function??? especially to remove the judgment. */
static inline void increase_log_info(unsigned int para_idx, unsigned inc_val)
{
unsigned int data_type = g_para_mapping_table[para_idx].data_type;
/* Get the parameter address and cast it to the corresponding type pointer before adding. */
if (data_type == DATA_TYPE_U8)
{
*((unsigned char*)(((unsigned char*)&g_log_info) + g_para_mapping_table[para_idx].offset)) += inc_val;
}
else if (data_type == DATA_TYPE_U16)
{
*((unsigned short*)(((unsigned char*)&g_log_info) + g_para_mapping_table[para_idx].offset)) += inc_val;
}
else
{
*((unsigned int*)(((unsigned char*)&g_log_info) + g_para_mapping_table[para_idx].offset)) += inc_val;
}
}
/* This macro is called very frequently, and efficiency needs to be considered. */
#define LOG_INFO_INCREASE(para_idx, inc_val) increase_log_info(para_idx, inc_val)
/********************************an alternative, end**********************************/
/********************************test case, begin**********************************/
void increase_log_info_test()
{
LOG_INFO_INCREASE(LOG_PARA_IDX_TX_NUM, 1);
LOG_INFO_INCREASE(LOG_PARA_IDX_TX_NUM, 2);
LOG_INFO_INCREASE(LOG_PARA_IDX_TX_NUM, 3);
LOG_INFO_INCREASE(LOG_PARA_IDX_RX_BYTES, 10);
LOG_INFO_INCREASE(LOG_PARA_IDX_RX_BYTES, 20);
LOG_INFO_INCREASE(LOG_PARA_IDX_RX_BYTES, 30);
}
/********************************test case, end**********************************/
Quick answer with, maybe, syntax errors. But I hope the idea can be grasped.
I would prepare an array with a "datatype" for every member of the T_LOG_INFO struct:
{
unsigned short tx_num;
unsigned int tx_bytes;
unsigned short rx_num;
...
}
Copy/paste the above struct and, with a lot of editing, the array would be declared like this:
const char datatypes[LOG_PARA_IDX_MAX] = {
/* unsigned short tx_num; */ 2,
/* unsigned int tx_bytes; */ 4,
/* unsigned short rx_num; */ 2,
...
}
For lazyness, I used numbers like 2, 4 and so on. They indicate mainly the length, but they can carry other info (20=array of 20 char...).
Then I would declare another data structure (the final one):
struct datadesc {
int addr;
char kind;
} datadesc_array[LOG_PARA_IDX_MAX];
Prepare the table in code (the program itself) with:
address=&g_log_info;
for (int i=0; i<LOG_PARA_IDX_MAX; i++) {
datadesc_array[i].addr = address;
datadesc_array[i].kind = datatypes[i];
address += datatypes[i]; // simple like this if datatypes[] only accounts for length...
}
At this point, when you receive a command, you do:
param_addr = datadesc[para_idx].addr;
param_kind = datadesc[para_idx].kind;
switch (param_kind) {
case 2: // unsigned short
*(short*) param_addr = ...
break;
case 4: // unsigned int
*(unsigned int*) param_addr = ...
}
This way you have a reduced set of cases, just one for every data type you cope with. The only long work is done while preparing the datatypes[] array,
Basically, you can't. Data types in C are a purely static, compile-time construct. There's no such thing as a variable that holds a type or anything like that.
So you fundamentally can't avoid a chain of ifs, or else a switch, with the code for each different type written out separately. In principle you can avoid some of the repetition using macros, but that may actually end up being harder to read and understand.
The efficiency isn't so bad, though. A modern compiler is likely to handle an if chain in an efficient way, and switch might be even better.
Given this, your array of offsets and types may be unnecessary complexity. I would start with something much simpler, albeit longer:
void increase_log_info(unsigned int para_idx, unsigned inc_val) {
switch (para_idx) {
case LOG_PARA_IDX_TX_NUM:
g_log_info.tx_num += inc_val;
break;
case LOG_PARA_IDX_TX_BYTES:
g_log_info.tx_bytes += inc_val;
break;
// ...
}
}
It'll probably compile into a jump table. That's probably more efficient than what you have, as we don't have to keep accessing the mapping table somewhere else in memory and doing the corresponding address calculations. If it really can be proved to be too slow, you could consider some alternatives, but don't optimize prematurely!
This also has the advantage of being robust if the offset or types of any of the g_log_info members changes. In your code, you have to remember to manually update your mapping table, or else face very confusing bugs which the compiler will give you no help in detecting.
If you have an extremely large number of members, consider generating this function's C code with a script instead of by hand.
If this is inlined and called with a constant para_idx value, you can expect the compiler to propagate the constant and emit only the code to update the specific member in question.
I'm working on an application that needs to convert any type of the variable from big to little-endian.
My system works with different variable types (16, 32, and 64 bits wide), and I need to be able to change the endianness with a single function. I wrote a function that manages to swap bytes in any variable however, I'm not happy with it. It works, but it requires dereferencing void pointers, which are prone to error with the double star...
Is there any better way to approach the problem?
Is there any way to avoid void pointers as return value? I was thinking about switch-case loop (eg. case 4 bytes -> return int32) however, I don't know how to write a function prototype for a function that returns different values.
My function:
void* swapBytes(void* number, int bytes_num){
void* swapped;
unsigned __int8* single_byte_ptr;
swapped = malloc(bytes_num * sizeof(__int8));
for (int i = 0; i<bytes_num; i++){
single_byte_ptr =((unsigned __int8*)number)+i; //get current byte
*( (__int8*)(swapped)+((bytes_num-1)-i)) = (unsigned __int8)*single_byte_ptr; //save the byte in new position
}
return swapped;
}
the way I call this function
__int64 big_number = 35169804487071;
big_number = *(__int64*)(swapBytes(&big_number, 8));
One problem you have is that you're leaking memory. You return a pointer to malloc'ed memory, but you're not saving the pointer when you return.
Given that you're assigning the result back to the same value, you're better off updating the existing variable, swapping the current byte with a byte on the "opposite" side.
You also don't need to use a void * anyplace other than the parameter type. Inside of the function, just use a pointer to an unsigned char or unsigned __int8 to work through the bytes.
void swapBytes(void* number, int bytes_num)
{
unsigned __int8* ptr = number;
for (int i = 0; i<bytes_num/2; i++) {
unsigned __int8 tmp = ptr[i];
ptr[i] = ptr[bytes_num-1-i];
ptr[bytes_num-1-i] = tmp;
}
}
Then call it like this:
swapBytes(&big_number, sizeof(big_number));
Your solution is very over-engineered and also entirely unsuitable for embedded systems such as MPC57xx.
Any integer type can get safely iterated across using a pointer to character. Assuming uint8_t* is a character type for your compiler, it's as simple as this:
void little_to_big16 (uint8_t big [sizeof(uint16_t)],
const uint8_t little [sizeof(uint16_t)])
{
big[0] = little[1];
big[1] = little[0];
}
Then write big_to_little16, big_to_little32 etc etc as needed. Such functions can and should probably be inlined too.
Example of use:
#include <stdio.h>
#include <inttypes.h>
void little_to_big16 (uint8_t big [sizeof(uint16_t)],
const uint8_t little [sizeof(uint16_t)])
{
big[0] = little[1];
big[1] = little[0];
}
int main (void)
{
uint16_t little = 0xAABB;
uint16_t big;
little_to_big16((uint8_t*)&big, (uint8_t*)&little);
printf("%"PRIx16, big);
}
Output on x86 little endian:
bbaa
I have an array of structures as a function parameter and the size of the array is dynamic. My coworker said that I'll have to use a double pointer since the values contained in the array of struct will be overwritten.
The parameter that will become a double pointer is the following :
xPIDConfig_t **pxPIDConfig
Here is what the structure looks like for the xPIDConfig_t :
typedef struct
{
ePIDType_t ePIDType;
/* Common fields for the different types of PID */
float fLowerSaturationLimit;
float fUpperSaturationLimit;
float fOldInput;
float fIError;
uint32_t ulDeltaTime;
eBool_t bSaturationEnable;
eBool_t bAntiWindupEnable;
eBool_t bNegativeErrorEmptyIError;
union
{
/* Parallel PID fields */
struct
{
float fProportionalGain;
float fIntegralGain;
float fDerivativeGain;
}xParallelPID;
/* Non-interactive PID fields */
struct
{
float fControllerGain;
uint32_t ulIntegralTime;
uint32_t ulDerivativeTime;
}xNonInteractivePID;
}xUniqueFields;
}xPIDConfig_t;
The size of the array of pxPIDConfig will vary.
But I am not sure how to malloc that double pointer or even how to use the function containing the double pointer.
I was just wondering if anyone had a good example of code of how to use a function with a double pointer array of variating size? and how to properly change the values contained in the array itself inside a function?
Right now this is how I change the values within the function :
pxPIDConfig->ePIDType = ePIDType;
pxPIDConfig->fOldInput = 0;
pxPIDConfig->fIError = 0;
pxPIDConfig->ulDeltaTime = ulDeltaTime;
pxPIDConfig->bSaturationEnable = bIsSaturationEnable;
pxPIDConfig->bAntiWindupEnable = bIsAntiWindupEnable;
pxPIDConfig->bNegativeErrorEmptyIError = bNegativeErrorEmptyIError;
when the pointer is double do I have to use double '->'? This is very confusing for me.
Thank you all for the help
/***************** EDIT ************************************
My function is working right now, but I got told I need to use memory allocation since the size of my arrays varies according to the number of loops I want to implement.
Here are the parameters of my function :
eError_t eControlCascadeInit( uint8_t ucNumberOfLoops, ePIDType_t *pePIDType, xPIDConfig_t **pxPIDConfig, float *pfLowerLimit, float *pfUpperLimit, uint32_t *pulDeltaTime, \
eBool_t *pbIsSaturationEnable, eBool_t *pbIsAntiWindupEnable, eBool_t *pbNegativeErrorEmptyIError, \
float *pfPGain, float *pfIGain, float *pfDGain, float *pfCGain, uint32_t *pulITime, uint32_t *pulDTime )
They're all arrays of size ucNumberOfLoops. All of them are read-only arrays, except for the pxPIDConfig one that is write-only. The function initializes all the xPIDConfig_t present in the array with the parameters passed to the function through array.
array[ 0 ] contains the parameters for the first PID controller being initialized.
array[ 1 ] contains the parameters for the second PID controller being initialized and so on...
It's like that for all the parameters in the function.
Hope it makes my question more clear?
Here you have an example of how to use double-pointer, to change the pointer in the function:
void allocate(xPIDConfig_t **array, size_t size)
{
*array = malloc(sizeof(**array) * size);
/* some examples how to access the struct members vi double pointer -*
(*array) -> ulDeltaTime = 100;
(**array).ulDeltaTime = 100;
(*(array + 5)) -> ulDeltaTime = 100;
array[5] -> ulDeltaTime = 100;
(*array[5]).ulDeltaTime = 100;
}
int main(void)
{
xPIDConfig_t *array;
allocate(&array, 100);
printf("%s\n", array ? "success" : "failure");
free(array);
}
You would only need a double pointer if the function reallocates the array to a different size. If the size isn't changing, you can just pass a pointer to (usually the first) element of the array, along with any size or index required by the function. For example:
extern void frobPidConfig(xPIDConfig_t *);
// 'frob' the xPIDConfig_t array elements from index a to b
void frobSomePidConfigs(xPIDConfig_t *pidconfigs, unsigned int a, unsigned int b)
{
unsigned int i;
for (i = a; i <= b; i++)
{
frobPidConfig(&pidConfigs[i]);
// Example of member access:
pidConfigs[i].ulDeltaTime = 42;
}
}
Example of calling code:
xPIDConfig_t *pidConfigs;
unsigned int n = 10; // or whatever...
pidConfigs = calloc(sizeof *pidConfigs, n);
if (!pidConfigs)
{
// Allocation error
exit(1);
}
/* ... */
frobSomePidConfigs(pidConfigs, 2, 5);
On the other hand, if the function needs to reallocate the array and initialize any new elements, it could be done using a double pointer like this:
extern void initPidConfig(xPIDConfig_t *);
void reallocPidConfigs(xPIDConfig_t **pidConfigs, unsigned int oldSize, unsigned int newSize)
{
unsigned int i;
// Reallocate to new size
xPIDConfig_t *realloced = realloc(*pidConfigs, sizeof **pidConfigs * newSize);
if (newSize && !realloced)
{
// allocation error
exit(EXIT_FAILURE);
}
*pidConfigs = realloced;
// Initialize any additional elements
for (i = oldSize; i < newSize; i++)
{
initPidConfig(*pidConfigs + i); // or: initPidConfig(&(*pidConfigs)[i]);
// Examples of member access:
(*pidConfigs)[i].bSaturationEnable = true;
(*pidConfigs + i)->bAntiWindupEnable = true;
}
}
Example of calling code:
xPIDConfig_t *pidConfigs = NULL;
// Note: realloc of the NULL pointer in *pidConfigs is OK.
reallocPidConfigs(&pidConfigs, 0, 10);
frobSomePidConfigs(pidConfigs, 2, 5);
Limited to addressing assumptions and questions regarding your title question:
"How to use double pointers (pointer to pointer) for an array of structures properly in standard C"
First, just because the function argument might have a double pointer (i.e. xPIDConfig_t **pxPIDConfig) does not mean that the variable need to be allocated memory with a double pointer, i.e. if the function eg is called like this: funcChangeParam(&pxPIDConfig); this often means that the object being passed needs to be changed in some way, requiring that the address of be passed, not the object itself. Also, if the object itself is a pointer, (such as a pointer to several instances of a struct object.) then the function used to pass the object for modification will be prototyped with arguments such as void funcChangeParam(xPIDConfig_t **pxPIDConfig); (Note the double pointer here.)
So with this function prototype Making the allocation of memory look like this:
void funcChangeParam(xPIDConfig_t **pxPIDConfig);
//allocate memory for multiple instances of struct
xPIDConfig_t *pxPIDConfig = malloc(countOfInstances * sizeof(*pxPIDConfig);
if(pxPIDConfig)
{
//use pxPIDConfig
funcChangeParam(&pxPIDConfig);pass pointer to multiple instances of struct
And references to the object members inside the calling function could use the following notation. Eg:
//in a loop or other construct where i is defined from 0 to countOfInstances - 1
(*pxPIDConfig)[i].ePIDType = ePIDType;//modification of assignment per your example
//etc.
//The following is a trivial example for illustration purposes.
//Code here uses a simplified struct, function
//prototype, and simple calling example, the concept
//of which easily translates to what you are
//asking about.
typedef struct {
int num;
}test_s;
void change(test_s **new);
int main(){
test_s *test = malloc(10*sizeof *test);
change(&test);
return 0;
}
void change(test_s **new)
{
for(int i=0;i<10;i++)
{
(*new)[i].num = (i+1)*3; //init all instances to some value
}
}
I'm getting an error on a struct call inside a function. In the code, I am converting two bytes of data into one 16-bit data and I want to store it into one of my struct variables. However, I am getting an error that I can't pinpoint. The compiler is telling me that the error is in the line unsigned int fat.sector_size = combinedBytes;. What am I doing wrong here? Thanks in advance!
EDIT: The error that I'm getting is
main.c:62:19: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘.’ token
main.c:62:19: error: expected expression before ‘.’ token`
struct fileSystem_info{
unsigned int sector_size; //Sector size
int cluster_size_in_sectors; //Cluster size in sectors
int entries_in_root_directory; //Number of entries in root directory
int sectors_per_fat; //Sectors per file allocation table
int reserved_sectors; //Number of reserved sectors on the disk
int hidden_sectors; //Number of hidden sectors on the disk
int sector_number_of_first_copy_of_fat; //Sector number of the first copy of the file allocation table
int sector_number_of_first_sector_of_root_directory; //Sector number of the first sector of the root directory
int sector_numner_of_first_sector_of_first_usable_data_cluster; //Sector number of the first sector of the first usable data cluster
};
//Converts two 8 bit data to one 16 bit data
unsigned converter(unsigned mostSignificant_bit, unsigned leastSignificant_bit){
uint16_t value = (uint16_t)(mostSignificant_bit << 8) | leastSignificant_bit;
//return((mostSignificant_bit * 256) + leastSignificant_bit);
return (value);
}
unsigned int sectorSize (){
struct fileSystem_info fat;
unsigned char first_byte = buffer[11];
printf("%hhu \n", first_byte);
unsigned char second_byte = buffer[12];
printf("%hhu \n", second_byte);
unsigned int combinedBytes = converter ((int)second_byte, (int)first_byte);
unsigned int fat.sector_size = combinedBytes;
return (combinedBytes);
}
Here
unsigned int fat.sector_size = combinedBytes;
remove the type
fat.sector_size = combinedBytes;
(... and perhaps have a break ... ;-)
Refering the question on version-info in the comments below:
Do something like this:
#define VERSION_MAJOR (0) /* \ */
#define VERSION_MINOR (1) /* +--<-- adjust those for upcoming releases. */
#define VERSION_MICRO (42) /* / */
struct fileSystem_info
{
unsigned int version;
unsigned int sector_size; //Sector size
...
When initialising an instance of struct fileSystem_info, do:
struct fileSystem_info fsi = {0};
fsi.version = (VERSION_MAJOR << 24) | (VERSION_MINOR << 16) | VERSION_MICRO};
Doing so allows you a maximum version number of 255.255.65535.
As you always write version to disk you could later determine which version you wrote by reading the first unsigned int version from the structure an then in decide how to go on reading. This might be relevant in case the structure and how its content is to be interpreted following version changed during the different releases of your program.
An identifier cannot have ..
At
unsigned int fat.sector_size = combinedBytes;
you are defining a new variable of unsigned int type, called fat.sector_size (which is an invalid identifier.
If you want to refer to the member sector_size of the fat variable, don't use the syntax for variable definition. Just write
fat.sector_size = combinedBytes;
I am tring to get sound samples from microphone through Fez Panda 2. I am using rlp to accomplish that. Here is my code:
int GHAL_AnalogIn_Read(unsigned char channel)
{
return ((*((int*)(ADC_DATA_BASE_ADDRESS) + channel)) >>8) & 0x3FF;
}
int ReadAudio(unsigned int *generalArray, void **args, unsigned int argsCount ,unsigned int *argSize)
{
unsigned char *buffer = (unsigned char*)args[0];
int buffer_lengh = argSize[0];
unsigned char channel = *(unsigned char*)args[1];
int i=0;
while(i<buffer_lengh)
{
buffer[i] = GHAL_AnalogIn_Read(channel);
i++;
RLPext->Delay(100);
}
return 0;
}
The problem is that I need float values not unsigned char because I'm performing fft on these sound samples. So I need modification that will provide me float values. Any ideas?
Have you got experience with C? Especially with the meaning of * and &? * means: get the value pointed by address. So void ** args says someting like 'get the value pointed by the value obtained from address'. void is used to freely input anything you like. As you can not put whole structures or objects in an argument, you provide the pointer (an address) to a structure or object. By using the * you obtain the value on the address of the argument.
In C you do not pass whole arrays in an argument, you pass on the address of the first index.
Now you could simply re-factor your function to be something like:
int ReadAudio(unsigned int *generalArray, float arg, unsigned int argsCount ,unsigned int *argSize)
But as void **args is pointing to a buffer now, I think you should know what operation you want to perform on the data collected. An analog read will always provide you with an integer, most ADC (analog - digital - converter) are 10-bit or so.
If a float is 4 bytes on a 32-bit system, you want to mangle your data (unsigned char *buffer) in a 4-byte boundary.
EDIT: I have overlooked this in the documentatio: Note: Parameter of all function in RLP code file must have format follow this:Note: Parameter of all function in RLP code file must have format follow this:. Just cast the buffer bytes to a float by 4 byte boundary and I think you will do fine.