LecLabs 2.4 Structures and Makefiles


Intro to Programming Languages.

[ ← Module List ]

This lab will help you understand structs and using Makefiles to build multi-file programs.


Challenges

Lecture video on struct into

Creating a struct data type and loading it with data.

Lecture video on string to struct

Creating a struct data type and loading it with data from a delimiated string.

Lecture video on array of struct

Objective

The program reads a file containing song information, stores the details in dynamically allocated structures, and prints the song details of every 5 songs starting from first song. The song information includes the genre, artist, and title of each song.

CTRL-Click Here for Follow Along Video

Requirements

  1. File Format
    • Each song entry is enclosed between +++ BEGIN and --- END.
    • Within each song entry, the information is formatted as key-value pairs:
      • genre:
      • artist:
      • title:
    • The keys (genre, artist, title) are followed by a colon and a space, and the values are strings.
    • The file contains an unknown number of songs
      
      +++ BEGIN
          genre:Dance
          artist:Ariana Grande
          title:7 rings
      --- END
      +++ BEGIN
          genre:Dance
          artist:Halsey
          title:Without Me
      --- END
          
  2. Song Struct
    • Song struct holds a single song's information
    • It is made up of 3 c-strings of varying lengths
      
      #define MAX_GENRE_LENGTH 30
      #define MAX_ARTIST_LENGTH 50
      #define MAX_TITLE_LENGTH 100
          
      
      typedef struct Song {
          char genre[MAX_GENRE_LENGTH];
          char artist[MAX_ARTIST_LENGTH];
          char title[MAX_TITLE_LENGTH];
      } Song;
          
  3. Remove unwanted characters from end of line: remove_char_from_end(char * str, char char_to_remove)
    • Check if provided c-string ends withe char_to_remove
    • If so, remove the last character from the c-string
  4. Read and Load Songs: Song* read_songs_from_file(const char *filename, int *count)
    • Open file using provided filename
    • Allocate initial memory for songs, capacity = 10, don't forget sizeof()
      • Allocated memory will put each Song right next to each other in memory
      • The allocated heap will look like this:
        
          Memory Address      | Data (Song Struct)
          --------------------|---------------------------------------------------------
          0x1000              | Song 1
          0x1000 - 0x101D     |   genre (30 bytes)
          0x101E - 0x1049     |   artist (50 bytes)
          0x104A - 0x10D3     |   title (100 bytes)
          --------------------|---------------------------------------------------------
          0x10D4              | Song 2
          0x10D4 - 0x10F1     |   genre (30 bytes)
          0x10F2 - 0x111D     |   artist (50 bytes)
          0x111E - 0x11A7     |   title (100 bytes)
          --------------------|---------------------------------------------------------
          0x11A8              | Song 3
          0x11A8 - 0x11C5     |   genre (30 bytes)
          0x11C6 - 0x11F1     |   artist (50 bytes)
          0x11F2 - 0x127B     |   title (100 bytes)
          --------------------|---------------------------------------------------------
              
    • While getline (using getline, which auto allocates on the heap any size of input)
      • If +++BEGIN check
        • If song_index is >= capacity, then allocate more space for list
        • Use memset to set all the strings in the struct to 0 for the current array location
        • If unsure about memset, then watch this video on memset

      • Clean up line using remove_char_from_end
      • If genre: artist: title: in line then
        • Copy the value to the structure using strncpy
        • Add null terminator to the end of the field (Use MAX value) b/c strncpy does not guarantee null termination when value is longer than genre's max size
    • Set count pointer equal to song_index + 1
    • Close file and free line
  5. The main function
    • Check that file argument was provided otherwise print usage message and exit
    • Call read_songs_from_file
    • Print every fifth song from the heap-based array
    • Free songs

Steps to Complete

  • Implement the requirements
  • Compile and test the program
  • Run /challenge/tester
  • Get flag

Lecture video on enum

Lecture video on C preprocessor

Use the C preprocessor with the compile command to make 2 different versions of the same program.

Lecture video on Header files

Lecture video on Makefile

Update and use a makefile to compile a multi-file program.

  1. Modify printer.h and add a header guard
  2. Add the missing lines to the Makefile by following the instructions in the comments.

Objective

Change the song list program from the last lab to use multiple files and a Makefile

CTRL-Click Here for Follow Along Video

  1. Modify songs.h
    • Add a header guard for songs.h
      
          #ifndef SONGS_H
          #define SONGS_H
          ...
          #endif
          
    • Move #define statements to songs.h
    • Move Song structure definition to songs.h
    • Add declaration for read_songs_from_file because it is called from main.c
  2. Modify utils.h
    • Add header guard for utils.h
    • Add declaration for remove_char_from_end
  3. Modify utils.c
    • Add include "utils.h" to the top of utils.c
    • Move definition of remove_char_from_end from main.c to utils.c
  4. Modify songs.h
    • Add header guards
    • Add a declaration for read_songs_from_file get the definition from main.c
  5. Modify songs.c
    • Add include "songs.h" to the top of the file
    • Move read_songs_from_file's definition (the name and code) from main.c to songs.c
  6. Modify main.c
    • Remove #define MAX_ statements, Song structure definition, remove_char_from_end and read_songs_from_file
    • Add include "songs.h"
  7. Modify Makefile
    • Add songs.o and utils.o to OBJS
    • Add dependency information for songs.c and utils.c
      • View examples in Makefile
      • Dependency information can be determined using gcc -MM songs.c

Steps to Complete

  • Implement the requirements
  • Compile and test the program
  • Run /challenge/tester
  • Get flag

Objective

Change the song list program from lab 2.4.1.5 to request the filter criteria and then filter the songs printed out

Click Here for Follow Along Video

Requirements

  1. Modify main.c
    • Prompt the user to enter filter criteria
    • Use fgets to get the user's filter criteria
    • Change the for loop the loop through all items
    • In the song printing for loop
      • Create 3 new character pointer variables for a lowercase version of genre, artist, and title
      • Compare the strings against the user's filter_criteria, if a match is found then print that record
      • If the criteria exists anywhere in the fileds, then print the song
      • Do not Free the new pointers
  2. Improve Filter to be Case Insensitive
    • Declare a function char *to_lowercase(const char *str) in utils.h
    • Define to_lowercase in utils.c (returns lowercase version of c-string)
      • Allocate a new string that's 1 character larger than the length of the incoming string
      • Use tolower on each character while copying to new string
      • Append a null terminator to the end of the output string
      • Return the lowercase string
  3. Freeing the pointers is now required in the for loop: free genre, artist, and title

Steps to Complete

  • Implement the requirements
  • Compile and test the program
  • Run /challenge/tester
  • Get flag

30-Day Scoreboard:

This scoreboard reflects solves for challenges in this module after the module launched in this dojo.

Rank Hacker Badges Score