CS 240 Summer 2024

Lab 1: Getting acquainted with C and its system environment (210 pts)

Due: 06/19/2024 (Wed.), 11:59 PM

Objective

The objective of this introductory lab is to familiarize you with the system environment for developing C programs under Linux on our lab machines.


Reading

Read chapter 1 from Kernighan&Ritchie (textbook). The introduction provides an overview of C programming. It is a bit dense. You are not expected to understand all the material. What you need to understand is the material discussed in class, with the textbook serving as a supplementary resource.


Lab1 code base

The C code base for lab1 is available as a tarball at

/homes/park/pub/cs240/lab1.tar

on the Linux machines (borg) in our lab. If you are not able to access the tarball it implies that you are not logged onto a lab machine. Copy the tarball to your working directory of choice and unpack it by running

% tar xvf lab1.tar

Under lab1, you will find 7 subdirectories that contain versions of the example code discussed in class. As noted in class, always compile and run the final version on a Linux PC in our labs.


Problems [210 pts]

Problem 1 (15 pts)

Compile the code in directory v1/ by running

% gcc main.c

What are the size, timestamp, and protection settings for owner, group, and other of main.c? Use the command "ls" with option "-l" to determine the properties. Modify the protection bits so that only the owner (you) can read and write main.c. Protection bits for group and other should be set so that no access whatsover is permitted. What are the owner protection bits for a.out, the executable file generated by gcc? What is the key difference when compared to main.c? Set the owner protection bits of a.out to match the ones of main.c. What happens when you try to execute a.out? Note: Per course policy you must ensure that your work is protected from access by others. For files belonging to you on CS's Linux, this means setting the protection bits of group and other to not be readable, writable, executable.

Problem 2 (15 pts)

What are two ways of changing the name of the executable file generated by gcc so that it is not the default a.out but a name of your choice? Where is the executable file, gcc, located on our system? Use the whereis app to find gcc's location. Does the location shown by whereis specify the true location of gcc? Explain your reasoning based on the discussion in class. What is the true name and full pathname of gcc that we are using in CS 240?

Problem 3 (15 pts)

The two components of the app gcc that we are primarily concerned with in CS 240 are the C compiler and preprocessor. As noted in class, the C preprocessor (CPP) is helper software that assists the C compiler by handling all statements starting with '#' such as #include <stdio.h> in v2/main.c. The CPP command (or directive) #include instructs CPP to locate the file stdio.h where the angle brackets indicate where to look for the file. On our lab machines running Linux, CPP will look for stdio.h in the directory /usr/include among other places that are part of gcc configuration. Verify that stdio.h indeed is a file in the /usr/include directory, determine its size and protection bit settings. Compile v2/main.c using gcc and verify that a.out executes correctly.

Problem 4 (15 pts)

Copy stdio.h from /usr/include to your v2/ directory and rename the file prob4.h. Make a copy of v2/main.c, call it main33.c under v2/. Modify main33.c so that angle brackets of CPP statement #include <stdio.h> are replaced by double quotation and the name of the header file is prob4.h: #include "prob4.h" Run gcc on main33.c and verify that it compiles without error or warning. Execute a.out to verify that it runs correctly. What has happened by replacing angle brackets by double quotation with respect to gcc's actions?

Problem 5 (20 pts)

To actually run the machine code contained in the file a.out on the Linux PC hardware in our labs, we utilize the help of another app, called a shell, which acts primarily as a user interface. There are various versions of shell apps used in Linux from the barebones Bourne shell (/bin/sh) to more feature rich shells such as C shell (/bin/csh) and Bourne Again Shell (/bin/bash). The core software structure of all shells is the same, and we will code a simple shell app as an exercise for coding a client/server app in C later in the course. Determine the type of shell you are running on the Linux machine by running the command /bin/ps upon logging in to a lab machine. Based on the discussion in class, explain in words what happens when a.out is executed with the help of a shell app

$ a.out

at the shell prompt '$' which can be changed to output '%' or variously customized. Your explanation should include the roles shell, gcc, a.out, linker, and loader.

Problem 6 (20 pts)

The code in v1/main.c is not practically useful since we cannot inspect the work performed when executed. We used code written by others, called library functions, in v2/main.c to output the result of addition to stdout, a device name used in C, to specify a terminal display attached to a computer. To use library functions we need to tell gcc how to find them. The CPP statement #include <stdio.h> helped do that. In C any function that is invoked must have its function prototype specified in the file where it is invoked. This applies to all functions whether written by you or a library function. Inspect stdio.h (see Problem 3) and find the function prototype of library function printf(). Why the function prototype of printf() looks the way it does will become clear later in the course. For the moment, verify and show the function prototype of printf() as gleaned from stdio.h.

Since v2/main.c only uses library function printf() and stdio.h contains function prototypes of many other library functions that we are not using (including function prototypes that are not being used is allowed in C), it stands to reason that we can extract the function prototype of printf() from stdio.h and copy it to main.c without include stdio.h. To check if this works, make a copy of v2/main.c and call it v2/main44.c. Edit main44.c by deleting the CPP include statement and insert the function prototype of printf() above the code main() in main44.c. Test and verify that main44.c compiles and executes correctly.

