On Structs
A struct is a quintessential example of an abstraction born out of engineering necessity — it was the demand that chose the struct, not the struct that created the demand.
1. What Is a Struct?
Wikipedia’s struct article describes it as follows:
In C, a struct (structure) is a data structure that falls under the category of composite (aggregate) data types.
In plain language, a struct is a composite data type created by combining basic data types as needed.
2. What Are Structs Good For?
In the early days of C, there was no struct. When the language needed to handle composite data types composed of multiple coupled data types, programmers had to use separate arrays of different types and figure out how to manage them. This was neither elegant nor easy to understand, so structs were born.
Engineers love encapsulation at every layer — making the abstract concrete, or rather making the concrete abstract — and structs are one of the most typical forms of encapsulation. For any large-scale project, basic data types alone are insufficient and cannot accommodate every requirement. Structs, with their flexibility and power, instantly provided a solution to these problems.
We can use structs to combine multiple variables to describe complex objects. We can bundle a set of related data together and pass it into a function or return it as a function’s return value, rather than handling scattered data pieces individually.
For example, if you were writing a product management program for a supermarket, a struct is far more concise than writing a pile of arrays to store the data:
| |
C was originally created as a tool for the UNIX operating system. In UNIX, UNIX v3 introduced a compiler with struct support, and it was immediately put to use in subsequent versions.
As you can see, the birth of structs was no accident — it was an inevitability under specific conditions. Essentially, large-scale engineering projects meant that programmers had a strong need for abstraction and encapsulation, which led them to create structs. It was the demand that chose the struct, not the struct that created the demand.
3. Things to Note When Using Structs
Structs are not exactly the same in C and C++. Here are some examples to illustrate.
- Defining and declaring a struct in C:
1 2 3 4 5 6 7 8struct goods //Product { char name[20]; //Product name float price; //Unit price float weight; //Weight }; struct goods var_goods; // struct goods is the type name, var_goods is the variable nameAlternatively, use the
typedefkeyword to define a type alias:1 2 3 4 5 6 7 8typedef struct goods //Product { char name[20]; //Product name float price; //Unit price float weight; //Weight } Good; Good var_goods; // "Good" is the type alias, var_goods is the variable name; using "Good" is similar to using "int"- Defining and declaring a struct in C++:
1 2 3 4 5 6 7 8struct goods //Product { char name[20]; //Product name float price; //Unit price float weight; //Weight }; goods var_goods; // goods is the type name, var_goods is the variable nameIn C++, both
structandclassare essentially classes, so they are used similarly andtypedefis not needed.The difference between
->and.with structsTo quote Lion Yang from Zhihu:
Simply put, one is a shortcut, and the other is syntactic sugar.
Let’s illustrate with code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21#include <iostream> using namespace std; struct goods //Product { char name[20]; //Product name float price; //Unit price float weight; //Weight }; int main(void) { goods s; //Struct instance goods* p; //Struct pointer p = new goods; s.price = 10; p->price = 10; if ((&s)->price == (*p).price) { cout << "Prices are equal!" << endl; } delete p; }As you can see, we used
s.priceandp->pricefor assignment, and we could also use(&s)->priceand(*p).pricefor reading. Fundamentally, there is not much difference — in memory, both work by offsetting from the struct’s base address to reach the desired memory location for reading or writing data. However, there is a subtle distinction in usage: the left operand of->must be a struct pointer variable, while the left operand of.must be a struct variable.The output of the above program is:
1Prices are equal!These two operators are leftovers from history. Back then, programming was mostly done from the machine’s perspective, and the code was closer to machine logic than to human intuition. If machines could think,
(*p).iwould be like counting i characters forward from p on a number line; whereasp.iwould require finding p’s position first before counting. Machines exist to serve humans, so the->operator was introduced as a more intuitive alternative. Using->lets us express(*p).iconcisely asp->i, which became the pragmatic compromise.