Lab 2: C Strings and File I/O
In this lab, you’ll get some more practice with C, focusing on “strings” and file I/O.
Part A: Playing with structs and strings
Getting started
First, make a new file, let’s call it lab2_struct.c. Define the following struct (retype this, don’t just copy it!):
typedef struct
{
int a[2];
long b;
char c[8];
char d[5];
} data_t;Now, write a function print_data that takes in a data_t and prints out the elements of this struct. You’ll need to remember a couple of things:
- You have to import
<stdio.h>to getprintfto work. - You can print an
intwith%dor%xwithout trouble, but alongis big – use%ldor%lxfor something so “long”. - You can print a string (which is really just a null-terminated sequence of
char) using%s.
Once this works, create a new variable using this struct definition in main, fill it with values, and print it. Use whatever you want for a and b, but store "systems" in C as follows:
data.c[0] = 's';
data.c[1] = 'y';
...Don’t forget to null-terminate data.c! Then go ahead and put some bytes in d. Some UTF-8 encoded bytes would work nicely:
data.d[0] = 0xeb;
data.d[1] = 0xb4;
data.d[2] = 0x84;
data.d[3] = '\0';Finally, call your print_data function, and make sure to return 1 from main to indicate a successful program completion.
To compile your code, we’ll use a shorter version of the gcc command than usual (we don’t want it checking for nefarious things we’ll do later): gcc -o lab2_struct lab2_struct.c.
Run your program (./lab2_struct). Does it print what you expect?
How “strings” print
When printf finds a %s format specifier, it starts printing characters at the location provided, reading bytes one by one until it finds a null terminator ('\0').
-
Take a moment and write out the memory diagram for this struct. Where are the bytes of
canddrelative to each other? -
Okay, let’s be nefarious. Add this line of code near the bottom of
main:data.c[7] = '#';. -
Add another call to
print_data. What has changed? Why does this happen?
Copying strings
One way to copy string data around is to use the strcpy function (you’ll need to import <string.h>). It blindly copies the src string into the dst, including the null terminator, regardless of the size of dst.
There’s a variant called strncpy in which you have to specify the number of bytes to copy. This copies the provided src string, padding with 0s at the end (effectively null terminators) if you say to copy more bytes than src has before it hits a null terminator.
-
Add another line to copy a new string into
c:strcpy(data.c, "Lulu");. -
Call
print_data. Does the result make sense? -
Now add another line:
strncpy(data.c, "weRsneaky", 9);. -
Call
print_dataone more time. Can you still make sense of the result?
Part B: File I/O
Getting started
Take a look at the line_counter.c sample, and try to answer the following questions:
- What is EOF?
- How are lines separated in a file?
- How would you count the number of vowels instead of the number of lines?
Parsing lines
Modify the line_counter.c sample to additionally print out the first character of each line, both as a character (%c) and in hex (%x). Try out your changes on this simple text file (actually copy these lines, as one has a tab and another has a space, then save it in the same folder as line_counter.c):
Good morning!
How are you
doing
today??How would you modify the program further to print out the length of each line? What about the last character? Make sure to think carefully through your logic to ensure you’re handling the last line in the file, too.