Computer Science 010

Lecture Notes 6

Advanced Emacs, Version Control, Math in C, and Function Calls

Help in Emacs

So far the only help command we have looked at in Emacs is the info command. Here are three more commands that you may find useful:

Apropos is bound to C-h a. It prompts you for a string:

Apropos command (regexp):

Type in a word and it will show you all the commands that have that word in their names along with the key sequence they are bound to and a one line description. The list appears in a separate buffer and Emacs moves your cursor into that buffer.

As you should have figured out by now, knowing the key bindings for commands allows you to enter the commands more quickly, but it takes a lot of effort to learn what the commands are. Use C-h b to see a list of all the key bindings. Again, these will be displayed in a separate buffer. You could print out a list by clicking with the mouse in that buffer and then use M-x print-buffer.

One more useful help command allows you to get help specifically about your current mode. If you are editing a C file and in C mode, you can find out more about C mode by typing C-h m. This creates a buffer with help on your current mode. You can read it online or you could again click in the buffer and use the print-buffer command to get a printed copy.

Compiling in Emacs

Last time we saw how to run the debugger while still in Emacs. We can also compile our code from within Emacs. To do this type M-x compile. The message buffer at the bottom of Emacs will say:

Compile command: make 

Hit backspace to erase the word make and type your usual compilation command

Compile command: gcc -Wall -g -o match match.c

If you have not saved all of your changes, Emacs will put out a message asking you if you want to save the buffer first:

Save file /home/cs-students/jcool/match.c? (y, n, !, q, C-r or C-h)

You should type y. Emacs will save the buffer and continue with the compilation. Now Emacs will create a buffer called *compilation*. It will display error messages in this buffer. If you click on an error message in this buffer with your mouse and then hit the return key, Emacs will scroll the error message to the top line of the buffer, move the cursor to the buffer containing the file with the error, placing the cursor on the line where the error occurred. This makes it very easy to find the lines with errors. You can go back to the *compilation* buffer and repeat this for another error message.

After you compile once, subsequent compile commands will print the last compile command that you executed. If you want to compile the same file again, just hit the return key. If not, just edit the line.

Customizing Emacs

One of the nice things about Emacs is that it is highly customizable. One way that it can be customized is by changing modes within Emacs.

Major and minor modes

Emacs provides major and minor modes. Major modes are based upon the type of information that is in the buffer. We have seen 3 major modes so far: C mode, gdb mode, and compilation mode. In each case, the status line indicates what mode we are in. This is approximately in the middle of the line and is enclosed in parentheses. A major mode typically redefines some key bindings to do something interesting for that mode. For example, in compilation mode, the return key is redefined to show us where an error message occurs in our program.

Emacs also has minor modes. Minor modes allow some tweaking of the major mode. We have seen two minor modes in C mode: auto-newline-insertion mode and hungry-delete mode. We can turn these on and off individually and still stay in C mode. These update the status line to show us what minor modes we are in as well.

C mode offers several styles of indentation, including the ones show in King on pages 79-80. If you don't like the default style, you can change it using the c-set-style command (remember to precede it with M-x). It will prompt you with:

Which C indentation style? gnu

It will display gnu because that is its default style. Erase gnu and type ?. You will see a list of indentation styles. Pick one. When you are back in your C buffer, type C-c C-q. It will reformat your current function using that style. Play around until you find a style you like.

You might also want to control how many spaces a line indents when it auto-indents. To do that, use the set-variable command. You will get the prompt:

Set variable:

Type c-basic-offset and return. Now you will get the prompt:

Set c-basic-offset to value:

Type the amount you want and hit return. Back in your C buffer, use C-c C-q to reformat again.

.local_emacs

If you play around with these two commands you will find a setting that you like and you will want to use again. If you don't save it somehow, you will need to re-enter those commands everytime you start Emacs. The way to save these settings is by defining a file called .local_emacs and placing some commands in that file to set up your environment. This file uses a language called Lisp to define the customizations. Lisp has a rather unusual syntax, but it is generally easy enough to copy what somebody else does and make some modifications. Here is what you would put in your .local_emacs file to use k&r indentation, an indent of 2 spaces, auto-newline-insert mode and hungry delete mode:

;;; These are some possible customizations for C mode
   
;;; Define a function named my-c-mode-hook to perform my customizations
(defun my-c-mode-hook ()
  ;; Use k&r style formatting
  (c-set-style "k&r")
   
  ;; Use 2 as the amount for indentation
  (setq c-basic-offset 2)
   
  ;; Turn on auto-newline-insert mode
  (c-toggle-auto-state 1)
   
  ;; Turn on hungry-delete mode
  (c-toggle-hungry-state 1)
)
   
;; Install my-c-mode-hook so these commands are run whenever I enter C mode.
(add-hook 'c-mode-hook 'my-c-mode-hook)
   

Lisp syntax is:

(function-name arguments)

First we define a function using defun. The name of the new function is my-c-mode-hook. The new function takes no parameters. The indented text is the statements in the function. The first executes the c-set-style function to change the style. The next changes the value of the c-basic-offset variable by calling the setq function. The second argument to this function indicates what value to assign. The next 2 statements turn on the minor modes. If you don't want to turn them on, just leave those statements out. Then outside the function, the add-hook statement indicates that our new function should be called whenever we enter C mode. The syntax is certainly bizarre. Becoming an expert in Emacs Lisp programming is definitely difficult. Fortunately, we usually have sample code we can follow to figure out what to do. In fact, the code above is just a modification of the code provided on the C mode info page under the "Sample .emacs file" link.

RCS

