In-depth Exploration of Struct in Embedded C Language

Note:

This article is not original content; it is modified based on original work, aimed at sharing knowledge and learning exchange. Since I cannot determine who the original author is, I apologize if this infringes on your rights; please contact me for removal.

In-depth Exploration of Struct in Embedded C Language
1-The Huge Role of Struct
In-depth Exploration of Struct in Embedded C Language

When facing a large C/C++ program from someone, we can evaluate the programmer’s experience and level just by looking at their use of struct. Because a large C/C++ program inevitably involves some (even a lot) of structures for data combination, these structures can combine data that originally belongs to a whole. To some extent, whether one knows how to use struct and how to use struct is a hallmark of whether a developer has rich development experience. In network protocols, communication control, and embedded systems C/C++ programming, we often need to transmit not just simple byte streams (char type arrays), but a whole composed of various data, represented as a structure.

Inexperienced developers often save all the content to be transmitted in a char array in order, using pointer offset methods to transmit network messages and other information. This makes programming complex, prone to errors, and once the control method and communication protocol change, the program requires very detailed modifications.

An experienced developer flexibly uses structures. For example, suppose the network or control protocol requires the transmission of three types of packets, with formats respectively as packetA, packetB, packetC:

struct structA 
{ 
 int a; 
 char b; 
}; 
struct structB 
{ 
 char a; 
 short b; 
}; 
struct structC 
{ 
 int a; 
 char b; 
 float c; 
}

For an excellent developer, the transmission of packets is designed as follows:

struct CommuPacket 
{ 
 int iPacketType; // Packet type flag 
 union // Each transmission is one of the three packets, using union 
 { 
 struct structA packetA; struct structB packetB; 
 struct structC packetC; 
 } 
};

During packet transmission, the entire struct CommuPacket is directly transmitted. Suppose the prototype of the sending function is as follows:

/* pSendData: Address of the byte stream to send, iLen: Length to send */ 
Send(char * pSendData, unsigned int iLen);

The sender can directly call to send an instance of struct CommuPacket sendCommuPacket:

Send( (char *)&sendCommuPacket , sizeof(CommuPacket) );

Suppose the prototype of the receiving function is as follows:

// pRecvData: Address of the byte stream to send, iLen: Length to receive 
// Return value: Actual number of bytes received 
unsigned int Recv(char * pRecvData, unsigned int iLen);

The receiver can directly call to save the received data in an instance of struct CommuPacket recvCommuPacket:

Recv( (char *)&recvCommuPacket , sizeof(CommuPacket) );

Next, judge the packet type for corresponding processing:

switch(recvCommuPacket.iPacketType) 
{ 
 case PACKET_A: 
… // A type packet processing 
break; 
 case PACKET_B: 
… // B type packet processing 
break; 
 case PACKET_C: 
… // C type packet processing 
break; 
}

What is most noteworthy in the above program is:

Send( (char *)&sendCommuPacket , sizeof(CommuPacket) ); 
Recv( (char *)&recvCommuPacket , sizeof(CommuPacket) );

The forced type conversion: (char *)&sendCommuPacket, (char *)&recvCommuPacket, first taking the address, then converting to char type pointer, so that we can directly use the function that processes byte streams. Using this forced type conversion, we can also facilitate program writing, for example, to initialize the memory where sendCommuPacket resides to 0, we can call the standard library function memset() like this:

memset((char *)&sendCommuPacket, 0, sizeof(CommuPacket));

2-Struct Member Alignment
Companies like Intel and Microsoft have posed a question similar to the following:
#include <iostream> 
#pragma pack(8) 
using namespace std;
struct example1 
{ 
 short a; 
 long b; 
}; 
struct example2 
{ 
 char c; 
 example1 struct1; 
 short e; 
}; 
#pragma pack() 
int main(int argc, char* argv[]) 
{ 
 example2 struct2; 
 cout << sizeof(example1) << endl; 
 cout << sizeof(example2) << endl; 
 cout << (unsigned int)(&struct2.struct1) - (unsigned int)(&struct2) << endl; 
 return 0; 
}

What is the input result of the program?

The answer is: 8 16 4

In-depth Exploration of Struct in Embedded C Language

2.1 Natural Alignment
Struct is a composite data type, whose constituent elements can be variables of basic data types (such as int, long, float, etc.) or data units of some composite data types (such as array, struct, union, etc.). For structures, the compiler automatically aligns member variables to improve computation efficiency. By default, the compiler allocates space for each member of the structure according to its natural alignment conditions. Each member is stored in memory in the order they are declared, and the address of the first member is the same as the address of the whole structure.

Natural alignment is the default alignment method, which aligns according to the largest member size in the structure.For example:

