The difference between a bad programmer and a good one is whether he considers his code or his data structures more important. Bad programmers worry about the code. Good programmers worry about data structures and their relationships.
Linus Torvalds
In addition to fundamental types built into the language (integer, float, character, …) the C/C++ language allows you to create custom types. Custom types are used to combine related variables and to extend the C/C++ language to simplify it and make it easier to use when solving a particular problem. For example, if you are writing an application that draws pictures on the computer screen, then many of your functions will require screen coordinates (x, y) as the input parameters:
// draw a line between points (x1,y1) and (x2,y2); void DrawLine(int x1, int y1, int x2, int y2); // draw a circle of radius r with center in point (x,y) void DrawCircle(int x, int y, int r);
Repeating “int x, int y” everywhere is confusing and error prone. Can you understand the following code without looking at the functions definitions?
DrawLine(114, 124, 125, 565); // is it x1, y1, x2, y2 or x1, x2, y1, y2 or ??? DrawCircle(56, 42, 67); // where is radius? first? or last? or in the middle?
Instead, you can define a new custom type COORD using the struct keyword. In our new type the two individual coordinates are “combined” together to create a new entity:
struct COORD {
int x;
int y;
};
We can use our new data type anywhere we use built-in types:
// draw a line between points p1 and p2
void DrawLine(struct COORD p1, struct COORD p2);
// draw a circle of radius r with center in point p
void DrawCircle(struct COORD p, int r);
...
struct COORD p1 = { 114, 124 };
struct COORD p2 = { 125, 565 };
struct COORD p3 = { 56, 42 };
DrawLine(p1, p2);
DrawCircle(p3, 67);
You can access individual “fields” in the structure by using the “dot” notation:
struct COORD p;
p.x = 123;
p.y = 124;
cout << “The coordinates are x = “ << p.x << “ and y = “ << p.y << endl;
if(p.x > p.y) {
cout << “The x coordinate is greater than y” << endl;
} else if(p.y > p.x) {
cout << “The y coordinate is greater than x” << endl;
}
The structures are simple collection of individual fields and can include fields of different types (including other structures themselves!):
// define a structure for a circle: center point + radius
struct CIRCLE {
COORD center_point;
int radius;
};
// define size of something: two fields - witdh (w) and height (h)
struct SIZE {
int w;
int h;
};
// define a structure for a rectangle: top left corner + size
struct RECT {
COORD top_left_corner;
SIZE size;
};
To access fields in complex data structures, simply use the “dot” on each “level”:
struct RECT foo; foo. top_left_corner.x = 120; foo. top_left_corner.y = 435; foo. size.w = 14; foo. size.h = 94;
Finally, to make the code look nicer, you can use “typedef” to assign an alternative name (alias) to a type (and remove the need to use “struct” keyword everywhere!):
typedef struct CIRCLE CIRCLE; ... void DrawCircle(CIRCLE c); ... CIRCLE c; c.center.x = 56; c.center.y = 89; c.radius = 10; DrawCircle(c);
You can even use typedef and struct together in one statement!
typedef struct COORD {
int x;
int y;
} COORD;
In this example the “struct COORD { … }” part defines the type and the rest is a standard “typedef“.
You can use typedef to assign an alias to any type including the built-in types. For example, to make a program more readable you can typedef integer type to a special ERROR_CODE type to clearly distinguish error codes from all other possible usages for integers:
typedef int ERROR_CODE; ERROR_CODE DoSomething(); // the function returns an error code, nothing else!
Using struct and typedef allows a programmer to “customize” the C/C++ language and create better abstractions for solving a particular problem. In the future, we will be looking at different libraries (collections of functions) that can be used as building blocks for simplifying common tasks. All the good libraries extensively use struct and typedef to provide a simple yet powerful interface for developers.
Usually the data structures design is hard to change and it lives longer than the algorithm. A good data structures design makes programming easy and enjoyable. A bad data structures design leads to nightmares and bugs in the code.
Exercises
1) Write a function that calculates a distance between two given points.
2) Write a function that calculates the middle point between the two given points.
3) Write functions that calculates the area of a circle and a rectangle.
4) Define structures for 3-dimensional coordinates, a sphere, and a cube. Write functions to calculate volume of a sphere and a cube.
5) Define a data structure for
a Lunar Module from the game you wrote. Refactor the game to use the new data structure. Try to separate the code responsible for game physics from the code that reads/writes data to/from user.
Pingback: Practical programming | Random Notes