Embedded

View Original

Embedded Wednesdays: The Basic Structure of C Code

Hello, World! You’ve probably heard about the Hello World program. It is used to test the installation of the C compiler. This is what Hello World looks like:

#include <stdio.h>

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

If all goes well, this program prints “Hello, World!”.

In previous posts, I’ve said that computers are really stupid, they execute simple instructions, one at a time, in a strict order. The instructions are set by the chip manufacturer, and the order of the instructions is set by the person writing the programs. The computer slavishly follows the script set up by the programmer.

When I was first starting out in computer science, my first professor, Dr. Adams, gave us a simple question:

You have two variables, X and Y. How would you exchange the values of X and Y?

Oooh, oooh, I know:

X = Y

Y = X

A good answer, but incorrect. Let’s see what that does.

Assume that we initially have:

See this content in the original post

Executing the instruction X = Y means X gets the value of Y. This is called an assignment statement. We are giving, or assigning, the value on the right of the equal sign to the value on the left of the equal sign.[1] So we now have:

See this content in the original post

Clearly, this is not the answer to the ultimate question of life, the universe, and everything, the 42 has been wiped out.

Since both X and Y are now 17, executing the instruction Y = X doesn’t do anything useful.

The proper method of exchanging X and Y is:

Temp = X

X = Y

Y = Temp

We start with the same initial state but with an extra variable (values are underlined when they change:

See this content in the original post

First instruction: Temp = X

See this content in the original post

Second instruction: X = Y

See this content in the original post

Third instruction; Y = Temp

See this content in the original post

And now, X and Y have exchanged values. Three instructions, executed one at a time, in a strict order, from the top-down.

C works the same way, assignment goes left to right, code executes from the top down, and the designers apparently hated to type, so there are a few decorations that we have to live with.

Stupid Syntactical Sugar

What does syntactical mean? A language’s syntax is the set of rules that determine if a phrase is properly formed. Just wise goodly formed sentence englishes!

Syntax does not give you the meaning of a phrase, only that it is well formed.

1   #include <stdio.h>
2
3   void main( void ) {
4      printf( “Hello, World!\n”);
5   }

Here are some syntax rules using Hello, World! as our example.

  • Your program will begin with a call to a subroutine called “main”, as you can see on line 3 of our example.

  • A C program is made up of single statements or groups of statements. A single statement is ended with the semicolon character ( ; ). Groups of statements are enclosed in curly braces “{“ to begin and “}” to end. You can see the semicolon at the end of line 4, and the curly braces on lines 3 and 5.

  • C programs are sent through the preprocessor before the compilation process begins. The preprocessor copies in extra code, triggered by the #include command, and does text replacement using the #define command. You can see the #include command on line 1. The preprocessor does a bunch of other work, but you can investigate them later.

  • C code is kept in files using filenames that end with the suffix “.c”. Note that this is not a capital C, it is lowercase. C code that should not use the preprocessor uses the .i suffix. Header files use the .h suffix and contain information about the associated .c file.

  • You should put comments into your programs to explain the purpose of the program and how any non-obvious parts work. These comments are for your colleagues, and more importantly, for your future self. A comment starts with the sequence “/*” and ends with “*/”. Using the sequence “//” you mark the rest of a single line as a comment.

SUGGESTIONs for C programs

  • Your subroutine “main” should be placed in a file called main.c. Much like web servers using the file index.html, where you can find the home page, we conventionally use main.c so everyone can find the main subroutine.

  • If a .c file has a .h header file, the two should share a common name, such as spi.c and spi.h. This pair of files deal with all aspects of the SPI.

  • Groups of statements, enclosed in curly braces, are indented for the benefit of the programmers.

  • A set of statements can be separated into a subroutine, or function.

  • A C program can be broken into multiple .c files. By thinking of your program as a set of cooperating modules, each taking care of its own portion of the system, the whole program becomes more manageable.

  • When naming variables and subroutines, use names that describe their purpose.

  • To find a subroutine easily, prefix its name with the name of the module that it’s in. For instance, the subroutine ChecksumInit is a part of the checksum system, and it can be found in the file checksum.c and uses the header file checksum.h.

Your program, as stated above, is made up of statements. There are only three kinds of statements:

  1. Statements that assign a value to a variable

  2. Statements that call a subroutine

  3. Control statements with direct the flow of your program

The following is a short piece of code to give you an example that shows comments, as well as the basic structure of a C program.

/*
 * This program is intended to show the basic structure of a C program. 
 * It takes the form of a program to calculate the exclusive OR 
 * checksum of a GPS data packet.
 *
 * This is a multi line comment. The stars at the beginning of each 
 * line are there for show, and they indicate where the comment starts 
 * and ends.
 *
 * The first portion of the program is the #include statements that 
 * tells the preprocessor to bring in some code from stdio.h to support 
 * the printf function, and from stdint.h to define int32_t, uint32_t, 
 * and uint16_t. strlen.h gives us the strlen function, and stdlib.h 
 * gives us the value EXIT_SUCCESS.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

/*
 * Next, we declare a character array (string), that holds one  
 * “sentence” from a GPS. Following this data is a delimiter ‘*’, 
 * followed by the checksum, a calculation done on the characters in 
 * the sentence that is used to detect any transmission errors in the 
 * data. The sentence is prepended by the ‘$’ character, but only the 
 * characters between the $ and the * are used in the checksum 
 * calculation.
 */

/*
 * $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
 */

const char vals[] = "GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W"; // Checksum should be 6A

/*
 * C programs start with a call to the subroutine called main. Main 
 * returns a value that can be used to judge if the program ran 
 * successfully or encountered an issue. In this case we always return 
 * a code indicating success.
 */

int32_t main(void) {
   uint32_t checksum;   // A holding spot for the checksum values
   uint16_t i;          // A loop counter
   checksum = 0;        // Initialize the checksum to zero.

   /*
    * The following is a looping structure that executes the statements
    * enclosed by the curly braces. The loop is initialized by setting
    * i = 0. It continues while the condition “i < strlen(vals)” is 
    * true. At the end of each loop, the instruction i++ is executed, 
    * incrementing the value of i. strlen(vals) returns the number of 
    * characters in the string called val.
    */

   for (i = 0; i < strlen(vals); i++) {
      checksum = checksum ^ vals[i];     // This line is indented
   }                                     // The } lines up with the for
   printf("%X\n", checksum & 0x000000FF);

   return( EXIT_SUCCESS);
}                                    // The } lines up with the int32_t

Next week we'll take a look at the various commands to control the execution flow of C programs.


[1] The C compiler will complain about rvalues and lvalues. An rvalue is a value that can go on the right ( r ) of an equal sign, and an lvalue is a value that can go on the left ( l ) of an equal sign.

You can say  val = 4 + 5;  as val is a variable and can go on the left of the equal sign. It is an lvalue.

You cannot say  4 + 5 = val; as “4 + 5” cannot accept val’s value.   “4 + 5” is an rvalue and can only exist on the right side of an assignment.


This post is part of a series. Please see the other posts here.


By Tosaka [CC BY 3.0 (http://creativecommons.org/licenses/by/3.0)], via Wikimedia Commons