Enums and Structs in C

Creating a New Data Type

We have used many types of variables, such as integers, characters, floating-point numbers, and strings. However, the types of variables we can use are not limited to these. We can create our own variable types. For example, by creating a new type called boolean, we can restrict its possible values to true and false; there is no third option. Or, we can create a variable type called seasons and limit its possible values to months. Enums are used for such operations. The term "enum" comes from "enumerator," meaning "counter" or "enumerator."

Life can be expressed with numbers. The most obvious examples of this can be seen while programming. For example, the letter A corresponds to the number 65 in the ASCII table, the capital letter B corresponds to 66, and so on. Computers don't understand signs, symbols, or characters; they only understand numbers. This is what enums serve. For example, if we choose 1 to represent true and 0 to represent false, we have created a new variable type. A computer doesn’t know what true or false is; it only knows 0 and 1. However, for the benefit of humans, programs with greater readability emerge. Let's create the boolean variable type:

#include<stdio.h>

int main(void) {
    // Defining the variable type
    enum boolean {
        false = 0,
        true = 1
    };

    // Now we define a variable called 'dogru_mu'
    enum boolean dogru_mu;

    // Assigning a value to the 'dogru_mu' variable
    // and checking it in the next line.
    dogru_mu = true;
    if (dogru_mu == true)
        printf("True\n");
    return 0;
}

Previously, there was no boolean data type. Now, we’ve created a new variable type that can have two different values: true and false. We used 0 to represent false and 1 to represent true. We didn’t need to specify the values for false and true explicitly; when defining the boolean type, we could just write false or true without using 0 and 1. When your program is compiled, values are assigned to undefined entries in sequence. You don't necessarily have to create an equality statement. Let's create a variable type ana_renkler that can take three main colors (Red, Yellow, Blue):

#include<stdio.h>

int main(void) {
    // Defining the variable type
    enum ana_renkler {
        Kirmizi,
        Mavi,
        Sari
    };

    // Defining the variable
    enum ana_renkler piksel;

    // Setting the variable value to Blue.
    // We can also input Yellow and Red.
    piksel = Mavi;

    // Comparing the variable value.
    if (piksel == Kirmizi)
        printf("Red pixel\n");
    else if (piksel == Mavi)
        printf("Blue pixel\n");
    else
        printf("Yellow pixel\n");

    return 0;
}

We don't know the numeric value of Kirmizi, Mavi, or Sari; they most likely start from 1 and continue sequentially up to 3. However, this doesn’t prevent us from using them just like in the previous example.

When defining variables of the new types we've created, you might have noticed that we need to write enum every time. Instead of repeating this, there are two alternative ways. The first one is to define the variable at the same time as creating the new data type:

#include<stdio.h>

int main(void) {
    // Creating the new variable type
    // and defining a variable from this type at the same time.
    enum boolean {
        false = 0,
        true = 1
    } dogru_mu;

    dogru_mu = true;
    if (dogru_mu == true)
        printf("True\n");
    return 0;
}

The method above allows you to define a variable from the new data type right as you create the type, saving you from writing enum repeatedly. The other way to avoid writing enum each time is using typedef. Here's how it works:

typedef old_data_type new_data_type_name;

With typedef, you can assign a new name to any existing data type. For example, with typedef int tam_sayi;, you can use tam_sayi instead of int. Let's see the application of typedef for enums:

#include<stdio.h>

int main(void) {
    // Creating the new data type
    // and also defining a variable of that type.
    enum boolean {
        false = 0,
        true = 1
    };
    
    // Using typedef to create a new name for the boolean type.
    typedef enum boolean bool;

    bool dogru_mu;

    dogru_mu = true;
    if (dogru_mu == true)
        printf("True\n");
    return 0;
}

Special Data Types and Functions

The concept of enums can be expanded. For example, if you define the enum globally, you can use it as a function parameter. Let's create a simple function that will print information about the variable it receives:

#include<stdio.h>
// Defining a list of months.
enum ay_listesi {
    ocak = 1, subat, mart, nisan,
    mayis, haziran, temmuz, agustos,
    eylul, ekim, kasim, aralik
};

// Defining a type alias for convenience.
typedef enum ay_listesi aylar;

void ay_ismini_yazdir(aylar);
int main(void) {
    // Creating a variable of type 'aylar' and assigning 'kasim'.
    aylar bu_ay = kasim;

    // Printing the month number.
    printf("%d. month: ", bu_ay);

    // Calling the function to print the month name.
    ay_ismini_yazdir(bu_ay);
    return 0;
}

// Printing the month name based on the passed 'aylar' type variable.
void ay_ismini_yazdir(aylar ay_adi) {
    switch(ay_adi) {
        case ocak: printf("January\n"); break;
        case subat: printf("February\n"); break;
        case mart: printf("March\n"); break;
        case nisan: printf("April\n"); break;
        case mayis: printf("May\n"); break;
        case haziran: printf("June\n"); break;
        case temmuz: printf("July\n"); break;
        case agustos: printf("August\n"); break;
        case eylul: printf("September\n"); break;
        case ekim: printf("October\n"); break;
        case kasim: printf("November\n"); break;
        case aralik: printf("December\n"); break;
    }
}

As you can see, you can pass custom data types created with enum to functions. Using enum to create a new data type simplifies many tasks, especially when you need to group or categorize data. Below are some examples of enum data types:

enum marital_status { single, married, widowed };
enum marital_status ayse = single;

enum education_level { primary, secondary, high_school, university, master };
enum education_level student;

enum gender { male, female };
enum gender person;

Structures (Structures)

Structures group various data types like integers, characters, etc., under a single umbrella. Inside these structures, you can have as many elements as you need, either of the same or different types. Structures are important for object-oriented programming (OOP) languages. If you are planning to work with modern languages like Java or C#, you should pay more attention to this topic.

Let’s create a program that asks for the birthday information and prints it out:

#include<stdio.h>

int main(void) {
    struct {
        int year;
        int month;
        int day;
    } birth_date;

    printf("Enter your birth date in DD-MM-YYYY format: ");
    scanf("%d-%d-%d", &birth_date.day, &birth_date.month, &birth_date.year);

    printf("Your birth date is: ");
    printf("%d/%d/%d\n", birth_date.day, birth_date.month, birth_date.year);
    return 0;
}

If we hadn't used structures, we would have had to declare three separate variables to store day, month, and year. Grouping them in a structure is more efficient and makes it easier to keep track of things, while reducing the chance of errors. Let's create a program that asks for the birthdays of you and your two siblings:

#include<stdio.h>

int main(void) {
    struct {
        int year;
        int month;
        int day;
    } you, sister, brother;

    printf("Enter your birth date: ");
    scanf("%d-%d-%d", &you.day, &you.month, &you.year);

    printf("Enter your sister's birth date: ");
    scanf("%d-%d-%d", &sister.day, &sister.month, &sister.year);

    printf("Enter your brother's birth date: ");
    scanf("%d-%d-%d", &brother.day, &brother.month, &brother.year);

    return 0;
}


Yorumlar

Bu blogdaki popüler yayınlar

Arithmetic Operators and Expressions

Functions

What is a Variable? How is it Defined?