A Detailed Explanation of Structures in C (Part 1)
C language supports two categories of data types: built - in types (such as char, int, float, double, and _Bool for boolean values) and user - defined types (including arrays, structures, unions, and enums). This article focuses on the structure type, a powerful user - defined type.
1. Understanding Structure Types
Before delving into the details of structures, it's essential to understand what a structure is. Just as the saying goes, "Know your enemy and know yourself, and you can fight a hundred battles without defeat." To master structures, we first recall some knowledge about arrays.
Arrays are used to store elements of the same data type, and these elements are stored continuously in memory. But what if we need a data type that can hold multiple different data types at once? The answer is the structure—the main topic of this article.
In programming, when we encounter an entity with multiple different attributes, defining each attribute separately would be cumbersome and inconvenient. Structures are designed to handle such scenarios perfectly. For example, a "person" entity might have attributes like name (a string), age (an integer), and height (a floating - point number), and a structure can group these different types of attributes together.
2. Declaring a Structure
After understanding what a structure is, the next step is to learn how to define a structure.
2.1 Syntax for Structure Declaration
The general syntax for declaring a structure is as follows:
struct tag_name
{
member_type member_name;
// Other member declarations can be added here
} variable_list;
| Symbol | Meaning |
|---|---|
tag_name |
It can be regarded as the name of the structure, also known as the "tag" of the structure. |
member_type member_name |
This is the member list. Inside the curly braces, we define the variables (members) that the structure will store, including their data types and names. |
variable_list |
This is the variable list. Here we directly specify the names of the variables created based on this structure. |
Note: The name of the structure (tag) and the names of the variables created from the structure are two distinct concepts and should not be confused.
For example, to describe a student (with attributes like name, age, gender, and student ID), we can define the structure like this:
struct StudentInfo
{
char name[30]; // Student's name
int age; // Student's age
char gender[6]; // Student's gender
char studentID[25]; // Student's ID
};
2.2 Creating and Initializing Structure Variables
Let's look at another example of a structure definition and variable initialization:
struct BasicData
{
char initial;
int number;
double score;
};
struct StudentProfile
{
char name[30]; // Name
int age; // Age
char gender[6]; // Gender
char studentID[25]; // Student ID
};
int main()
{
// Initialization in the order of structure members
struct StudentProfile stu1 = { "Zhang San", 20, "Male", "S2023001" };
printf("Name: %s\n", stu1.name);
printf("Age: %d\n", stu1.age);
printf("Gender: %s\n", stu1.gender);
printf("Student ID: %s\n", stu1.studentID);
printf("\n");
// Initialization by specifying the order of members (designated initializers)
struct StudentProfile stu2 = { .age = 19, .name = "Li Hua", .studentID = "S2023002", .gender = "Female" };
printf("Name: %s\n", stu2.name);
printf("Age: %d\n", stu2.age);
printf("Gender: %s\n", stu2.gender);
printf("Student ID: %s\n", stu2.studentID);
return 0;
}
In addition to creating structure variables inside the main function, we can also create them outside of functions. If a structure variable is defined outside of all functions, it has a global scope (similar to a global variable).
// Method 1: Define the structure and create variables at the same time
struct StudentProfile
{
char name[30];
int age;
char gender[6];
char studentID[25];
} stuA, stuB; // Variables created along with the structure declaration
// Method 2: First declare the structure, then create variables
struct StudentProfile stuC;
int main()
{
// Code to use the structure variables can be added here
return 0;
}
3. Special Declaration of Structures (Anonymous Structures)
When declaring a structure, its possible to omit the structure name (the tag). Such a structure is called an "anonymous structure". For example:
// Anonymous structure type
struct
{
int num;
char ch;
double score;
} anonymousVar;
struct
{
int num;
char ch;
double score;
} * pAnonymous;
It should be noted that anonymous structures have a limitation: they can only be used once. Here, "used once" means that all variables of this anonymous structure must be created within its declaration (in the variable list). After the declaration of the anonymous structure is completed, we can no longer create new variables of this structure type.
Now, let's consider a question: Can we write pAnonymous = &anonymousVar;?
At first glance, it seems that there is no problem. The structure pointed to by the pointer pAnonymous and the structure of the variable anonymousVar have exactly the same members. But in fact, the compiler will consider these two as different structure types. The error message will be similar to: "incompatible types when assigning to type 'struct *' from type 'struct *'" (the specific error message may vary depending on the compiler).
To summarize the precautions for using anonymous structures:
- An anonymous structure can only be used once.
- Even if the member lists of two or more anonymous structures are exactly the same, the compiler will treat these structure variables as different types.
4. Self - Reference of Structures
Can a structure contain a member whose type is the structure itself? The answer is yes, but there are certain syntax rules. Let's look at the following incorrect code:
// According to the above idea, we might write the following wrong code:
struct WrongNode
{
int data;
struct WrongNode next;
};
Is the above code reasonable? If it is reasonable, what is the value of sizeof(struct WrongNode)? Obviously, this structure declaration has a problem. It gives us a feeling of falling into an infinite loop, and the size of this structure will be infinitely large.
The correct way of self - reference is:
struct Node
{
int data;
struct Node* next;
};
We use a pointer to the structure as a member variable. This way, we can not only determine the size of the structure, but also use this pointer to access the internal data of the structure, achieving two purposes at once.
In addition, we also need to pay attention to an error - prone writing method when using typedef to rename a structure:
// Wrong way of writing
typedef struct WrongNodeDef
{
int data;
Node* next; // Error: At this time, the compiler has not recognized 'Node' yet
} Node;
Is the above writing method feasible? The reason is that the compiler executes the code from top to bottom. When it executes the statement Node* next, the compiler has not fully read all the statements of typedef, so it will regard Node as an undefined identifier.
The correct writing method is:
typedef struct NodeDef
{
int data;
struct NodeDef* next;
} Node;
5. Summary
In this article, we have covered the uses of structures, the declaration of structures, the creation and initialization of structure variables, and the self - reference of structures. The content is not too much, and I hope readers can digest and understand it well.
Finally, if you think this article is well - written, please give it a like 👍. Thank you very much ❤️❤️❤️. Let's meet again in the chapter "A Detailed Explanation of Structures in C (Part 2)"!