Chapter 1- Writing C programs
In this chapter you will see two step-by-step examples showing how to use Leonardo to write, compile and execute a C program. Chapters 5, 6, 7 and 8 will give you more information about these topics.
1.1 - A simple example: sorting an array of numbers
We're going to implement the well known Bubblesort algorithm. Since it's really simple we will focus on the steps required to write, compile and execute it in Leonardo, rather than on its behaviour.
In the C language a program is a collection of translation units, stored in files with ".c" extension. This allows you to have separate modules and to solve problems independently, which makes it easier to develop large and complex C programs. A C project is a list of all files that make up a C program: in this way, the compiler is able to compile each file in this list, and eventually, if no error was found, to link together all pieces of code to build an executable file.
Now we have to create a new C project. Launch Leonardo's application from the Finder and choose New + C Project from the File menu:
The following dialog will appear:
Select your destination folder (a good choice may be the Desktop Folder), change Untitled with Bubblesort and save. Now your new project file has been created (note a folder that contains it is created too):
Note it just contains two files given by default: ANSI.c and visualizer.c. By now ignore them.
Now, select the button in the C project window. In this way you will be asked for the name of a new file to be added to your project:
Type Bubblesort and click on the OK button. Now the file Bubblesort.c has been created within the "Bubblesort.µ f" folder, and it has been added to your project. A text editor window appears on your screen:
Now we're ready to write our program.
There's nothing remarkable about it. Just paste the following piece of code in Leonardo's text editor window :
#include
<stdio.h>
int n; long v[100]; void BubbleSort(long v[],int n){ int i=-1,another,q; do { another=0; for (i=1;i<n;i++) if ((v[i-1]>v[i])) { long temp=v[i-1]; v[i-1]=v[i]; v[i]=temp another=1; } } while (another); } |
void
main(){
int i; printf("How many numbers ? "); scanf("%d",&n); for (i=0;i<n;i++) { printf("Enter number #%d: ",i); scanf("%ld",&v[i]); } Bubblesort(v,n); for (i=0;i<n;i++) printf("v[%d]=%ld\n",i,v[i]); } |
Note that:
You will notice the text editor performs syntax coloring of sources, auto-indentation and parenthesis matching.
Step 3: checking program's syntax
Now let's check program's syntax:
The following error window will appear:
There are two errors and one warning: we have forgotten a semicolon and we have written Bubblesort instead of BubbleSort (note C is case-sensitive). Moreover, we have declared a variable (q) that has never been used in the BubbleSort function.
Note the error window is clickable: just click on an error (or warning) pane and you will jump to the text window containing the error, highlighted and ready to be corrected.
Tip: | To bring the error window to the front type Command+I or select it from the Window menu. |
Correct your source code and retry ... it should be OK now.
Now we are ready to run our first program.
Type Command + R (or select Run from the Run menu) to compile, link and launch your program.
The execution environment should appear:
The execution environment is made of at least three main windows:
Now press the forward step button: the first printf instruction is executed, showing the text "How many numbers ?" in the console window.
Now press the backward step button: the printf instruction has been reversed, and the text "How many numbers ?" disappears from the console.
If you want to execute your program in a continuous mode, press the forward continuous execution button.
At the end of a typical execution session you will be in the following situation:
Note your input is red, while program's output is black.
Try pressing repeatedly to test the backward mode.
If you want to reset your program in one hop, press.
If you want to execute it in backward mode, press.
Step 5: abort program's execution
When a program's execution reaches the end, it does not mean the process is really terminated: actually you can reverse the execution and play it backward and forward as long as you like.
If you want, instead, to remove your process from memory by closing its execution environment, you should click on the go-away button of the control tool:
Step 6: adding breakpoints to your program
A breakpoint is a special marker that tells the execution environment to stop when program's control flow reaches it. This may be very useful for debugging purposes. Unfortunately you can add breakpoints only at compile time, by means of the special directive:
#pragma breakpoint |
This directive may be put anywhere in the source code, provided it is used as statement.
Let's add a simple breakpoint to our example:
void
main(){
... Bubblesort(v,n);
for (i=0;i<n;i++) printf("v[%d]=%ld\n",i,v[i]); } |
The virtual machine will stop executing a program, either in forward mode or in backward mode, every time a breakpoint is encountered.
1.2 - Another example: the breadth-first visit of a graph
We will not describe all the steps that we shown in the previous section, so please look back at it for technical details about compiling and running programs.
In this section we will show a simple C implementation of the well-known breadth-first visit (BFV) of an undirected graph. For a complete description of the BFV algorithm see: Cormen, Leiserson, Rivest, "Introduction to Algorithms", Addison-Wesley.
A trivial C implementation of the BFV algorithm is shown below:
#include
<stdio.h>
#include <stdlib.h> #define MAX_NODES 100 #define MAXINT 32767 // Data structures char g[MAX_NODES][MAX_NODES]; int num_nodes; int parent[MAX_NODES]; // Routines int load_graph(){
} // Breadth-first visit void BFV(int i){
} // Main function void main(){
} |
Note the program is composed of the following data structures and routines:
char g[MAX_NODES][MAX_NODES] | This is an adjacency matrix that is used to hold the graph to be visited |
int num_nodes | This variable holds the number of nodes of the graph |
int parent[MAX_NODES] | This array is used as a parent-vector to represent the spanning tree generated by the BFV algorithm |
load_graph() | This function is used to initialize the data structures with a graph retireved from a graph file or from Leonardo's built-in graph editor |
BFV() | This funtion performs the breadth-first visit of the graph starting from a given node |
main() | This funtion calls load_graph and then BFV |
The program gets its input by means of the load_graph function that uses Leonardo's sytem calls:
- OpenGraph(): opens a graph chooser dialog allowing the user to pick up a graph from a list of all graphs currently available in the main memory (e.g. from a graph editor's window) and previously saved in the same folder as the currently executed program.
- GetNodesCount(): if called between OpenGraph() and CloseGraph() yields the number of nodes of the chosen graph (zero is case of error).
- GetArcsCount(): if called between OpenGraph() and CloseGraph() yields the number of edges of the chosen graph (zero is case of error).
- GetArc(long idx, long* start, long* end): if called between OpenGraph() and CloseGraph() stores in parameters *start and *end the end points of the idx-th edge of the chosen graph (idx must be in the range 0 to GetArcsCount()-1).
- CloseGraph(): closes a graph retrieval session.