to the main page        to the table of contents

Programming in Safe-C



Chapter 2 : my first programs


2.1 Install the Safe-C compiler

To compile your programs, vous need the Safe-C compiler that you can download here.

Create a folder on your C disk (for example C:\Safe-C), then unzip and copy all files in it.

You should see the following files :


2.2 Write a simple program

Open a Dos Command Box (see previous chapter) and move into the compiler's folder.

type the command :

    notepad hello.c

Notepad will ask you : "would you like to create a new file ?" Answer YES.

Type then your first program :


// hello.c

from std use console;

void main()
{
  printf ("Hello, World !\n");
}

Save and close Notepad.

Check that your file hello.c was created within the folder :

In the Dos Command Box, type then the command :

    mk hello

that will compile your source text hello.c into the program hello.exe

All that's left to do is starting your program by typing :

    hello

and you should see "Hello, World!" appear on the screen.

Congratulations, you just created your first program !



2.3 "Hello World" in details


// hello.c

from std use console;

void main()
{
  printf ("Hello, World !\n");
}

Let's examine this first program in details.

The first line starts with the sign // which indicates a comment. It is thus ignored by the compiler.

from std use console;

The second line tells the compiler to open the standard library std (std.lib) and to look for the component 'console'. If you have a look on the main page of this website to see the content of the component 'console' you will find the declaration of the function printf() that we will be able to use below.

void main()

The third line indicates to the compiler that the program starts here. All Safe-C programs consist of one or more functions. main() is such a function, and in fact all programs have a main() function because that's where the program starts. In general the main() function will call other functions.

{
  printf ("Hello, World !\n");
}

The curly brackets { } enclose the instructions that the function will execute. Here we have just one instruction, printf(), which is used to display a text of the command box. Each instruction ends with a semicolon (;)

To call a function, you just give its name (here printf) et you provide any arguments within parenthesis (here "Hello, World !\n"). When there are no arguments, you just add an empty pair of parenthesis ().

By the way, the sign \n does not display, it's just a symbol that indicates a carriage return to the next line. So, if you put a second printf() just after, it will display its text on the line below and not to the right.

In fact, we could have written the printf() in three times with the same effect :


  printf ("Hello, ");
  printf ("World !");
  printf ("\n");


Just try to modify this program to display something else ..

2.4 My 2nd program

The following program will use the formula C = 5/9*(F-32) to display a temperature conversion table between Farenheit and Celsius degrees :
    0   -17
   20    -6
   40     4
   60    15
   80    26
  100    37
  120    48
  140    60
  160    71
  180    82
  200    93
  220   104
  240   115
  260   126
  280   137
  300   148
This program introduces several new ideas : variables and repetition.

// table Fahrenheit-Celsius

from std use console;

void main()
{
  int fahr, celsius;
  int lower, upper, step;
  
  lower = 0;    // lower limit of temperature scale
  upper = 300;  // upper limit
  step = 20;    // step size
  
  fahr = lower;
  while (fahr <= upper)
  {
    celsius = 5 * (fahr-32) / 9;
    printf ("%5d %5d\n", fahr, celsius);
    fahr = fahr + step;
  }
}

In Safe-C, you need to declare a list of all variables before using them :

  int fahr, celsius;
  int lower, upper, step;

"int" indicates that they are integer variables that contain a number without comma between -2147483648 and +2147483647.

The program starts with 3 assignations that give the variables their initial values :

  lower = 0;    // lower limit of temperature scale
  upper = 300;  // upper limit
  step = 20;    // step size

The "while" instruction is used to loop : while the condition is true, we repeat the instructions between curly brackets. Note that the curly brackets are not necessary if there's only a single instruction.

  fahr = lower;
  while (fahr <= upper)
  {
    celsius = 5 * (fahr-32) / 9;
    printf ("%5d %5d\n", fahr, celsius);
    fahr = fahr + step;
  }

Most of the work is done in the computation "5 * (fahr-32) / 9" which will put its results in the left-hand variable. The sign * indicates a multiply, the sign / indicates division. Note that, since these are 'int' variable, thus without comma, results will be truncated, the fractionnary part is removed.

    celsius = 5 * (fahr-32) / 9;

The function printf is used to display a formatted line. Here the format %5d indicates that we want to display a decimal value (so int) in 5 positions.

    printf ("%5d %5d\n", fahr, celsius);

The last instruction will increase 'fahr' by the value 'step'. More precisely, it will compute the right-hand expression 'fahr + step' and put the result into the left-hand variable "fahr". The variable receives thus a new value before the next iteration of the loop.

  fahr = fahr + step;

You noticed that the temperatures are approximative since we work with type 'int'. Here's the same program with "float" variables :


// table Fahrenheit-Celsius

from std use console;