RCS is a version control system available on Unix. It is very useful to manage your files with RCS so that you can back out of changes if you make mistakes. In this course, you will use it to manage programs, but you should also find it extremely useful for managing drafts of papers that you write.

Each file that is managed by RCS has an RCS file in addition to the normal text file you edit. An RCS file contains a listing of the first version of your file in its entirety. Each time that you update the RCS file, it determines what has changed and adds a list of the changes to the file. In this way, multiple versions of a file are stored much more compactly than if each version was stored in its entirety.

RCS is even more useful when working on team projects because it ensures that multiple users do not modify the same file at the same time. We will not talk about these sharing aspects of RCS further in this class.

To use RCS, you should first create a directory called RCS in the same directory where you store your programs. If you have programs in more than one directory, you should have an RCS directory in each of those directories.

  -> mkdir RCS

Creating an RCS File

To create the initial RCS file for some document, you ``check in'' the document using the ci command. When you check in a document for the first time, RCS will ask you for a log message. This should be a simple description of the contents of the file, such as ``Program to find substrings''.

When you use ci, you must specify the name of the document that you are checking in. If there is an RCS directory in the same directory as your document file, you can simply say:

  -> ci -l match.c

This will prompt you for a log message. It will then create an RCS file named RCS/match.c,v.

You should never modify the contents of any RCS file directly. Instead, you should use RCS commands exclusively to modify the contents of any RCS file.

Creating New Versions

To create a new version of a document managed by RCS, you first ``check out'' the document. Then you modify it using Emacs. When satisfied with the changes you ``check in'' the document.

To check out a document, use the co command:

  -> co -l match.c

If there is an RCS directory in the directory where you issue the co command and that directory contains a checked-in version of match.c, this will create the file match.c. The contents of this file is the last version of match.c that was checked in.

After you are done modifying the file, check it back in using the ci command. You will be asked to describe the changes that you made. It is important that you provide enough information that you will later understand what that version represents in case you need to revert to that version at a later time.

Viewing an RCS File

Suppose you just want to see the latest version of a document, but you do not intend to modify it. In this case, you again use the co command, but omitting the -l argument:

-> co match.c

This will give you a read-only version of the program. It is not necessary to check in a document that is checked out in this manner.

Finding out what has Changed

Suppose you have modified a file since the last time that you checked it in and you want to know exactly what you changed. Use the rcsdiff command to do this. This will give you output similar to diff but it will generate its output by comparing the checked out version of the file with the most recently checked in version.

-> rcsdiff match.c

Displaying Information about RCS Files

To get information about an RCS file such as the log messages associated with versions of the file, use the rlog command:

  -> rlog match.c

Getting More Help

You can get more details about RCS from the RCS man pages.

Math in C

While Fortran has long been the programming language of choice for mathematical applications, C has gained popularity in recent years.

Floating point data types

C provides two floating point data types. float is a single-precision floating point value, while double is a double-precision value. A double typically takes twice the storage but provides more accuracy and can represent a wider range of numbers. In general, unless your application has need to conserve memory, it's a good idea to use the double datatype to represent floating-point values.

Just as you can use %d to print integers with printf, you can use %f to print floating point values. The situation is a bit trickier with scanf since it needs to know if the value being read is a float or a double. For a float, use %f and for a double, use %lf. Similarly, if reading a long instead of an int as we've been doing, use %ld.

C automatically converts between floating point and integer types as needed. So, for example, if you write:

double value=2;
The integer constant 2 will be converted to the double precision value 2.0 before being assigned. This applies to the result of mathematical expressions, function arguments, and function return values as well. Most often, these conversions result in exactly what you want, but not always. Consider this program:
#include <stdio.h>

int main() {
  int numerator, denominator;
  double quotient;

  printf("Enter two integers: ");
  scanf("%d %d", &numerator, &denominator);

  quotient=numerator/denominator;

  printf("%d/%d=%f\n", numerator, denominator, quotient);

  return 0;
}
Try it out and see what happens? Why? How can we fix this?

The C Math Library

C provides a number of math-related functions and constants in the Math library. See the man page for math to see a list of the available functions. To use them, include the math.h header file and add -lm to your compile/link command to tell the linker to look in libm.a for the math functions.

What happens when you call a function in C?

Quite a bit, actually. But if you can understand what happens here, it will help you avoid mistakes and you will be better equipped to track down problems when they arise.

Any time you pass a parameter to a function in C, you are creating a local variable in the function. The difference between these variables and other local variables you may declare in a function is that these variables are automatically initialized to the values passed in. Consider the function:

int addone(int val) {

   val++;
   return val;
}
which is called like this:
  int answer;
  answer=addone(2);
We can all agree that answer will get the value 3 after this code executes, but what's really going on?

When addone is called, a local variable val is created. It is initialized to 2. We increment this local variable to 3, then return that value. The return value is stored in the variable answer in the calling function.

But what about this call?

  int answer;
  int start=2;

  answer=addone(start);
Here, the local variable val will be initialized to the value of start, which is again 2. You might think that the val++ line in addone will also increment the variable start in the calling function, but this is not the case. It would be the case in some programming languages, but not in C (or in Java).

But then how can we change the value of a parameter, if that is what we actually want to do?

We can pass pointers to the actual variable. Consider this function:

void increment(int *val) {

   *val=*val+1;
}
which is called like this:
  int start=2;
  increment(&start);
Now, we're passing a pointer to the integer we want to increment. Inside the function, we dereference the pointer to get the old value and to tell it where to store the new value. So after the call to increment, the value of start in the calling function has been incremented to 3. What's going on here? Where have we seen this before?