Learn C With Me #08 — Structs | Live

In Lesson 8 of Learn C With Me, we dive into structs in C — a way to group multiple pieces of related data, potentially of different types, into a single user-defined type.

Using Beej’s Guide to C as our study material, we start with the basics of declaring a struct and gradually work our way through initialization, pointers to structs, passing structs to functions, copying them, and comparing them.

We cover:

  • What a struct is and why it is useful
  • Creating a user-defined type with struct
  • Declaring variables of a struct type
  • Accessing fields with the dot operator (.)
  • Struct initializers
  • Initializing fields in declaration order
  • Designated initializers
  • Why designated initializers are safer when field order changes
  • What happens to fields that aren’t explicitly initialized
  • Passing structs to functions
  • Passing a complete struct versus passing a pointer to a struct
  • Why passing a pointer can avoid unnecessary copying
  • Modifying the original struct from inside a function
  • Function declarations and why the compiler needs to know about a function before it is called
  • Dereferencing a pointer to a struct
  • The arrow operator (->)
  • Why ptr->field is equivalent to (*ptr).field
  • When to use . and when to use ->
  • Copying structs
  • Returning structs from functions
  • The difference between a shallow copy and a deep copy
  • Why copying a struct also copies any pointer values it contains
  • Comparing structs
  • Why the safest approach is to compare individual fields one by one

One of the most useful parts of this lesson is seeing why pointers to structs are so common in C. Passing an entire struct to a function makes a copy, while passing a pointer allows the function to work with the original object and can also be much cheaper when the struct is large.

We also meet the arrow operator for the first time. Instead of writing something like (*car).price, C lets us write the much cleaner car->price. The two forms mean the same thing — the arrow is simply convenient syntax for accessing a field through a pointer to a struct.

Later, we look at copying structs and discover that a normal assignment performs a shallow copy. The fields themselves are copied, including pointer values, but the data those pointers refer to is not duplicated automatically. C doesn’t provide built-in deep-copy behavior for structs.

Finally, we look at comparing structs and learn that there isn’t a general-purpose struct equality operator in C. The safe approach is to compare the relevant fields individually, rather than assuming the raw memory representation will always be suitable for comparison.

As usual, we experiment with the examples, hit compiler errors, stop when something doesn’t make sense, work through it with the live chat, and try to understand what C is actually doing under the hood rather than just memorizing syntax.

Homework #08 — Structs

For this week’s homework, we’re going to build a small RPG character program using C structs.

The goal is to practice what we learned in Chapter 8 — Structs of Beej’s Guide to C Programming while only using concepts we’ve covered in the course so far.

Create a character type

Define the following struct:

struct character {
    char *name;
    int health;
    int attack;
    int level;
};

This will represent a character in our tiny game.


1. Create two characters

Inside main(), create two variables of type:

struct character

Give them different names and stats.

At least one of them should use a designated initializer:

struct character hero = {
    .name = "Conan",
    .health = 100,
    .attack = 20,
    .level = 1
};

Create the second character yourself.


2. Print a character

Write this function:

void print_character(struct character c);

It should print all fields of the character.

For example:

Name: Conan
Health: 100
Attack: 20
Level: 1

Notice that we’re passing the struct itself to the function.


3. Take damage

Write:

void take_damage(struct character *c, int damage);

The function should subtract damage from the character’s health.

Health must never go below 0.

For example:

take_damage(&hero, 30);

If the character had 100 health before the function call, it should have 70 afterwards.

Inside this function, use the arrow operator:

c->health

4. Level up

Write:

void level_up(struct character *c);

When a character levels up:

level  +1
health +20
attack +5

The function must modify the original character through the pointer.

Again, use -> to access the fields.


5. Find the stronger character

Write:

struct character stronger_character(struct character a,
                                    struct character b);

Compare the attack values of the two characters.

Return the character with the higher attack value.

Example:

struct character strongest = stronger_character(hero, enemy);