void main()
{
  float fahr, celsius;
  float lower, upper, step;
  
  lower = 0.0;    // lower limit of temperature scale
  upper = 300.0;  // upper limit
  step = 20.0;    // step size
  
  fahr = lower;
  while (fahr <= upper)
  {
    celsius = 5.0 * (fahr-32.0) / 9.0;
    printf ("%8.3f %8.3f\n", fahr, celsius);
    fahr = fahr + step;
  }
}

which results in the following output :

   0.000  -17.777
  20.000   -6.666
  40.000    4.444
  60.000   15.555
  80.000   26.666
 100.000   37.777
 120.000   48.888
 140.000   60.000
 160.000   71.111
 180.000   82.222
 200.000   93.333
 220.000  104.444
 240.000  115.555
 260.000  126.666
 280.000  137.777
 300.000  148.888

Notice the special formatting for floats :

    
    printf ("%8.3f %8.3f\n", fahr, celsius);

which means : display with 8 character in total, including 3 digits after the comma.

The function printf has lots of options and allows the following formattings :

    
   "% [flag] [width] [. precision] Type"

   Type                          output
   ----                          ------
   %                             '%%' will be written '%'
   d  int                        decimal number (-61)
   u  uint or enum               unsigned number (12)
   x  integer or enum            hexadecimal number (7fa)
   e  float, double              scientifique format (3.9265e+2)
   f  float, double              floating comma (392.65)
   c  char or string             all chars, or max 'precision' chars.
   C  wchar or wstring           all wchars, or max 'precision' wchars.
   s  string                     string stops at nul, or "precision" chars
   S  wstring                    wstring stops at Lnul, or "precision" wchar

   flag
   ----
   -    For all            : justify left (default is : justify right).
   +    For d, e, f        : prefix '+' for positive or zero numbers.
   0    For d, e, f, x, u  : prefix leading '0' instead of spaces
                             (not allowed together with flag '-')

   width
   -----
   (Number)   minimum number of characters to display (padded with
              spaces or digits zero if necessary).
              The value is not truncated if the result is too long.
   *          The width is not specified in the format string but in an additional
              "int" parameter preceding the value to be displayed.

   precision
   ---------
   . Number  A point without number indicates a precision of zero.
             For f: it's the number of digits after the decimal point.
                    Default precision is 6.
                    No decimal point is displayed when precision is zero.
             For s/S: it's the exact number of characters to display.
                      By default it displays all characters.
   .*        Precision is not specified in the format string but in an additional
             "int" parameter preceding the value to be displayed.



Here is now a shorter way to write the same program :


// table Fahrenheit-Celsius

from std use console;

void main()
{
  int fahr;
  for (fahr=0; fahr<=300; fahr+=20)
    printf ("%5d %5d\n", fahr, 5 * (fahr-32) / 9);
}

The instruction "for" is made of 3 parts : the first "fahr=0" is only executed once. The condition "fahr<=300" is tested before each loop iteration to check if we continue to loop, and the third part "fahr+=20" is executed after the instructions to repeat, at the end of each loop.

The following version of the program uses "constants" which are variables that never change value. You will write them usually in upper case. Using a constant at the top of the program is interesting when you might want to change that value often or when it is used at several places in the program.


// table Fahrenheit-Celsius

from std use console;

void main()
{
  const int LOWER = 0;    // lower limit of temperature scale
  const int UPPER = 300;  // upper limit
  const int STEP  = 20;   // step size

  int fahr = LOWER;
  int celsius;
  
  while (fahr <= UPPER)
  {
    celsius = 5 * (fahr-32) / 9;
    printf ("%5d %5d\n", fahr, celsius);
    fahr = fahr + STEP;
  }
}


2.5 Input-Output


This program will ask you for 2 numbers and compute their sum :
// add.c

from std use console;

void main()
{
  int a, b, c;
  
  printf ("enter first number : ");
  scanf (" %d", out a);
  
  printf ("enter second number : ");
  scanf (" %d", out b);
  
  c = a + b;
  
  printf ("the sum is %d\n", c);
}

enter first number : 123
enter second number : 79
the sum is 202

The function scanf is used to input a value at the keyboard. It supports the following formattings :

  A blank character matches with zero or more white spaces to be ignored.
  A non-blank character, except a percent sign (%),
    must match exactly with the input character or the function will fail.

  Format :  "%[*][width] Type"

    *         A value is read but is not stored (there is no matching parameter).
   width      Maximum number of characters to read.

   Type                        Input
   ----                        -----
   '%'                         will read '%%'

   d   any int                 decimal number optionnaly prefixed with + or - (-61)

   u   any uint or enum        unsigned number (12)

   x   same as d or u          hexadecimal number (7fa)

   f   float or double         floating-point number (0,5) (12.4e +3)
   e   same as f

   c   char or string          fills parameter, or reads max 'width' chars.
   C   wchar or wstring        fills parameter, or reads max 'width' wchars.
   s   char or string          same as c but stops at first blank.
   S   wchar or wstring        same as C but stops at first blank.


