Friday, November 22, 2013

K&R C SE Lesson 1: Introducing K & R C Programming SE

(Post Under Construction)
 Kernighan and Ritchie "The C Programming Language" Second Edition is an classic interpretation of the C language in the same way as Strunk and White "Elements of Style" is a classic interpretation of the English language or Wolfgang Pauli's lecture on "Wave Mechanics" is a classic representation of the math of physics. This does not mean that all of the programming in K&R C is useful today or even the best representation of the use of C (1, 2).  A paraphrased statement of Pauli's famous belief about untestable physics applies to engineering software as well: If your code can't be proven wrong by someone, you have probably not produced anything of much value. 

Software engineering languages have variability, flexibility and polymorphism  by design  There are always multiple solutions to any given problem. Without honoring this fundamental nature of exploratory science; the power and endurance of any one language is limited. There are a number of  K&R solution sets provided on the web. Below is a selection of solutions to  the Fahrenheit-Celsius converter code discussed in Chapter 1 of  "The C Programming Language" as provided by Richard Heathfield.   A C language reference is available from the GNU Project. It is particularly important to read the while and do statement sections. A number of third party GNU C tutorials are also available. Use the google search term: 'filetype:PDF gnu c programming tutorial'. Learning GNU C is one such.

This post is primarily about understanding the logic and control structures of C. The implementation of C library functions (e.g. printf) are left for another discussion. -RMF
As I compile and run these programs on my X11 terminal, I use vi to paste the solutions into my console. Once I open vi with the name of the file I want to create, I take the following steps:
  • type "i" for insert
  • press the middle or left mouse button to paste the code into the console
  • press the ESC key
  • type ":wq" to write and quit the file
  • then use gcc to (link and) compile with a similar file name and the debug option "-g"
Running the binary gives me the output below. Note: Any elipses ('...') in my writing designates truncation of output or code. Note: The solutions below use code that is different than some of the original K&R solutions. 
vi FAHR_001.c
gcc -g FAHR_001.c -o FAHR_001.exe
 ./FAHR_001.exe
F     C
  0  -17.8
 20   -6.7
 40    4.4
 60   15.6
 80   26.7
100   37.8
120   48.9
... 
The first three examples from our K&R solution set use a while loop to step through the conversion algorithm before printing the resulting table. By looking at the table below, we can see how each program is structured to bring a different type of result:


FAHR_001.exe FAHR_002.exe FAHR_003.exe
F C C F C F



0 -17.8 0 32.0 300 572.0
20 -6.7 20 68.0 280 536.0
40 4.4 40 104.0 260 500.0
60 15.6 60 140.0 240 464.0
80 26.7 80 176.0 220 428.0
100 37.8 100 212.0 200 392.0
120 48.9 120 248.0 180 356.0
140 60.0 140 284.0 160 320.0
160 71.1 160 320.0 140 284.0
180 82.2 180 356.0 120 248.0
200 93.3 200 392.0 100 212.0
220 104.4 220 428.0 80 176.0
240 115.6 240 464.0 60 140.0
260 126.7 260 500.0 40 104.0
280 137.8 280 536.0 20 68.0
300 148.9 300 572.0 0 32.0


When we examine the output we can see that FAHR_001.c is designed to start at 0 degrees Fahrenheit and step by 20 degrees F until reaching 300 degrees. The corresponding Celsius equivalent is returned at each pass through the while loop. These lines in the code 

fahr = lower;
while(fahr <= upper)

set logical conditions that provide for a lower and upper limit to the conversion routine. Every step through a while loop adds the additional step amount (20) to the variable fahr with this line:

fahr = fahr + step;

until that upper limit is reached.


/* FAHR_001.c */
#include <stdio.h>

int main(void)
{
  float fahr, celsius;
  int lower, upper, step;

  lower = 0;
  upper = 300;
  step = 20;

  printf("F     C\n\n");
  fahr = lower;
  while(fahr <= upper)
  {
    celsius = (5.0 / 9.0) * (fahr - 32.0);
    printf("%3.0f %6.1f\n", fahr, celsius);
    fahr = fahr + step;
  }
  return 0;
}

The second program (FAHR_002.c) inverts the conversion algorithm to start at Celsius 0; adding all equivalent Fahrenheit temperatures with every pass through the while loop. To do this, the program sets a lower and upper Celsius limit, then adds the step amount to each new accumulating Celsius variable:

celsius = lower;
while(celsius <= upper)
...
celsius = celsius + step;

/* FAHR_002.c */
#include <stdio.h>

int main(void)
{
  float fahr, celsius;
  int lower, upper, step;

  lower = 0;
  upper = 300;
  step = 20;

  printf("C     F\n\n");
  celsius = lower;
  while(celsius <= upper)
  {
    fahr = (9.0/5.0) * celsius + 32.0;
    printf("%3.0f %6.1f\n", celsius, fahr);
    celsius = celsius + step;
  }
  return 0;
}

The third program (FAHR_003.c) is an exact copy of the previous program but for three lines that invert the numerical order of the Celsius to Fahrenheit conversion, making it return the upper limit of the conversion at the top of the table. Can you find those three lines? 

/* FAHR_003.c */
#include <stdio.h>

int main(void)
{
  float fahr, celsius;
  int lower, upper, step;

  lower = 0;
  upper = 300;
  step = 20;

  printf("C     F\n\n");
  celsius = upper;
  while(celsius >= lower)
  {
    fahr = (9.0/5.0) * celsius + 32.0;
    printf("%3.0f %6.1f\n", celsius, fahr);
    celsius = celsius - step;
  }
  return 0;
}

As an exercise I changed the Celsius limits to something more usefulfor the temperatures common in Bellingham, WA. Note that I had to change the datatype for the bounds variables:


  float fahr, celsius;
  float lower, upper, step;

  lower = -20;
  upper = 50;
  step = .33;

Since Celsius gradient is less granular than Fahrenheit there are three corresponding F temps for each Celsius integer if the step is set to .33:
...
 -2   28.1
 -2   28.7
 -2   29.3
 -1   29.9
 -1   30.5
 -1   31.0
 -0   31.6
  0   32.2
  0   32.8
  1   33.4
  1   34.0
  1   34.6
  2   35.2
  2   35.8
  2   36.4
...

The for loop delivers considerable more functionality than the while loop. Please read the appropriate section in the C language reference.  A for loop incorporates upper and lower bounds and the step inside the initial expression and then uses a program block (the code inside the brackets - {...}) to execute the conversion routine.

/* FAHR_004.c */
#include <stdio.h>

int main(void)
{
  float fahr, celsius;
  int lower, upper, step;

  lower = 0;
  upper = 300;
  step = 20;

  printf("C     F\n\n");
  for(celsius = upper; celsius >= lower; celsius = celsius - step)
  {
    fahr = (9.0/5.0) * celsius + 32.0;
    printf("%3.0f %6.1f\n", celsius, fahr);
  }
  return 0;
}

/* FAHR_005.c */
#include <stdio.h>

/* print Fahrenheit-Celsius table */
int
main()
{
     int fahr;

     for (fahr = 300; fahr >= 0; fahr = fahr - 20)
             printf("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));

   return 0;
}

No comments:

Post a Comment