CS 43 — Lab 0: C Warm Up

Due: Thursday, January 27 @ 11:59 PM

To incentivize completion, students who successfully complete all aspects of this lab will receive one additional "late day" for use on labs later in the semester.

For these exercises, you may either work alone or work with up to one partner. If you work with a partner: add a comment to the top of your .c files indicating who you worked with. You must both submit your solutions to GitHub.

Overview

This lab is a gentle refresher for C programming. It has two exercises:

  1. Haiku: a C string warm up. Completing this exercise will help you construct the messages you’ll need to send in lab 1.

  2. Filling an integer array: working with a function that will give you data in pieces. Completing this exercise will familiarize you with the behavior of the recv function, which you’ll use in lab 1 to receive data from the network.

For these programs (and all future C programming assignments), you should get into the habit of always running them with valgrind. If you have memory issues (e.g., segfaults), valgrind will tell you where they’re coming from. Even if you don’t have segfaults, valgrind will tell you if you’re doing something that might be a problem.

Lab 0 Goals:

  • Recall how to program in C

  • Practice with strings in haiku

  • Simulate send / recv buffer management behavior in fill_int_array

1. Haiku

You probably haven’t done much C programming recently. Even if you have, you probably haven’t used string functions much. We’ll be using strings for the protocols we implement in labs 1 and 2, and students often struggle more with the strings than the networking function calls.

This warm up exercise will get you reacquainted with C string functions. You’ll use a short template to generate digital haiku poetry. Since this program is essentially a Mad Lib, it’ll be up to the user to ensure the proper number of syllables (your program should not try to enforce syllables)!

The template is (note the spaces):

a new NOUN arrives
  the ADJECTIVE air begins to blow
    NOUN will come next

Your job is to fill in the template based on user input to construct a full haiku. Actually, you’ll do it twice, using two different methods. For the first, you’ll use a strcpy and strcat. For the second, you’ll use sprintf.

At a terminal, use the man command to access the manual page for these functions to get the details of how they work.

1.1. Requirements

  • I’ve provided a main function for you. You should NOT change main — it’s already set up. You only need to implement the other two functions.

  • The build_haiku_strcat function should allocate enough memory to store the full haiku in the result variable using malloc. It should then make a sequence of strcpy and strcat calls to fill in result according to the template and the passed-in user values. It should ultimately return result (a pointer to the fully-constructed haiku).

  • The build_haiku_sprintf function should allocate enough memory to store the full haiku in the result variable using malloc. It should then make just one call to sprintf to fill in result according to the template and the passed-in user values. It should ultimately return result (a pointer to the fully-constructed haiku).

  • Your program should generate no warnings from valgrind. If valgrind ever tells you something is wrong DON’T IGNORE IT! Fix it before moving on. Note that the provided main function is freeing the memory you allocated in your two build_haiku_ functions.

  • Any time you call a function that can fail (e.g., malloc or any system call), you MUST check the function’s return value for failure. If a critical function fails, you can just terminate your program with a call to exit.

1.2. Examples

User input shown in bold.

$ valgrind ./haiku
==2538227== Memcheck, a memory error detector
==2538227== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2538227== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==2538227== Command: ./haiku
==2538227==
Enter a noun: fall
Enter an adjective: cool
Enter a noun: homework
strcat version:
a new fall arrives
  the cool air begins to blow
    homework will come next


sprintf version:
a new fall arrives
  the cool air begins to blow
    homework will come next
==2538227==
==2538227== HEAP SUMMARY:
==2538227==     in use at exit: 0 bytes in 0 blocks
==2538227==   total heap usage: 4 allocs, 4 frees, 2,848 bytes allocated
==2538227==
==2538227== All heap blocks were freed -- no leaks are possible
==2538227==
==2538227== For lists of detected and suppressed errors, rerun with: -s
==2538227== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$ valgrind ./haiku
==2538855== Memcheck, a memory error detector
==2538855== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2538855== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==2538855== Command: ./haiku
==2538855==
Enter a noun: doge
Enter an adjective: much
Enter a noun: wow
strcat version:
a new doge arrives
  the much air begins to blow
    wow will come next


sprintf version:
a new doge arrives
  the much air begins to blow
    wow will come next
==2538855==
==2538855== HEAP SUMMARY:
==2538855==     in use at exit: 0 bytes in 0 blocks
==2538855==   total heap usage: 4 allocs, 4 frees, 2,848 bytes allocated
==2538855==
==2538855== All heap blocks were freed -- no leaks are possible
==2538855==
==2538855== For lists of detected and suppressed errors, rerun with: -s
==2538855== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