The following program asks you to enter 3 numbers before computing their average :


// averager.c

from std use console;

void main()
{
  int a, b, c;

  printf ("please input 3 numbers : ");
  scanf (" %d %d %d", out a, out b, out c);

  printf ("the average is %d\n", (a+b+c) / 3);
}




The following program will read values and compute their average, however you will notice that we don't use scanf(). Input values are passed to the function main() during program start.


// averager2.c

from std use console, strings;

void main (string[] arg)
{
  int i;
  float f, sum, count;
  
  sum = 0.0;
  count = 0.0;
  for (i=1; i<arg'length; i++)
  {
    sscanf (arg[i], " %f", out f);
    sum += f;
    count += 1.0;
  }
  
  printf ("the average is %f\n", sum/count);
}

Here is how to start it :
C:\SAFE-C> averager2 56 129 126
the average is 103.666664

Be sure to provide at least one value, otherwise count will equal zero and this will cause a "division par zero" program crash.


With the following program you can create a file on disk and write text lines in it.


// writer.c

from std use console, files;

void main()
{
  FILE file;
  int  rc;
  
  const string FILENAME = "myfile.txt";
  
  rc = fcreate (out file, FILENAME, ANSI);
  if (rc < 0)
  {
    printf ("error: cannot create file %s\n", FILENAME);
    return;
  }
  
  fprintf (ref file, "Hi World !\n");
  fprintf (ref file, "This is a test writing into file %s\n", FILENAME);
  fprintf (ref file, "have a good day.\n");

  fclose (ref file);
}

After execution, the file "myfile.txt" will exist on your disk. If you open it with notepad, you will see that it contains :


Hi World !
This is a test writing into file myfile.txt
have a good day.




The following program will read a text file and display its content on the screen.


// reader.c

from std use console, files;

void main()
{
  FILE file;
  int  rc;
  char line[100];
  
  const string FILENAME = "myfile.txt";
  
  rc = fopen (out file, FILENAME);
  if (rc < 0)
  {
    printf ("error: cannot open file %s\n", FILENAME);
    return;
  }
  
  while (fgets (ref file, out line) == 0)
    printf ("%s", line);
    
  fclose (ref file);
}

if you execute it, you will see the following display :


Hi World !
This is a test writing into file myfile.txt
have a good day.




The following program will read a text file and count the number of "a" letters it contains.


// counter.c

from std use console, files, strings;

void main()
{
  FILE file;
  int  rc;
  char line[100];
  int  count;
  
  const string FILENAME = "myfile.txt";
  
  rc = fopen (out file, FILENAME);
  if (rc < 0)
  {
    printf ("error: cannot open file %s\n", FILENAME);
    return;
  }
  
  count = 0;
  while (fgets (ref file, out line) == 0)
  {
    int len = strlen(line);
    int i;
    for (i=0; i<len; i++)
    {
      if (line[i] == 'a')
        count++;
    }
  }
    
  fclose (ref file);
  
  printf ("number of A's : %d\n", count);
}

If you try it, you will see the following output :


number of A's : 4




It's possible to separate the parts "file reading" and "letter counting" by using a function.


// counter2.c

from std use console, files, strings;

int number_of_a (string line)
{
  int len = strlen(line);
  int count = 0;
  int i;
  for (i=0; i<len; i++)
  {
    if (line[i] == 'a')
      count++;
  }
  return count;
}

void main()
{
  FILE file;
  int  rc;
  char line[100];
  int  count;
  
  const string FILENAME = "myfile.txt";
  
  rc = fopen (out file, FILENAME);
  if (rc < 0)
  {
    printf ("error: cannot open file %s\n", FILENAME);
    return;
  }
  
  count = 0;
  while (fgets (ref file, out line) == 0)
    count += number_of_a (line);
    
  fclose (ref file);
  
  printf ("number of A's : %d\n", count);
}




It's possible to put the function in a separate file. You need then to create 3 files :


// acounter.h

int number_of_a (string line);

// acounter.c

from std use strings;

public int number_of_a (string line)
{
  int len = strlen(line);
  int count = 0;
  int i;
  for (i=0; i<len; i++)
  {
    if (line[i] == 'a')
      count++;
  }
  return count;
}

// mycounter.c

from std use console, files;
use acounter;

void main()
{
  FILE file;
  int  rc;
  char line[100];
  int  count;
  
  const string FILENAME = "myfile.txt";
  
  rc = fopen (out file, FILENAME);
  if (rc < 0)
  {
    printf ("error: cannot open file %s\n", FILENAME);
    return;
  }
  
  count = 0;
  while (fgets (ref file, out line) == 0)
    count += number_of_a (line);
    
  fclose (ref file);
  
  printf ("number of A's : %d\n", count);
}

You need only to compile the file containing the function main, the compiler will follow the "use" clauses and find the other files automatically.


to chapter 3