Re: My first code ever in C, how do you rate it?
Posted: Mon Aug 10, 2026 3:31 pm
I see, so if I understood correctly, it doesn't manually zero the bits in memory, it just reports as "free" the memory bytes to be used for other stuff.Not cleared. Those variables are part of the stack, and when a task exits that stack is handed back to the heap allocator...
This means they may be overwritten, maybe not, undefined behaviour.
I think I understand, if my function used just a local copy of the value, I could free the memory allocation of the original without problems.Whatever t points at (or whatever handle t contains) is passed on into the api, but not the memory location of t itself. This means t is a pointer or handle, you can let it go out of scope safely. Compare to:
Code: Select all
some_var t;
initialize_thing(&t);
do_thing_with_thing(&t, 123);
A pointer to t gets passed to all functions, meaning the memory at t is actively used by the API. Letting t go out of scope = stuff might break.
In my case however xTaskCreate binds my function to accept only a void* argument, so I don't have any choice but passing a pointer to the original like this, with EnArgs being the passed argument and declared at file scope:
Code: Select all
void enabler_function(void *pvParameters){
struct enabler_args *args = pvParameters;
bool status = 0; //0 is disabled, 1 is enabled
while(1){
xSemaphoreTake(enabler_button_semaphore, portMAX_DELAY); //wait until the interrupt is triggered
if(status == 1){ //if the cycle is enabled at the moment of pressing
blink_manager_disable(args->cycle_handle); //disabled the possibility to toggle the cycle
status = 0;
gpio_set_level(args->LED, 0); //status LED is OFF
printf("Cycles are disabled\n");
xSemaphoreTake(enabler_button_semaphore, pdMS_TO_TICKS(250)); //button debounce
}
else{ //if the cycle is already disabled at the moment of pressing
blink_manager_enable(args->cycle_handle); //enables the possibility to toggle the cycle
status = 1;
gpio_set_level(args->LED, 1); //status LED is ON
printf("Cycles are enabled\n");
xSemaphoreTake(enabler_button_semaphore, pdMS_TO_TICKS(250)); //button debounce
}
}
};
I've also found a workaround, copying the EnArgs into a function-scope struct.
This way I can keep EnArgs at app_main() scope and allow it to get cleared.
Code: Select all
void enabler_function(void *pvParameters){
struct enabler_args LocArgs = *(struct enabler_args*)pvParameters;
bool status = 0; //0 is disabled, 1 is enabled
while(1){
xSemaphoreTake(enabler_button_semaphore, portMAX_DELAY); //wait until the interrupt is triggered
if(status == 1){ //if the cycle is enabled at the moment of pressing
blink_manager_disable(LocArgs.cycle_handle); //disabled the possibility to toggle the cycle
status = 0;
gpio_set_level(LocArgs.LED, 0); //status LED is OFF
printf("Cycles are disabled\n");
xSemaphoreTake(enabler_button_semaphore, pdMS_TO_TICKS(250)); //button debounce
}
else{ //if the cycle is already disabled at the moment of pressing
blink_manager_enable(LocArgs.cycle_handle); //enables the possibility to toggle the cycle
status = 1;
gpio_set_level(LocArgs.LED, 1); //status LED is ON
printf("Cycles are enabled\n");
xSemaphoreTake(enabler_button_semaphore, pdMS_TO_TICKS(250)); //button debounce
}
}
};
Now it comes down to choose one of the two version, I think allowing EnArgs to get freed upon app_main()'s closure would be nice, less memory occupied for nothing, but I will still have the same duplicated values inside the initialised tasks anyway.
Initially I thought:
if I moved the initialization function of my API into the enabler_function, I could save up some memory for the heap allocator, passing to my API's functions only the pointer to the function-scope handle and allow the tasks to use that one.
But now I realize that EnArgs, which contains handles, GPIOs, bools... still has to be stored somewhere.
So as long as I only use pointers to the original WITHOUT making copies, the memory used will always be the same, and at this point it's better to keep EnArgs at file scope, work with pointers and make the project easier to update, improve, implement as a piece somewhere else...
With all the above reasoning I thought I finally understood my last issue as well, but trying to replicate how the Espressif's API sends data here and there proved me wrong.
I've tried to do some experiments:
Code: Select all
//right before app_main()
typedef struct {
int TestValue;
}TestStruct_t;
void TestFunction(void *TestArgument){
TestStruct_t LocStruct = *((TestStruct_t *)TestArgument); //All good here
TestStruct_t LocStruct2 = TestArgument; //Tried to do the same as you, IDE reports error " Initializing 'struct TestStruct_t' with an expression of incompatible type 'void *' [typecheck_convert_incompatible]"
LocStruct.TestValue =+ 10;
};
};
Code: Select all
//inside app_main()
//Testing if void* can take also non-pointers
TestStruct_t TestStruct;
TestStruct.TestValue=10;
printf("Initiating test to see if void* can take non-pointers as argument");
/*
-The following line returns as error:
Passing 'struct TestStruct_t' to parameter of incompatible type 'void *'
main.c:23:25: note: passing argument to parameter 'TestArgument' here [typecheck_convert_incompatible]
-And instead returns the following if I type the argument as (void *)TestStruct:
Operand of type 'struct TestStruct_t' where arithmetic or pointer type is required [typecheck_expect_scalar_operand]
*/
TestFunction(TestStruct);
If you can help me this last time I'll be grateful, otherwise if the content of the function can't be made public I understand, I'll just use it as it is.