2. Filling an integer array

When you receive data from the network, you often don’t know in advance how much data the other side is going to send you. To simulate this, I’ve written a function for you, called get_ints, which you’ll repeatedly call until it tells you (via its return value) that no more data is coming. (This is very similar to the way that we’ll use the recv function in lab 1.)

When you call get_ints, you pass in a pointer to the location where you want it to store integers for you. It will randomly chose how many integers to store at that location (up to some max that you can choose), and its return value will tell you how many it stored. If it returns 0, that means it has no more integers for you, and you should stop calling it.

The function’s prototype is:

int get_ints(int *destination, int at_most);

When you call get_ints, you provide it with:

  • destination (integer pointer): get_ints will fill in integers starting at the location this pointer points to. Note that you (the caller) are responsible for ensuring that the memory destination points to has enough space to store the integers — the get_ints function does not allocate memory for you.

  • at_most (integer): get_ints will fill in no more than this many integers into the destination. It might (and often will) fill in fewer than this many though!

get_ints returns the number of integers it copied to the destination. If it copies no integers (and returns 0), that means it’s done, and you don’t need to call it again.

The number of integers it will ultimately copy is randomized to prevent you from hard coding a stopping point, but you may assume that it will be no more than GET_INT_MAX, which is #defined to be 200.

The integers it produces just count by 10’s, starting from 0. That is, when your program is done, your integer array should contain:

Index Value

0

0

1

10

2

20

3

30

…​

…​

I recorded a short YouTube video to help visualize how this function behaves and how you want to call it. You need to be logged into your @swarthmore.edu Google account to view it.

As you repeatedly call get_ints, you should store the integers consecutively. For example, lets say you start with an empty array and you call get_ints, which returns 7. That means the first 7 positions of the array (indices 0-6) have now been filled in. The next time you call get_ints, you should tell it to start filling in at the address of position 7, to pick up where you left off (and to preserve the first 7 items that you already copied). Thus, you’ll need to keep a running sum of how many integers it has filled in so that you can keep resuming where you left off.

Each time you call get_ints, you should pass in at_most to indicate how many integers you’re willing to accept (based on the amount of remaining memory you have allocated at the destination). Using the example above, the first time you call get_ints, you’d pass in GET_INT_MAX, since you haven’t yet filled anything in. After it returns 7, the next you call get_ints, you’d want to pass in at_most as GET_INT_MAX - 7, since you’ve already filled in 7 values. Adjusting at_most downward each time will ensure that you don’t overflow your array.

2.1. Requirements

  • For this exercise, the code in get_ints.c is provided for you, and you shouldn’t modify it. You’re welcome to look at it, although you don’t need to understand how it works to complete the lab.

  • You should only edit fill_int_array.c, and in that file, you only need to fill in the main function. Your main must:

    • Allocate enough memory to store all the integers you might ever need to store.

    • Call the provided setup function to randomize get_ints's behavior.

    • Repeatedly call get_ints, storing the integers consecutively, until it returns 0.

    • Call check_ints on your integer array to verify that they’re stored correctly.

    • Free any memory you dynamically allocated (if you dynamically allocated any).

  • Your program should generate no warnings from valgrind. If valgrind ever tells you something is wrong DON’T IGNORE IT! Fix it before moving on.

  • Any time you call a function that can fail (e.g., malloc or any system call), you MUST check the function’s return value for failure. If a critical function fails, you can just terminate your program with a call to exit.

2.2. Examples

The output here isn’t particularly interesting:

$ valgrind ./fill_int_array
==2549853== Memcheck, a memory error detector
==2549853== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2549853== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==2549853== Command: ./fill_int_array
==2549853==
Looks good!
==2549853==
==2549853== HEAP SUMMARY:
==2549853==     in use at exit: 0 bytes in 0 blocks
==2549853==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==2549853==
==2549853== All heap blocks were freed -- no leaks are possible
==2549853==
==2549853== For lists of detected and suppressed errors, rerun with: -s
==2549853== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

The Looks good! comes from check_ints. It will complain if the integers in your array aren’t consecutively counting by tens.

Submitting

Please remove any excessive debugging output prior to submitting.

To submit your code, commit your changes locally using git add and git commit. Then run git push while in your lab directory.