Then print the returned character with:

print_character(strongest);

This exercises passing structs to functions and returning a struct from a function.


6. Copy a struct

Create another character:

struct character copy;

Then copy one of your existing characters:

copy = hero;

Change only the health of the copy:

copy.health = 10;

Then print both:

print_character(hero);
print_character(copy);

Before running the program, try to predict what will happen.

Does changing:

copy.health

also change:

hero.health

Why or why not?


7. Compare two characters

Write:

int same_stats(struct character a, struct character b);

Return 1 if both characters have the same:

health
attack
level

Otherwise return 0.

Compare the fields one at a time.

For this exercise, ignore the name field.

Example:

if (same_stats(hero, enemy)) {
    printf("The characters have the same stats!\n");
}

Expected concepts

Your finished program should give you practice with:

struct character

struct initialization:

struct character hero = {
    .name = "Conan",
    .health = 100,
    .attack = 20,
    .level = 1
};

accessing a struct directly:

hero.health

accessing a struct through a pointer:

c->health

passing a struct:

print_character(hero);

passing a pointer to a struct:

take_damage(&hero, 30);

copying a struct:

copy = hero;

and returning a struct from a function:

struct character strongest = stronger_character(hero, enemy);

Rules

Use only concepts we’ve covered so far in Learn C With Me, up to and including Chapter 8 — Structs.

You do not need:

  • file input/output
  • dynamic memory allocation
  • typedef
  • enum
  • any libraries or techniques we haven’t covered yet

Keep the program small.

The goal isn’t to make a game. The goal is to become comfortable with structs, particularly the difference between:

character.health

and:

character_pointer->health

and the difference between passing or copying a struct and modifying the original struct through a pointer.

As always: experiment.

Try to predict what the program will do before compiling it. Change values. Break things. Read the compiler errors.

If you get stuck, post your code and compiler output in the thread.

Kind of got destracted on step 3 and made mini game instead XD

code of random combat
#include <stdio.h>
#include <string.h>

struct character {
    char *name;
    int health;
    int attack;
    int level;
};

void print_character(struct character c) {
    printf("Characters Info\n");
    printf("Name:%s\nHealth:%d\nAttack:%d\nLevel:%d\n", c.name, c.health, c.attack, c.level);
};

void take_damage(struct character *c, int dmg) {
    printf("%s takes a hit for %d\n", (*c).name, dmg);
    c->health = c->health - dmg;
    if (c->health <= 0) {
        printf("%s died!\n", c->name);
        c->health = 0;
    } else {
        printf("%s has %d health remaining after the attack and survived the maiming.\n", c->name, c->health);
        //made you look XD
    }
    printf("\n");
};

int is_alive(struct character c) {
    return c.health > 0;
}

int main(void) {
    struct character hero = {
        .name = "Conan",
        .health = 100,
        .attack = 20,
        .level = 1
    };
    struct character boss = {
        .name = "ice",
        .health = 300,
        .attack = 30,
        .level = 3
    };

    printf("THE HERO \n");
    print_character(hero);
    
    printf("\nAND THE BOSS\n");
    print_character(boss);

    printf("\nTHERE GASE MEAT!\n");

    printf(" = BATTLE STARTS = \n");

    for (int round = 1; is_alive(hero) && is_alive(boss); round++) {
        printf("\n= Round %d =\n", round);
        
        printf("%s attacks!\n", hero.name);
        take_damage(&boss, hero.attack);
        
        if (!is_alive(boss)) {
            break;
        }

        printf("%s attacks!\n", boss.name);
        take_damage(&hero, boss.attack);

        if (!is_alive(hero)) {
            break;
        }
    }
    printf("\n = BATTLE ENDED = \n");
    return 0;
};
picture of result

well gues i failed the home work but allways good to remember how to change pointers value

edit: fixed hidden code formating
edit2: wtf how do i not get it broken?

edit3: no clue how just added code in white text area and deleted rest…

1 Like