Problem 7 (15 pts)

An application software coded in C may be viewed as a collection of functions whose execution starts at the designated function main() which may call other functions that, in turn, may make further function calls. This characterization is not completely accurate since main() itself contains statement "return" which indicates that main() itself is called by some other function. By default, gcc performs several tasks behind the scenes which includes inserting code of a function called _start() which calls main(). The return value of main(), a convention, is recommended to be 0 if main() has executed normally; some other value such as -1 if not. The role of _start() is systems related that is beyond the scope of CS 240. In v2/main.c insert the return statement after the call to printf() with return value 0 or -1. Check that the program executes as before without the return statement. Note that when a return statement is omitted, gcc inserts it on the programmer's behalf. Change the return value of main() to five different values with the aim of causing the program to run abnormally. Specify what these values were and their outcome.

Problem 8 (30 pts)

The version in v3/ makes a further enhancement such that the two numbers to be multiplied are provided as input via standard input which, by default, is the keyboard attached to a Linux PC in the labs. On the surface and from the viewpoint of a "user friendly" programming language, it may seem that

scanf("%d %d", x, y)

to inform the scanf() function to read two integer values from keyboard and store them in integer variables x and y would be a reasonable choice. How does gcc respond when you omit the ampersand & in front of x and y when calling scanf()? Does it generate an error or warning? From a practical perspective, how is a compilation warning different from a compilation error? When you execute a.out you should see output to stdout that reads "Segmentation fault" which is the most common run-bug you will encounter in CS 240. Based on the discussion in class and providing 6 and 3 as input to scanf(), explain what may be causing the program to fail. Make sure to tie your explanation to the discussion from class and the specific values provided to scanf().

Bugs detected by gcc during compilation that result in gcc refusing to generate executable a.out are simple bugs. Coding headaches arise when run-time bugs arise that cannot be detected at compilation time. The bulk of coding effort and time revolves around figuring out why run-time bugs occur. Proficiency at debugging is a skill acquired through practice and experience. There is no shortcut.

Problem 9 (20 pts)

v4/ contains a floating point (i.e., real number) variation of v3/, and in v5/ code is made more modular by implementing the addition part as a separate function, add2(). Since add2() is essentially a one-liner, the separation doesn't buy much in terms of enhanced modularity. However, the principle is clear: if the tasks were more involved, putting the code in a separate function would make the design more modular and organized. add2() takes the values to be subtracted as its two arguments and returns the result to the calling function. Hence the caller is main() and the callee is add2(). When passing the two arguments to add2(), why do we not use ampersands, that is, invoke add2(&x,&y)? What happens if we do? Test by making a copy of v5/main.c, v5/main55.c, and compile by running gcc. Does the code change result in a compilation bug or run-time bug?

Problem 10 (15 pts)

What was the purpose of the coding change from v5/ to v6/? Do we need to name the new file add2.c? If not, why name the file add2.c? Assuming all files containing C code end with suffix .c are contained in the current directory, what shortcut courtesy of your shell can be use to reduce typing effort when invoking gcc?

Problem 11 (30 pts)

Create a subdirectory v25/ under lab1/ and code a function maximus3() which takes three integer arguments and returns their maximum value. Do not use library functions but write basic C code from scratch. How elegant or efficient your code has not bearing the points received for this problem. Those concerns will be addressed later in the course. Place maximus3() in its own file v25/maximus3.c. Verify that maximus3() works correctly by calling it from a test function main() in v25/main.c and printing to stdout the result returned. Call maximus3() with different arguments to increase confidence that your code works correctly. Make sure to annotate your code with comments that help the reader understand your code. In the real-world much of the time and effort in software engineering is spent during testing. This includes run-time debugging since tests oftentimes fail due to bugs in the code. To exhaustively determine correctness of maximus3() by brute force, how many calls to maximus3() would need to be made? Explain your reasoning.


Bonus problem (20 pts)

Create a subdirectory v26/ under lab1/ and code a function raisetopower() which takes two integer arguments, raises the first argument to the power of the second argument, and returns the result. For example, raisetopower(2, 3) returns 8 since 2 to the power of 3 is 8. Do not use library functions but write basic C code from scratch. Annotate your code. Note that 0 raised to any power is 0, and any integer raised to the power 0 is 1. Place raisetopower() in its own file, v26/raisetopower.c. Test and verify that your code works correctly.

The Bonus Problem is entirely optional. Bonus points count toward reaching 35% of the course grade contributed by lab assignments.


Turn-in instructions

Electronic turn-in instructions:

i) For problems that require answering/explaining questions, submit a write-up as a pdf file called lab1.pdf. Place lab1.pdf in your directory lab1/. You can use your favorite editor subject to that it is able to export pdf files which many freeware editors do. Files submitted in any other format will not be graded. The TAs need to spend their time evaluating the content of your submission, not switching between editors or hunting down obscure document formats which wastes time and is in no one's interest.

ii) We will use turnin to manage lab assignment submissions. In the parent directory of lab1, run the command

turnin -c cs240 -p lab1 lab1

You can verify/list your submission by running: turnin -c cs240 -p lab1 -v. Please double-check that you submitted what you intended to submit.


Back to the CS 240 web page