1. Initial Impressions of Structures in C and C++
A structure (struct) can encapsulate different types of data together, forming a new, more complex data type.
For example, in C, if we want to describe a student’s information, such as name, age, and score, we can define a structure like this:
struct Student { char name[20]; // Name, stored as a character array int age; // Age float score; // Score};
Here, the struct keyword defines a structure named Student, which contains three members: name, age, and score, representing the student’s name, age, and score, respectively, with different data types.
In C++, the definition of a structure is similar to that in C, and can be defined as follows:
struct Student { std::string name; // Name, using the more common string type in C++ int age; float score;};
As we can see, C++ uses std::string to represent strings, which is more convenient than C’s character arrays. For instance, operations like string concatenation and copying have corresponding member functions, eliminating the need for manual handling like in character arrays.
Structures in programming can organize related data together, making the code clearer, more readable, and easier to maintain. For example, in a student management system, using structures to represent student information allows for easy manipulation of various student data instead of scattering names, ages, scores, etc., across different variables, which can lead to messy code. Additionally, structures are fundamental for implementing complex data structures (like linked lists, trees, etc.), allowing different data elements to be combined logically to achieve various complex functionalities.
2. Structures in C
2.1. Definition and Declaration
The basic syntax for defining a structure in C is:
struct StructureName {
DataType Member1;
DataType Member2;
// More members can be added
};
For example, to define a structure representing a point:
struct Point { int x; int y;};
Here, struct is the keyword, Point is the structure name, and x and y are the members of the structure, both of which are of type int.
Using typedef can simplify the definition of structure variables. For example:
typedef struct { int x; int y;} Point;
After this, we can directly use Point to define variables without needing to write the struct keyword each time, like Point p1;.
After defining the structure type, there are various ways to declare structure variables. Besides the above method, we can also declare variables while defining the structure, like this:
struct Point { int x; int y;} p1, p2;
Here, two structure variables of type Point, p1 and p2, are declared simultaneously.
2.2. Member Access
In C, the member operator “.” is used to access the members of a structure. For example, with the following structure definition and variable declaration:
struct Student { char name[20]; int age; float score;};struct Student stu1;
To assign values to the members of stu1, we can write:
strcpy(stu1.name, "Xiao Ming"); // Note: use strcpy for string assignmentstu1.age = 20;stu1.score = 95.5;
Reading the values of members is done in a similar way, for example:
printf("Name: %s, Age: %d, Score: %.2f\n", stu1.name, stu1.age, stu1.score);
2.3. Initialization
There are various methods for initializing structures in C. One way is to initialize at the time of definition, for example:
struct Point { int x; int y;};struct Point p1 = {10, 20};
Alternatively, we can initialize by assigning values to members one by one:
struct Point p2;p2.x = 30;p2.y = 40;
For structures containing complex members (like string arrays), care must be taken to use appropriate functions (like strcpy) when initializing strings, as direct assignment is not allowed. For example:
struct Student { char name[20]; int age;};struct Student stu = {"Xiao Hong", 18}; // Initialized at definition// Or initialized by assigning values one by onestruct Student stu2;strcpy(stu2.name, "Xiao Gang");stu2.age = 19;
2.4. Structure Arrays
In C, structure arrays are a very practical data structure that allows multiple elements of the same structure type to be stored in an array. Defining a structure array is straightforward. For example, if we have already defined the Student structure, we can define a structure array of type Student:
struct Student { char name[20]; int age; float score;};struct Student students[3];
Here, we define a structure array named students, which can hold 3 elements of type Student.
When initializing a structure array, we can use the following method:
struct Student students[3] = { {"Zhang San", 20, 85.5}, {"Li Si", 21, 90.0}, {"Wang Wu", 19, 88.0}};
This initializes the three elements in the array respectively.
When using structure arrays, it is common to combine loops to operate on each element in the array. For example, to traverse the structure array and output each student’s information:
for (int i = 0; i < 3; i++) { printf("Name: %s, Age: %d, Score: %.2f\n", students[i].name, students[i].age, students[i].score);}
2.5. Structures as Function Parameters
In C, there are mainly two ways to pass structures as function parameters: by value and by pointer.
Passing by value means directly passing the structure variable as a parameter to the function, which will create a copy of the structure for operation. For example:
#include <stdio.h>
struct Point { int x; int y;};
// Function receives structure variable as parametervoid printPoint(struct Point p) { printf("x: %d, y: %d\n", p.x, p.y);}
int main() { struct Point p1 = {10, 20}; printPoint(p1); return 0;}
In this example, the printPoint function receives a Point structure variable p, and operations on p inside the function do not affect the external p1, as p is a copy of p1. This method is simple and intuitive, but when the structure is large, the overhead of copying the structure can impact performance.
Passing by pointer means passing the pointer of the structure variable as a parameter to the function, allowing the function to access and manipulate the structure through the pointer. For example:
#include <stdio.h>
struct Point { int x; int y;};
// Function receives structure pointer as parametervoid modifyPoint(struct Point *p) { p->x = 100; p->y = 200;}
int main() { struct Point p1 = {10, 20}; modifyPoint(&p1); printf("x: %d, y: %d\n", p1.x, p1.y); return 0;}
In this example, the modifyPoint function receives a pointer to a Point structure p, and through pointer p, it can directly modify the value of external p1. This method performs better when the structure is large, as only a pointer (typically 4 or 8 bytes) is passed instead of the entire structure copy.
3. Structures in C++
3.1. Extended Features of C++ Structures
C++ extends structures to incorporate more object-oriented programming features. C++ structures can not only contain different types of data members like C but can also include member functions. For example, defining a structure representing a rectangle:
struct Rectangle { int width; int height; // Member function to calculate the area of the rectangle int area() { return width * height; }};
In this example, the Rectangle structure not only has width and height data members but also includes an area member function to calculate the area of the rectangle. This approach makes the code more compact and intuitive, encapsulating related data and operations together.
C++ structures also introduce access specifiers, which are not present in C structures. Access specifiers control access to structure members. C++ has three access specifiers: public, private, and protected. By default, members of a C++ structure are public, similar to C structures where all members are public by default, but C++ structures can flexibly control member access permissions.
3.2. Member Functions
In C++ structures, member functions can access all members of the structure, including private members (if any). There are two ways to define member functions: one is to define them directly inside the structure, as shown in the area function of the Rectangle structure above; the other is to define them outside the structure, for example:
struct Circle { int radius; // Declare member function double area();};// Define member function outside the structuredouble Circle::area() { return 3.14 * radius * radius;}
Here, the area function is first declared in the Circle structure and then defined outside the structure using the scope resolution operator ::.
A constructor is a special member function that is automatically called when a structure object is created, mainly used to initialize the structure’s member variables. For example:
struct Point { int x; int y; // Constructor Point(int a, int b) : x(a), y(b) { // Additional initialization logic can be added here }};
Here, a parameterized constructor Point(int a, int b) is defined, initializing the x and y member variables using an initialization list.
A destructor is also a special member function that is automatically called when a structure object is destroyed, mainly used to release resources allocated during the object’s lifecycle (like dynamic memory). The destructor’s name is prefixed with a tilde ~, for example:
struct Resource { int* data; // Constructor Resource() { data = new int[10]; // Allocate dynamic memory } // Destructor ~Resource() { delete[] data; // Release dynamic memory }};
In this example, the constructor of the Resource structure allocates dynamic memory, and the destructor releases this memory when the object is destroyed, preventing memory leaks.
3.3. Access Specifiers
Public members in C++ structures can be accessed both inside and outside the structure. For instance, if the members of the previous Rectangle structure are all public:
struct Rectangle {public: int width; int height; int area() { return width * height; }};
When used externally:
Rectangle rect;rect.width = 5;rect.height = 3;int area = rect.area();
We can directly access the width, height member variables, and area member function.
Private members can only be accessed within the structure and cannot be accessed directly from outside. For example:
struct Person {private: int age;public: void setAge(int a) { age = a; } int getAge() { return age; }};
Outside, we cannot directly access age; we can only indirectly access it through public member functions setAge and getAge:
Person p;p.setAge(25);int age = p.getAge();
This protects the age member variable from being modified arbitrarily from outside, enhancing data security and encapsulation.
Protected members are similar to private members, but the difference is that protected members can be accessed in derived classes (new structures obtained through inheritance). For example:
struct Base {protected: int value;public: void setValue(int v) { value = v; }};struct Derived : public Base {public: void printValue() { // Can access the protected member from the base class std::cout << "Value: " << value << std::endl; }};
In the Derived structure, we can access the protected member value inherited from the Base structure, but it still cannot be accessed directly from outside.
3.4. Inheritance
Structures (struct) in C++ support inheritance, a feature that gives them powerful extensibility. The inheritance mechanism can be understood as allowing developers to derive new structures based on existing “structure templates”, much like creating an upgraded version of an existing “container”. This new structure not only inherits all member attributes and methods from the parent structure but can also add exclusive new features as needed, achieving functional extension and customization. For example:
struct Shape { int x; int y; void move(int newX, int newY) { x = newX; y = newY; }};struct Circle : public Shape { int radius; double area() { return 3.14 * radius * radius; }};
Here, the Circle structure inherits from the Shape structure, and the Circle structure not only has the x and y member variables and move member function from the Shape structure but also adds its own radius member variable and area member function. Through inheritance, existing code can be reused, reducing redundant development, and improving code maintainability and extensibility. For instance, if the logic of the move function in the Shape structure needs to be modified, all structures inheriting from Shape (like Circle) will be automatically affected without needing to modify each one individually.