First, let’s talk about
As we all know, the biggest difference between microcontroller programming and computer programming is that microcontrollers have very limited resources, and most low-end microcontrollers do not have an operating system. Except for some embedded-level chips that use Linux, most operations are done with simple RTOS, and some simple applications or chips may not use any system at all, running directly as bare-metal programs.
So, if we can’t change the main control chip, what can we do? We should squeeze out resources from the original program, and below I summarize a few common methods for everyone to refer to (specific content can be found online).
Union
Since union structures share the same memory, they can significantly save memory space. So, when should we use union? What are the characteristics of union?
Here are a few points to answer:
1) All members of the union and the union itself have the same address;
2) The storage model of the union is affected by endianness, we can test it with the following code (if the output is 1, it indicates little-endian mode, otherwise it is big-endian mode).
Endianness Basics
3) Union is different from struct; changes to members of the union may affect other member variables, so we need to form a mutual exclusion, for example, our sequential execution is actually mutually exclusive, so we can use union for function processing cache, etc. (I personally think it can also be considered time-sharing reuse and will not be affected by the initial value of memory).
#include<stdio.h>
typedef union _tag_test
{
char a;
int b;
}uTest;
uTest test;
unsigned char Checktype(void);
int main(void)
{
printf("%x\n",(unsigned int)&test.a);
printf("%x\n",(unsigned int)&test.b);
printf("%x\n",(unsigned int)&test);
printf("%d\n",Checktype());
}
unsigned char Checktype(void)
{
uTest chk;
chk.b = 0x01;
if(chk.a == 0x01)return 1;
return 0;
}
Bit Fields
Because in our usual programming process, the variables we use are closely related to the actual situation, just like the state of a switch, which we generally represent as 0 or 1 for on and off, respectively, so we can use a bit to represent it. If we use a char to store it, we would waste almost 7 bits. If similar situations arise in the future, then a large portion of memory will not be effectively utilized. Therefore, the bit field in C language is used to solve this problem.
However, we need to pay attention to the following points:
1) Bit fields are implemented in structures, where the length specified for a bit field cannot exceed the defined type, and a bit field can only be defined within the same storage unit.
2) The use of unnamed bit fields can be seen in the following code.
3) Since bit fields are related to data types, their memory usage is also related to the platform’s bitness (related content can be found online).
#include<stdio.h>
//Result: Compilation successful
//Reason: Conventional form (structure occupies two bytes)
typedef struct _tag_test1
{
char a:1;
char b:1;
char c:1;
char d:6;
sTest1;
//Result: Compilation fails
//Reason: d's bit field length of 10 exceeds char type length
/*
typedef struct _tag_test2
{
char a:1;
char b:1;
char c:1;
char d:10;
sTest2;
}*/
//Result: Compilation successful
//Reason: Below uses unnamed bit field, occupying 8 bytes
typedef struct _tag_test3
{
int a:1;
int b:1;
int :0;//Unnamed bit field
int c:1;
sTest3;
}int main(void)
{
printf("%d\n",sizeof(sTest1));
printf("%d\n",sizeof(sTest3));
printf("Welcome to follow our public account: The Last Bug\n");
}
Structure Alignment
The structure alignment issue may not be a concern for many, but it is often encountered in memory copying in communication fields. The structure alignment issue is also platform-dependent; to improve memory access efficiency, the CPU may read 2, 4, or 8 bytes at a time, so the compiler will automatically align the memory of structures.
No more unnecessary talk; the code speaks for itself:
#include<stdio.h>
#pragma pack(1)
//With byte alignment, precompiled results: 12, 8
//Without byte alignment, precompiled results: 6, 6
typedef struct _tag_test1{
char a;
int b;
char c;
}STest1;
typedef struct _tag_test2{
int b;
char a;
char c;
}STest2;
int main(void)
{
printf("%d\n",sizeof(STest1));
printf("%d\n",sizeof(STest2));
printf("Welcome to follow our public account: The Last Bug\n");
}
Algorithm Optimization
In fact, algorithm optimization mainly involves modifying some implementations of algorithms to achieve a balance between efficiency and memory usage. We all know that algorithms have complexity issues; most high-efficiency algorithms use memory to exchange for efficiency, which is a concept of trading space for time. Therefore, when our memory usage is limited, we can appropriately trade time for space to free up more space to implement more functions.
Similarly, when we are doing related designs, we should try to use local variables to reduce the use of global variables!

END
→Follow us, don’t get lost←