The author is Xie Enming, an official account of Programmer Union (wechat id: CoderHub). Please indicate the source of reprint. Original: www.jianshu.com/p/39b41aa5c…

The whole series of C language Exploration

Content abstract


  1. preface
  2. Define a struct
  3. Use of structures
  4. Structure pointer
  5. union
  6. enum
  7. conclusion
  8. Part 2 Lesson 7 Notice

1. Introduction


A lesson is the second part C language exploration trip | lesson 5: pretreatment, should be more relaxed.

This is going to be a very exciting and interesting lesson, but it’s going to be difficult.

As we all know, C language is a procedural programming language, which is different from Java, C++, and other object-oriented programming languages.

In object-oriented programming languages, there is the concept of a class.

C has no such “types” as classes, but can’t C “simulate” object-oriented programming?

No, C can simulate object-oriented programming if you design it well.

This lesson will give you the ability to implement “object-oriented” in C with structs.

We’ve already covered Pointers, arrays, strings, and preprocessing, so you’re pretty good with C. But we can’t stop there. We have to be Bigger than Bigger

In addition to using the variable types already defined in C, you can do something even more dramatic: create your own variable types.

We can call these “custom variable types” and look at three: struct, union, and enum.

Because when you need to write more complex programs, you will find it important to create custom variable types.

Fortunately, it’s not that hard to learn. But you need to focus on this one, because we’re going to be using structs all the time, starting next time.

2. Define a struct


What is a struct?

Struct is short for structure, so the technical term for struct is “structure.”

Definition: A struct is a collection of variables, but these variables can be of different types.

Does this definition remind you of our old friend arrays? Every member in an array must be of the same type, so structs are more flexible.

In general, structs are defined in.h headers, along with preprocessing commands and function prototypes.

Here is an example of a struct:

Struct name of your struct {char variable1; short variable2; int otherVariable; double numberDecimal; };Copy the code

Struct definitions start with the keyword struct, followed by the name of your custom struct (e.g. Dog, Cat, Person, etc.).

In general, in my code, my struct is named according to the variable naming rules, except that I will capitalize the struct name, for example: SchoolName.

But my normal variables are always lowercase, for example: studentNumber.

This is just a personal habit, so that you can distinguish between normal variables and custom variables in your code. I’ll learn about enum and union later, and I’m also used to capitalizing their names.

After the name of the struct, we need to write a pair of curly braces inside which we will write the various types of variables that your struct will contain.

In general, you have to define at least two variables inside curly braces of a struct. If there is only one variable, there is no point in defining the structure.

Note: don’t forget to add a semicolon (;) after the braces Because, after all, the struct is a variable, and the definition of a variable always ends with a semicolon.

As you can see, creating a custom variable is not complicated. A structure is a hodgepodge of primitive type variables.

Of course, we will also look at the nested definition of a structure (a structure contains another structure) later in the course.

An example of a structure


Suppose we need to customize a structure that stores the coordinates of a point on the screen.

Here’s an idea of the coordinates of the 2D world:

When we do research in the 2D world, we have two axes:

  • The horizontal axis (left to right, also commonly known as the X-axis).
  • The vertical axis (bottom to top, also commonly known as the Y-axis).

As long as math hasn’t been given back to elementary school physical education teachers, everyone should know the X and y axes.

Now, can you write the definition of a struct called Coordinate? I have eyes on you!

You can write your own first, and then check the reference answers we give:

struct Coordinate { int x; // int y; };Copy the code

Simple, isn’t it? Our Coordinate struct contains two variables, x and y, both of int type, representing the abscissa value and the ordinate value respectively.

And of course, if you want to, you can create a struct that represents a point in 3D space, just by adding the Z-axis to this Coordinate structure.

An array inside a structure


Structures can also hold arrays.

For example, we could construct a structure named Person (for “Person”) as follows:

struct Person { char firstName[100]; / / name char lastName [100]; / / name of char address [1000]; // address int age; // age int boy; // Gender, Boolean: 1 = boy, 0 = girl};Copy the code

As you can see, the structure variable contains five basic variables.