struct naturalalign 
{ 
 char a; 
 short b; 
 char c; 
}; 

In the above structure, the largest size is short, which is 2 bytes, so the char members a and c in the structure are aligned to 2. The result of sizeof(naturalalign) is 6 (the structure alignment is determined by the largest size);

If changed to:

struct naturalalign 
{ 
 char a; 
 int b; 
 char c; 
};

The result above is obviously 12, not 6. Let’s see, char is one byte, int is 4 bytes, so the largest in the structure is int b, so the alignment is calculated in 4-byte units. Therefore, even though char a is one byte, it still requires a 4-byte space.

2.2 Specified Alignment

Generally, the default alignment conditions can be changed using the following methods:Use the pseudo-instruction #pragma pack (n), and the compiler will align according to n bytes; · Use the pseudo-instruction #pragma pack (), to cancel the custom byte alignment.

Note:If the n specified in #pragma pack (n) is greater than the size of the largest member in the structure, it will not take effect; the structure will still align according to the size of the largest member.

For example:

#pragma pack (n) 
struct naturalalign 
{ 
 char a; 
 int b; 
 char c; 
}; 
#pragma pack ()

When n is 4, 8, or 16, the alignment method is the same, and the result of sizeof(naturalalign) is 12. However, when n is 2, it takes effect, making the result of sizeof(naturalalign) equal to 6.

Additionally, using __attribute((aligned (n))) can also align the structure members on n-byte boundaries.

2.3 Answer to Intel and Microsoft Interview Questions
The second line of the program #pragma pack(8) specifies an alignment of 8, but since the largest size in struct example1 is 4 (the size of the long variable is 4), struct example1 still aligns to 4 bytes, and the size of struct example1 is 8, which is the output result in line 18; struct example2 contains struct example1, whose maximum size of simple data members is 2 (short variable e), but because it contains struct example1, and the maximum member size in struct example1 is 4, struct example2 should also align to 4; the alignment specified by #pragma pack(8) does not apply to struct example2, hence the output result in line 19 is 16; since the members in struct example2 are aligned to 4, 3 empty spaces should be added after char variable c, and then there is the memory space for member struct1, resulting in the output in line 20 being 4.
3-Deep Differences Between Struct in C and C++
In C++ language, struct has the functionality of a “class,” and the difference from the keyword class is that the default access permission for member variables and functions in struct is public, while in class it is private.

For example, defining struct class and class class:

struct structA 
{ 
 char a; 
… 
} 
class classB 
{ 
 char a; 
 … 
} 
then: 
structA a; 
a.a = 'a'; // Access public member, valid 
classB b; 
b.a = 'a'; // Access private member, invalid

Many documents state that this has given the full difference between struct and class in C++, but in fact, there is another point that needs attention: Struct in C++ maintains full compatibility with struct in C (which aligns with C++’s original intention—“a better C”), therefore, the following operations are legal:

// Define struct 
struct structA 
{ 
 char a; 
 char b; 
 int c; 
}; 
structA a = {'a' , 'a' ,1}; // Directly assign initial values during definition

That is, struct can be directly assigned initial values to its member variables using { } when defined, while class cannot. In the classic book “Thinking C++ 2nd Edition,” the author emphasizes this point.

4-Programming Notes on Struct
#include <iostream>
using namespace std;
struct structA
{
 int iMember;
 char* cMember;
};
int main(int argc, char* argv[])
{
 structA instant1, instant2;
 char c = 'a';
 instant1.iMember = 1;
 instant1.cMember = &c;
 instant2 = instant1;
 cout << *(instant1.cMember) << endl;
 *(instant2.cMember) = 'b';
 cout << *(instant1.cMember) << endl;
 return 0;
}

In-depth Exploration of Struct in Embedded C Language

The output order of the above result is: a,b;

Why is this? Because in line 16, the modification of instant2 changes the value in instant1!

The reason is that the assignment statement instant2 = instant1 in line 14 uses variable-by-variable copying, which makes the cMember in both instant1 and instant2 point to the same memory, hence the modification of instant2 also modifies instant1.

In C language, when a structure contains pointer-type members, one must pay attention to whether the assignment statement causes the pointer-type members in the two instances to point to the same memory. In C++ language, when a structure contains pointer-type members, we need to rewrite the struct’s copy constructor and overload the “=” operator.
In-depth Exploration of Struct in Embedded C Language

END

This article is reprinted from the WeChat public account “Xiao Nan Microcontroller”. Copyright belongs to the original author. If there is any infringement, please contact for deletion.

Recommended Reading
Comic | The Tragic Chinese Software Development
A Huawei C language interview question that many people stumbled on!
An indescribable hard disk broke down, and it’s embarrassing to fix it…

→ Follow for more updates ←

Leave a Comment