CS202 Lecture notes -- Unix, Vi, Files and Compiling


Where you are and where you are going

In CS102, you learned to program using an IDE, which is itself a program to facilitate C++. The intent of this is to get the rewards of programming to you quickly, without a lot of the frustrations that you get when you don't use an IDE.

Starting in CS202, we're going to wean you from the IDE. Rather quickly, in fact. You are going to learn how to write C++ programs in a Unix environment. This will be far more cumbersome than using an IDE. However, it facilitates you learning about computers, which is an important part of your education.

We're in a fortunate time in history (writing this in 2019) that all operating systems have some version of Linux at their core. So, whether you are running native Linux, Windows, Mac-OS or whatever today's Pi operating system is, you should have access to a Linux shell with a C++ compiler. This needs to be the first thing that you do for this class -- besides learning how to use the lab computers, you should make sure you have your own personal access to a Unix environment. Consult the TA's in lab if you have questions. That's part of their job responsibilities.


Learning Unix

I'll do some basic Unix in class. However, Kevin Heard from Berkeley has written a wonderful tutorial, available at http://people.ischool.berkeley.edu/~kevin/unix-tutorial/. Work through the first five chapters, plus chapter 8. We don't support passwd, so don't worry about that yet. Also, don't spend too much time learning pico. You'll want to use a more powerful editor, like vi or emacs.

Since this tutorial does not cover file permissions, please also read this web page, which discusses file permissions and chmod.

Learning Vi

The editor vi is my editor of choice. It is ancient, and getting it into your muscle memory takes a little time and dedication. It's worth it, in my opinion. However, you can use any editor you want: emacs, pico, even TextEdit or Wordpad. Still, give vi a try, because vi helps to make you think like a Computer Scientist.

There is an updated verion of vi called vim. It is free, and is the default version of vi on our lab machines. It is usually part of mac downloads, and you can configure cygwin to install it. Better yet, on Windows, you can download a vim program so that you can simply double-click it and it pops up a vim window. Go ahead and do that.

Then, go to http://www.vim.org/docs.php and download the book PDF and start working through the tutorial. I know a low of vi tricks and will use them in class. If you see me do something and don't understand it, ask and I'll back up and show it to you.

Files and Compilation

After wading through the Unix and Vi tutorials, you will be able to create text files in your own directories. In Unix, C++ programs are text files, typically with the .cpp extension. A program may reside in one file, or it may be spread over many files. We'll start with just one file for now.

Our example program is the canonical "hello world" program, which is in the file src/hw.cpp in the directory for these lecture notes (/home/jplank/cs202/Notes/Unix-Vi). Of course, if you click on that link, you can save the program on your own computer.

I will always use "UNIX>" to denote my shell prompt. I will usually have keyboard input either boldfaced, or boldfaced and in red. That helps you to figure out what I'm doing with these lecture notes.

You cannot run a C++ file. Instead, you have to "compile" it with a program called a "compiler." The compiler that we use is called g++. If you run g++ on a C++ file, and there are no errors, it will generate an "executable" file. By default, that file is named a.out. You may run that file by simply typing its name, preceded by "./" into the shell.

UNIX> pwd
/home/jplank/cs202/Notes/Unix-Vi
UNIX> ls
bin  index.html  makefile  src
UNIX> ls src
hw.cpp
UNIX> ls -l src
total 4
-rw-r--r--. 1 jplank jplank 103 Aug 18 12:57 hw.cpp
UNIX> g++ src/hw.cpp
UNIX> ls
a.out  bin  index.html	src
UNIX> ls -l
total 36
-rwxr-xr-x. 1 jplank jplank 18088 Aug 18 14:26 a.out
drwxr-xr-x. 2 jplank jplank    19 Aug 18 14:20 bin
-rw-r--r--. 1 jplank jplank 13461 Aug 18 13:35 index.html
-rw-r--r--. 1 jplank jplank   202 Aug 18 14:20 makefile
drwxr-xr-x. 2 jplank jplank    20 Aug 18 14:14 src
UNIX> ./a.out
Hello World!
UNIX> 
In this example, I compiled src/hw.cpp and it created the executable a.out. I ran it by typing "./a.out", and it printed "Hello World!"

The "ls -l" gives you a detailed listing of the files, including ownership (jplank), last modification time (August 18, 12:57 for a.out), size (18088 bytes for a.out), and "permissions." Those are in the first word. The x's in the listing for a.out mean that it is a program that can be executed. (BTW, if you compile this, you'll very likely get a different size for a.out, as it depends on your machine and compiler).

I can make g++ create the executable with a different name using the "-o filename" flag. You put that flag before the "source file" (the .cpp file) and I typically like to put executables in the bin directory:

UNIX> rm a.out
UNIX> g++ -o bin/hw src/hw.cpp
UNIX> ls -l bin
total 20
-rwxr-xr-x. 1 jplank jplank 18088 Aug 18 13:00 hw
UNIX> bin/hw
Hello World!
UNIX> 
When your program has errors, the compiler will print out error messages, and it will not make an executable. Sometimes figuring out what the error is difficult. However, you usually get a line number of the problem, meaning you can edit the source file, go to that line, and try to figure it out. For example, go ahead and modify hw.cpp so that there is no semi-colon after "endl":
UNIX> vi src/hw.cpp
... delete that semi-colon
UNIX> cat src/hw.cpp
#include <iostream>
using namespace std;

int main()
{
  cout << "Hello World!" << endl         // There's no semi-colon after endl
  return 0;
}
UNIX> g++ src/hw.cpp
src/hw.cpp: In function 'int main()':
src/hw.cpp:6:33: error: expected ';' before 'return'
   cout << "Hello World!" << endl
                                 ^
                                 ;
   return 0;
   ~~~~~~                         
UNIX> 
That's a nice error message -- you need a semi-colon on line 6. By the way, if you haven't used "cat" before, it simply prints a file to the screen. Fix the bug, recompile and rerun to double-check yourself. Now, edit it again, and delete the second quotation mark. I'll use "cat -n" to print line numbers when it prints the file:
UNIX> vi src/hw.cpp
... delete the second quotation mark:
UNIX> cat -n src/hw.cpp
     1	#include <iostream>
     2	using namespace std;
     3	
     4	main()
     5	{
     6	  cout << "Hello World! << endl;   // There's now no quotation mark after '!'
     7	  return 0;
     8	}
UNIX> g++ src/hw.cpp
src/hw.cpp:6:11: warning: missing terminating " character
   cout << "Hello World! << endl;
           ^
src/hw.cpp:6:11: error: missing terminating " character
   cout << "Hello World! << endl;
           ^~~~~~~~~~~~~~~~~~~~~~
src/hw.cpp: In function 'int main()':
src/hw.cpp:7:3: error: expected primary-expression before 'return'
   return 0;
   ^~~~~~
UNIX> 
That error printout is a little more cluttered, but it gets the point across. Some are more difficult. On my macintosh, when I try to compile that program, I get vomit on the screen:
UNIX> g++ src/hw.cpp
hw.cpp:6:11: warning: missing terminating " character
hw.cpp:6: error: missing terminating " character
hw.cpp: In function 'int main()':
hw.cpp:7: error: no match for 'operator<<' in 'std::cout << return 0 '
/usr/include/c++/4.2.1/ostream:112: note: candidates are: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>& (*)(std::basic_ostream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:121: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ios<_CharT, _Traits>& (*)(std::basic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:131: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::ios_base& (*)(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:169: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long int) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:173: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long unsigned int) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:177: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(bool) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/bits/ostream.tcc:92: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short int) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:184: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short unsigned int) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/bits/ostream.tcc:106: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:195: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(unsigned int) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:204: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long long int) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:208: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long long unsigned int) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:213: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(double) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:217: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(float) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:225: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long double) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/ostream:229: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(const void*) [with _CharT = char, _Traits = std::char_traits]
/usr/include/c++/4.2.1/bits/ostream.tcc:120: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_streambuf<_CharT, _Traits>*) [with _CharT = char, _Traits = std::char_traits]
UNIX> 
When you get that, just look at the first few lines and ignore the rest. The first few lines here help you diagnose the problem in the same way as on our lab machines.

The program "make"

make is an ancient program that has gone through infinite variations and upgrades. It should not be surprising to you that I like it in its most primitive (and therefore most portable) version. You do not need to learn how make works.

In lecture note and lab directories, I will include a file called makefile. When you type "make" in one of these directories, it will perform compilation for you. Also, if you type "make clean", it will delete temporary and compiled files. These are both super-useful features, so even if you don't learn how to write a makefile, you should get used to using make when I have written one:

UNIX> ls                                      # As you note, a.out is leftover from previously. 
a.out  bin  index.html  makefile  src
UNIX> ls bin                                  # So is bin/hw.
hw
UNIX> make clean                              # "make clean" deletes these files.
rm -f bin/* a.out
UNIX> ls                                      # See -- there's no more a.out
bin  index.html  makefile  src
UNIX> ls bin                                  # And no more bin/hw
UNIX> make                                    # Typing "make" compiles src/hw.cpp to bin/hw
g++ -std=c++98 -Wall -Wextra -o bin/hw src/hw.cpp
UNIX> bin/hw
Hello World!
UNIX>