  • The first three represent first name, last name, and address, respectively, and are character arrays.

  • The fourth is age.

  • The fifth is gender, which is a “Boolean” (of course, C itself does not define a Boolean type (true or false), but a value can be used to “represent” a Boolean). Boy is an int whose value is 1. If it’s 0, it’s a girl.

This structure can be used to build an address book program. Of course, you can add other variables to the structure to make it better.

There is generally no limit to the number of variables in a structure as long as there is enough memory.

3. Use of structures


Now that our structures are defined in the.h header, we can use them in files that include this header.

Here’s how to create a variable of type Coordinate (we’ve defined this structure before, representing coordinates in two dimensions) :

#include "coordinate.h" // Assume that the header containing the structure definition is called coordinate.hint main(int argc, char *argv[]) { struct Coordinate point; // Create a variable of type Coordinate with the name pointreturn 0;
}
Copy the code

As above, we created a variable of type Coordinate, named Point.

This variable automatically has two child variables: x and y, both of type int, representing the abscissa and ordinate values of this two-dimensional coordinate, respectively.

You might ask, “Is it necessary to create the keyword struct at the beginning of a structure variable?”

Yes, it’s a must.

The struct keyword enables the computer to distinguish between basic variable types, such as int, and custom variable types, such as Coordinate.

However, adding the struct keyword every time is a bit troublesome. So the smart C language developers devised the typedef keyword.

Of course, most of mankind’s inventions are for the sake of laziness. Who wouldn’t want to be more efficient?

The typedef keyword


A typedef is a C keyword. It is a combination of type and define, which, as the name suggests, means “type definition”.

When you hear “type definition,” it seems hard to understand. However, the function of typedefs is not as mysterious as its meaning.

Go back to the.h header that defined Coordinate. Let’s add a command that starts with a typedef to create an alias for your Coordinate structure.

What is an alias?

For example, there is a person, his real name is Wang Xiaoming, alias can be Xiao Ming, Ming Ming, etc., but they all represent that person.

A reference mechanism similar to C++.

So an operation on an alias is an operation on the original object.

For example, when you misbehave in class as a child, when the teacher calls the roll call, he calls your nickname or your real name, both of which are called you, you have to go to the punishment station.

So let’s just start with the definition of Coordinate structure by saying, well, it’s always nice to come at the end, but you can do it at the front:

typedef struct Coordinate Coordinate;

struct Coordinate
{
    int x;
    int y;
};
Copy the code

As you can see, we have added a new line:

typedef struct Coordinate Coordinate;
Copy the code

To better understand what this command does, let’s break it down into three parts:

  1. typedef: indicates that we are going to create an alias.
  2. struct Coordinate: This is the structure for which we will alias it.
  3. Coordinate: This is the alias to create.

So, what this command means is, “Coordinate is struct Coordinate from now on.”

And by doing that, we don’t have to put the struct keyword every time we create a variable for a new Coordinate structure.

Therefore, our.c file can be rewritten as:

int main(int argc, char *argv[]) { Coordinate point; // Because of the typedef, the computer knows that Coordinate here is actually struct Coordinatereturn 0;
}
Copy the code

Of course, you don’t have to call it Coordinate, you could call it Coor, maybe even less confusing. Such as:

typedef struct Coordinate Coor; struct Coordinate { int x; int y; }; Coor coor; // Create a structure variableCopy the code

It is recommended that you add a typdedef command after defining a struct type, so that you do not have to write the struct keyword at the beginning of your code every time you create a variable of this type.

A lot of programmers do that. Because a good programmer is a programmer who knows how to be lazy, which is different from a lazy programmer. We want to make the code “write less, do more”.

Of course, the above code block could be abbreviated as:

Typedef struct struct name {// struct contents} alias;Copy the code

So the Coordinate block above can be abbreviated as:

typedef struct Coordinate
{
    int x;
    int y;
} Coordinate;
Copy the code

Note: Later in our sample code, sometimes for example

Person player1;
Copy the code

In this form, it is assumed that we have used a typedef before:

typedef struct Person Person;
Copy the code

The struct keyword at the beginning can be omitted, instead of writing:

struct Person player1;
Copy the code

Modify struct member variables


Now that we’ve created our point variable, which is Coordinate, hopefully you’re not confused, we can change the value of its members.

How do we access the two members of point x and y? As follows:

int main(int argc, char *argv[])
{
    Coordinate point;

    point.x = 10;
    point.y = 20;

    return 0;
}
Copy the code

Thus, we have successfully modified the values of the two members of point to x coordinates of 10 and y coordinates of 20.

So our point is going to be at the point 10, 20.

So, in order to access a member of a structure, we can do this:

Structure instance name. Member nameCopy the code

The middle point (.) Indicates a subordinate relationship.

If you have a background in object-oriented programming, you might think: this is too similar to “classes and objects”.

Yes, we can actually use structs to “simulate” classes.

If we use the Person structure we created earlier as an example:

int main(int argc, char *argv[]) { Person user; // user indicates a user.printf("What's your last name? ");
    scanf("%s", user.lastName);

    printf("What's your name? ");
    scanf("%s", user.firstName);

    printf("So your name is %s% S, disrespectful disrespectful n", user.lastName, user.firstName);

    return 0;
}
Copy the code

Run output:

What's your last name? Wang, what is your name? Xiaoming turns out your name is Wang XiaomingCopy the code

We pass user.lastname to scanf so that the value entered by the user directly changes the lastName member of user. We do the same for user.firstname.

Of course, we can also add the assignment to address, age, and boy.

Of course, you might say, “I don’t know about structs, but I could have done the same thing with two separate string variables lastName and firstName.”

Yes, but the nice thing about using a structure is that we can create variables for that structure, encapsulating a lot of related data into a whole, rather than defining it pieceily.

For example, after the Person structure is defined, all variables created using Person automatically contain lastName, firstName, address, age and boy, which is very convenient.

For example, we could create:

Person player1, player2; Typedefs (typedef struct Person;Copy the code

Both player1 and Player2 contain lastName, firstName, Address, age, and boy.

We can also be lazy: create an array of structs. Such as:

Person players[2];
Copy the code

This way, we can easily access variables in players[1], for example:

players[1].lastName = "xiaoming";
Copy the code

The advantage of using struct arrays is that you can easily use loops, and so on.

Test yourself


Create a structure called CoderHub and put the variables you want to create in the definition. We then create an array of these structures, assign values to the variables in a loop, and print out information about the variables in a loop.

Initialization of a structure

In previous lessons, we suggested that basic variables, arrays, and Pointers be initialized at creation time. Structures are no exception.

One of the great benefits of initialization is to avoid storing “arbitrary data” in this variable.

In fact, when a variable is created, if it is not initialized, it will take the value that was stored in memory at that time, so this value is very random.

Let’s recall how different variables should be initialized:

  • Base variables (int, double, char, etc.) : initialize to 0.

  • Pointer: initialized to NULL. In fact, NULL is in the stdlib.h library header and is a constant defined with the #define preprocessing command. It usually has a value of 0. Although 0, it can be defined in many ways, such as:

#define NULL 0
#define NULL 0L
#define NULL ((void *) 0)
Copy the code

But let’s just use NULL every time, just to make it clear that this is a pointer variable, not a regular variable.

  • Array: Initializes each member variable to 0.

So how do we initialize our friend structure?

Initialization of a structure is also very simple, similar to initialization of an array. We can define it as follows:

Coordinate point = {0, 0};
Copy the code

Thus, we initialize both point.x and point.y to 0 in order.

For structures like Person that have char arrays and ints, we can initialize char arrays to “” (the double quotes are empty).

We can initialize a string like this, the second part in C language exploration trip | lesson 4: string Forgot to mention that lesson. But I don’t think it’s too late to mention it.

So we can initialize our Person structure variable like this:

Person player = {"".""."", 0, 0};
Copy the code

However, we can also initialize a structure variable by creating a function, such as initializeStruct, that initializes each structure passed to it, which is much more convenient, especially if there are many variables in the structure.

As we learned in the pointer chapter, if we pass a normal variable to a function, then because C passes function arguments by value, it will make a copy of the function arguments passed to it, so that the changes inside the function are actually the copy, and the actual arguments are not changed.

In order for the argument to actually be modified, we need to use a pointer, which is the address to which the variable is passed.

The same is true for structures. So, let’s learn how to use structure Pointers. This is gonna get tough. You ready?

4. Structure pointer


Struct pointer creation is no different from normal pointer variable creation. Such as:

Coordinate *point = NULL;
Copy the code

This code creates a pointer variable to a Coordinate structure called Point (Coordinate is a structure we defined above to represent coordinates).

One more reminder:

It is generally recommended to write:

Coordinate *point = NULL; // The asterisk is next to the pointer variable nameCopy the code

Instead of writing:

Coordinate* point = NULL; // The asterisk is next to the structure name.Copy the code

The first is recommended for pointer creation.

The second way, if you create several pointer variables on a single line, it’s easy to forget to add an asterisk (*) before the second variable. For example, it is easy to write:

Coordinate* point1 = NULL, point2 = NULL; // Compile errorCopy the code

But that would get compiled wrong, because point2 is actually a Coordinate structure variable, not a Coordinate structure pointer variable!

So we suggest writing:

Coordinate *point1 = NULL, *point2 = NULL;
Copy the code

In previous lessons, we suggested the same thing for pointer variables of the underlying type:

int *number1 = NULL, *number2 = NULL;
Copy the code

Pointers to ints, in particular, are hard to detect.

int* number1 = NULL, number2 = NULL;
Copy the code

The compiler does not report errors. Since the value of NULL is 0, we can assign an int to number2 (note that number2 is not an int).

It’s always good to look back (” Heartbreak is inevitable…” ).

Struct as function argument


Here, we will learn how to pass a pointer to a structure to a function (as an argument) so that the structure can actually be modified inside the function.

Let’s look at an example:

#include <stdio.h>typedef struct Coordinate { int x; // int y; } Coordinate; void initializeCoordinate(Coordinate *point); Int main(int argc, char *argv[]) {Coordinate myPoint; initializeCoordinate(&myPoint); // The function takes the address of the myPoint variablereturn0; } // Initialize the structure variable void initializeCoordinate(Coordinate *point) {// Initialize the structure code}Copy the code

In the initializeCoordinate function above, we will place the code that initializes the structure’s member variables.

Let’s look at this code in order:

  • So first of all, we define a structure, called Coordinate, that contains two variables, x and y.

  • We’ve created a variable for the Coordinate structure in our main function called myPoint.

  • We pass the address of myPoint to the function initializeCoordinate.

  • Next, we add the code to initialize the x and Y variables in the initializeCoordinate function:

void initializeCoordinate(Coordinate *point){
    *point.x = 0;
    *point.y = 0;
}
Copy the code

The * before point is essential. Because the argument passed to the function is a pointer to a structure, we need to use the dereference symbol: the asterisk (*) to get the structure.

But does the serious reader see the error in the above function?

We want to dereference the point structure pointer with *, get the structure pointer, and then use. Theta takes the variables x and y. But if you write it like this, it actually works like this:

*(point.x) = 0;
*(point.y) = 0;
Copy the code

Because the. Sign has a higher priority than the asterisk.

If you’re interested, you can take a look at the precedence of C operators, but as we talked about in the previous lecture, what do you do if you can’t remember? I’ll just put the parentheses.

The above code will not compile because the structure pointer point does not have members called x and y, and we cannot use. Number to get to what value.

Therefore, we need to modify it. Change it to the following:

void initializeCoordinate(Coordinate *point) {
    (*point).x = 0;
    (*point).y = 0;
}
Copy the code

That’s right. The effect of operator precedence is removed by parentheses.

But, as I said before, programmers are lazy people.

If you have to do this every time you want to fetch a member variable from a structure, use asterisks, parentheses, and. Number. It makes Denis Ritchie drunk. He would not allow this to happen, so he defined a new symbol: -> (an arrow. Yes, it is.

Usage:

point->x = 0;
Copy the code

Equivalent to:

(*point).x = 0;
Copy the code

Is it a lot easier?

Remember: this symbol can only be used on Pointers.

Therefore, our function can be rewritten as:

void initializeCoordinate(Coordinate *point) {
    point->x = 0;
    point->y = 0;
}
Copy the code

We could also write this in main:

int main(int argc, char *argv[]) { Coordinate myPoint; Coordinate *myPointPointer = &myPoint; myPoint.x = 10; MyPointPointer ->y = 15; myPointPointer->y = 15; // Change the y value of myPoint with a struct pointerreturn 0;
}
Copy the code

Structure is a very useful and important concept in C language, I hope you have a good grasp of it!

Of course, there are many details of knowledge, we will go to see their classic C language teaching materials, such as “C programming language” (not Tan Haoqiang that “C language programming”! But classics written by C writers), C and Pointers, C Expert Programming, Deep Anatomy of C, C Pitfalls and Pitfalls, etc.

5. union


Union means “union” and is a key word in THE C language. Some books also translate it as “common body”.

We can write an example of a union.

union CoderHub
{
    char character;
    int memberNumber;
    double rate;
};
Copy the code

At first glance, it looks like a struct. But is there really no difference?

Suppose we use C’s sizeof keyword. “) to test the size of the union (size refers to the number of bytes in memory, one byte equals eight bits) :

#include <stdio.h>typedef union CoderHub { char character; // The size is 1 byte int memberNumber; // The size is 4 bytes. // Size is 8 bytes} CoderHub; int main(int argc, char *argv[]){ CoderHub coderHub;printf("The size of this union is %lu bytes \n", sizeof(coderHub));
 
    return 0;
}
Copy the code

Run the program, output:

The size of this union is 8 bytesCopy the code

Suppose we also do a test on the structure and compare:

#include <stdio.h>typedef struct CoderHub { char character; // The size is 1 byte int memberNumber; // The size is 4 bytes. // Size is 8 bytes} CoderHub; int main(int argc, char *argv[]){ CoderHub coderHub;printf("Struct size is %lu bytes \n", sizeof(coderHub));

    return 0;
}
Copy the code

Run the program, output:

The size of this struct is 16 bytesCopy the code

Why is the size of our custom union 8 bytes and struct 16 bytes?

This brings us to the difference between a union and a struct.

The size of a struct is the sum of the sizes of all the variables in it.

But you say, “No, 1 + 4 + 8 = 13, why sizeof(coderHub) is 16?”

Good question! This is a bit more complicated and involves memory alignment, but we’ll get to that later. If you must know, memory alignment causes the first char variable to align the space of the second int variable to become 4, so that 4 + 4 + 8 = 16.

Interested readers should refer to the deep Anatomy of C for an explanation.

In memory-constrained environments such as embedded programming, memory alignment needs to be considered to save space.

The sizeof union is equal to the sizeof the variable with the largest (sizeof() gets the largest value). Therefore, the union can only store one variable at a time. The size of the union is equal to that of the largest variable, so as to ensure that it can accommodate any member.

Union is good for using many sets of variables of the same type, but only one of them is needed at a time, which saves space.

6. enum


After looking at struct and union, we’ll finally look at a very common custom variable type: enum.

Enum is an abbreviation of enumeration and a C keyword.

Enumeration is a special custom variable type. When I learned C, I didn’t understand it at first. But used well, it’s very practical.

We learned earlier that a structure contains multiple member variables that can be of different types (” member “is a bit object-oriented :P).

But enums are a set of optional values. So you can only take one of these values at a time, which sounds a little bit like union. But there is a difference between enum and union.

Let’s take an example to see the difference:

typedef enum Shape Shape; Enum Shape // Shape {THIN, // THIN indicates THIN MEDIUM, // MEDIUM indicates MEDIUM FAT // FAT indicates FAT};Copy the code

So, we define an enum variable called Shape. There are three values: THIN, MEDIUM and FAT. It doesn’t have to be capitalized, it’s just a habit.

So how do we create enum variables? As follows:

Shape shape = MEDIUM;
Copy the code

Shape can also be changed to THIN or FAT in the program.

Assigns a value to a member of an enum


Do you see the difference between enum and union and struct? Yes, in the definition of enum, each member has no variable type (int, char, double, etc.)!

That’s weird. Remember why enum members are used in uppercase?

Yes, because each member of an enum is not a variable, but a constant! But there are some differences between enum and constant definition and #define:

Like the code above:

typedef enum Shape
{
    THIN,
    MEDIUM,
    FAT
} Shape;
Copy the code

The compiler will automatically bind a constant value to each of the members. Let’s write a program to test this:

#include <stdio.h>

typedef enum Shape Shape;

enum Shape
{
    THIN,
    MEDIUM,
    FAT
};

int main(int argc, char *argv[]) {
    Shape shape = THIN;
    printf("THIN = %d\n", shape);
 
    shape = MEDIUM;
    printf("MEDIUM = %d\n", shape);
 
    shape = FAT;
    printf("FAT = %d\n", shape);
 
    return 0;
}
Copy the code

Run the program, output:

THIN = 0
MEDIUM = 1
FAT = 2
Copy the code

See? The compiler automatically assigns 0,1, and 2 to these three members. If no value is specified for enum members, their value starts at 0 and increments by 1.

We can also define the values of enum members ourselves without having the compiler assign them to us automatically each time.

We can write this:

typedef enum Shape
{
    THIN = 40,
    MEDIUM = 60,
    FAT = 90
} Shape;
Copy the code

In this way, we define values for each member ourselves.

We can also ask the compiler to assign us some values automatically and define some values ourselves, for example:

typedef enum Shape
{
    THIN,
    MEDIUM,
    FAT = 90
} Shape;
Copy the code

Above, we did not assign THIN and MEDIUM, so the compiler assigns them 0 and 1.

And FAT, because we’ve already specified 90, FAT is 90.

Enum and #define


Does enum look like a constant defined with #define?

Actually, it’s a little different:

  • #define macro constants (or preprocessed constants) are simply replaced during the preprocessing phase, while enumerated constants are determined at compile time.

  • In a compiler, you can debug enumerated constants, but not macro constants.

  • Enumerations can define a large number of related constants at a time, whereas the #define macro can define only one at a time.

7. To summarize


  1. Struct (struct) is a custom variable type that we can customize completely, as opposed to basic int, double, etc. The use of constructs makes our C programs more flexible and can do more things.

  2. A structure contains member variables, usually of the underlying type, such as int, double, etc., but it can also have pointer variables, arrays, and even other structure variables.

  3. To access a structure’s member variables, we can use the normal structure access method: structure variable names. Member variable name (connected by a dot).

  4. We can also access a structure’s member variables in a very simple structure pointer way: structure pointer variable name -> member variable name (connected by an arrow).

  5. The biggest difference between a union (” shared body “, or “union”) and a struct is that the size of a union is the size of the member variable with the largest capacity, whereas the size of a structure is the sum of each member variable (also considering memory alignment). The union can only take one of these variables at a time.

  6. An enum can only take a value from one member at a time, similar to a union. But enum members are constants, not variables. And if enum members do not specify a value, the compiler assigns values to each variable in ascending order, starting at 0.

8. Part 2 Lesson 7 Notice


That’s all for today’s lesson, come on!

Next lesson: the second part C language exploration trip | lesson 7: file reading and writing


My name is Xie Enming, the operator of the public account “Programmer Union” (wechat id: CoderHub), the moOCs elite lecturer Oscar, and a lifelong learner. Love life, like swimming, a little cooking. Life motto: “Run straight for the pole”