C was a language developed by Bell Laboratories in New Jersey to develop their new operating system, UNIX. It is a structured language that is based on Algol. One of the advantages it has over Algol is that it has well defined input/output. Initially, C had full access to all of memory addresses on a computer so that the developers could actually go in and change the operating system code any time they thought it was necessary to do so. This is very flexible, but also can be a bit dangerous. Modern C (and other language) compilers set up a fixed space for users to run their code. For complete specifications for C, please look at the ISO/IEC 9899:TC3 standard.
Introduction
C Code is created with procedures and variables. The procedures are main() for the primary procedure in any application. All applications need a main() function. All procedures, including main, can have parameters specified within the “()” after the procedure name. All procedures have a specified return type, the default return type is void, but it is best to specify the exact return value type. The most common return type for main() is int (or integer). The most common parameters are int main(void) (no parameters) and int main(int argc, char **argv) or int main(int argc,*argv[]) (where argc is an integer that tells how many parameters/arrays/strings follow in the pointer *argv. *argv can either be written as **argv – a pointer to an array of pointers or it can be written as *argv[] – a pointer to an array of arrays or strings). C and C++ have pointers.
Note The developers of JAVA took many of the ideas from C++ but, by design, did not include pointers. This is one of the main differences between C/C++ and JAVA.
Variables
Variables are used to store information in the program, either globally or locally for a particular procedure. I personally prefer using local variables defined and used in each procedure. Those variables go away when the procedure is exited. Global variables stay around as long as the program is being executed. There are a few circumstances where global variables may be needed, but it is far better to pass the information that is needed in a function to the function when it is called by another procedure. Procedures can also pass back information via its return value, like the int(eger) return value for main. The other way information can be passed back to the calling procedure is by setting the value(s) of parameters that were specified as pass-by-reference.
Some of the main variables are as follows. To have an unsigned variable, “unsigned ” must be written in front of the variable type. “signed” may be written in front of the variable type, but it is not needed.
Name | Bits/ Bytes |
Use | Signed (Default) Values |
Unsigned Values |
---|---|---|---|---|
char | 8/1 | Smallest value in C. Default is an 8-bit integer, but is usually used to store a letter | -127 to +127 | 0 to 28-1 (or 255) |
short | 16/2 | Smallest value that is usually used as an integer | -32,767 to +32,767 | 0 to 216-1 (or 255) |
int | 16/2 | Alternate name for smallest value for integer | -32,767 to +32,767 | 0 to 216-1 (or 65,536) |
long | 32/4 | Most commonly used integer value | -2,147,483,647 to +2,147,483,647 | 0 to 232-1 (or 4,294,967,296) |
long int | 32/4 | Same as long | -2,147,483,647 to +2,147,483,647 | 0 to 232-1 (or 4,294,967,296) |
long long | 64/8 | Integer value used for large value arithmetic | -9.223372×1018 to +9.223372×1018 | 0 to 264-1 (or 1.8446744×1019) |
long long int | 64/8 | Became more prevalent with advent of 64-bit processors | -9.223372×10^18 to +9.223372×10^18 | 0 to 264-1 (or 1.8446744×1019) |
float | 32/4 | IEEE 754 single-precision binary floating-point | – | – |
double | 64/8 | IEEE 754 double-precision binary floating-point | – | – |
bool | 8/1 | Boolean | TRUE=1 FALSE=0 |
Opposite of return() values |
Compiling an Example
The standard example with which everyone starts in order to show how to program C is “Hello World.” There are enough examples out there to leave it up to the user to find one. Since I was more interested in developing code that would read from standard in (usually the keyboard) and write to a file, I decided to start with a bit more complicated code, using the stdio.h library. C has commands that will read from and write to standard in (stdin) and standard out (stdout). Because I prefer being consistent in my commands, I chose the functions that could be adapted to read from and write to either a file or standard I/O.
The code I wrote allows me to read from standard input and write to a file. If I have a problem with the code, I can turn on a debugging printout to standard error (stderr) via a compiler switch. Because I am developing code on a CYGWIN simulator on a Windows 10 system, I use GNU cc or gcc to compile my code. I prefer to use emacs to edit my code, since I can also use the emacs commands to navigate the default shell (Borne Again Shell or BASH).
Since most code is created in module, I am including a second file that contains a function that will be called by the main() program. To include this code, a header file must be created to define the function’s interface. This header must be included in both the defining file and the file that uses the function. All header files end with a “.h” suffix. This includes header files for libraries and for separately compiled objects that are linked into the final executable. Header files should not be included more than once. Therefore, I am using the tag CURRENTTIME_H to check to see if it is not already defined before I define the tag and include the header information. I will compile the file containing the function first. Notice the compilation is set up to create an object (or .o) file instead of the executable. This allows the link into the main program to be done.
The header:
/* findTime() is a function that returns a char * pointer as defined in * * the calling routine. It returns a NULL pointer if there is an error * * determining the current time. */ #ifndef CURRENTTIME_H /* Make sure header is only included once */ #define CURRENTTIME_H char *currentTime(void); #include <time.h> /* For date and time generation. */ #endif
The function
/* findTime() is a function that determines the current time and then * * returns the value as a string. A NULL string is returned for errors. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "./currentTime.h" char *currentTime(void) { time_t currentTime; size_t maxStr=255; /* Start the work of extracting the current time and putting into a string */ currentTime = time(NULL); if (currentTime == (time_t) -1) { fprintf(stderr, "Failed to retrieve the proper time"); return(NULL); } else { return(ctime(¤tTime)); /* Return proper info */ } }
The compilation to produce a .o file from the function.
gcc -c currenttime.c
The main() program
/* fprintfsample.c - shows how I can read from stdin and * * write to a file. Begin with finding todays' date and * * converting the time to a String to print. Include sample * * debug code. * * System include (header/library files) */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "./currenttime.h" /* For date and time generation. */ /* Define the possible Return values for success of failure */ #define rFAILURE -1 #define rSUCCESS 0 /* Start of main, set up to return a pass/fail flag */ int main(void) { FILE * fp; char *str, *currentTimeStr; size_t maxStr=255; size_t numMem=1; int n=1; fp = fopen ("file.txt", "w+"); /* Open the output file */ currentTimeStr = currentTime();/* Obtain the starting time */ if (currentTimeStr == NULL) { fprintf(stderr, "Failed to retrieve the proper time"); return(rFAILURE); } #ifdef DEBUG1 fprintf(stderr, "%s %s\n", "Today's start is ",currentTimeStr); #endif /* End of DEBUG1 */ fprintf(fp, "%s %s\n", "Today's start is ",currentTimeStr); /*Now ask for input to add to file */ fprintf(stdout,"Type a phrase you want printed to file.txt\n"); fprintf(stdout,"To end input, enter a CR on a blank line.\n"); while(n>0) { fgets(str, maxStr, stdin); n=strlen(str)-1; #ifdef DEBUG1 fprintf(stderr, "strenth length %d\n", n); fprintf(stderr, "%s", str); #endif /* End of DEBUG1 */ if (n>0) { /* Only print the string if there is actual data */ fprintf(fp, "%s", str); } } currentTimeStr = currentTime();/* Obtain the starting time */ if (currentTimeStr == NULL) { fprintf(stderr, "Failed to retrieve the proper time"); return(rFAILURE); } #ifdef DEBUG1 fprintf(stderr, "%s %s\n", "Today's end is ",currentTimeStr); #endif /* End of DEBUG1 */ fprintf(fp, "%s %s\n", "Today's end is ",currentTimeStr); fclose(fp); /* Close the open file */ return(rSUCCESS); /* Return a pass flag */ }
Then the code can either be compiled without the debug statements included:
$ gcc -o fprintfsample fprintfsample.c currenttime.o
or with the DEBUG1 flag included to include your debugging code.
$ gcc -o fprintfsample -D DEBUG1 fprintfsample.c currenttime.o
Please notice that the -D DEBUG1 has the command-line option “-D” capitalized. For gcc, this is critical. Otherwise there will be a compile error. Once you have compiled the code, then you can run it.
$ ./fprintfsample.exe
Under development, I do leave the .exe suffix on the file. When I go into production, the file will be renamed to remove the suffix. Seeing the .exe reminds me that this code is code under development. To ensure the desired file is executed in the current directory, the reference to the program must be started with “./”. The shell variable $PATH could also be changed to include the current (.) directory. However, I explicitly look for the executable in the current directory so that I only start programs in this directory when I want to do so. Now, to see the content of the output file, use the “more” command.
$ more ./file.txt
Reasons for Certain Coding Choices
In my coding, I want to be as consistent as possible, whether I am designing and coding a function or subroutine or I am designing and coding the main program. Here is a list of some of the things I do and why.
- return() vs exit() – I choose to use return() for all “exits” from all functions, including main(). This makes me use one function consistently. I have worked with engineers who did not follow this practice, and always used exit() when an error occurred in their procedure. exit() ends the entire procedure, which means that when a called function uses exit(), the calling routine is not allowed to do any clean-up after the error exit.
- return vs return() – As you can tell, I like consistency. Therefore, I will use return() as I would any other function.
- Defining return values as #define statements in the code or included header file. Again, consistency is the key. If return values are defined in a header file, everyone who uses that header file will have consistent return values. This makes coding across a larger group more consistent. All developers will know what to expect.
- fprintf vs printf – printf is strictly for writing to stdout. Again, I like consistency across all code. Therefore, I use fprintf so that I can specify where I want to write the output, whether it is to a file or stdout or stderr.
- fgets vs fread – fread will read until an end of file (^D is encountered. fgets reads until an end of line is encountered. Since I am reading a line-by-line stream from stdin, I prefer to use fgets.
- Using pointers – I wanted to re-familiarize myself with pointer and arrays and how they are used in C. I probably need a complete section on this, because it is a vital concept to writing good C code.
- { – Believe it or not, where to put the opening { on a section of code has been a big controversy. I personally do not care and would go along with whatever the standard of the group is. If left to my own preferences, I would put the { on the same line as the code line that caused it to be needed. This allows me to not take up an extra line and thus allows more of the code to be visible on the display.
- main() vs main(VOID) vs main ( int argc, char *argv[] ) – The choice depends on if you want arguments passed into your C program. When I accept no arguments, I prefer including the VOID parameter to remind me that there are no arguments wanted for the program. If there are arguments needed, then the last choice is the most common way to allow a variable amount of arguments to be accepted by the program. Often, a programmer will test the number of arguments be passed into the program by checking the value of argc. Sometimes the number of arguments is fixed, so if argc is anything different the program can return(ERROR) to the caller. If the program can receive a variable number of arguments, then this is a way to know how to assign the arguments to the proper variables for use in the program. For variable number of arguments, there are usually default values for each argument, so the program knows what to do if not all arguments are passed into the program.
Testing
Testing is an important part of all code verification. For Agile programming, it is a critical part of the process. At first, running test scripts is OK. The more robust the script is, the better the test will be. Eventually, all tests need to be automated so that regression tests can be run at any time. For the code above, I will run a manually test. The problem that needs to be solved is that the output file will have a date and time stamp of exactly when it was run. It is best to require no visual inspection of the actual results, unless a test is flagged as having failed. At that point, a visual inspection of the results of the test is required to determine what went wrong and develop a solution to fix it. For this test, I developed an input file titled
testinput.src
It contains:
This is the test input file. What I type in this file should match the output file of the program. Have enough lines to make a difference. Include a few numbers, like 2001 and 2016. Since the output will have a date at the top, a special comparison is needed for the first line. Now, try it again and again and ONE last time. Special characters might also be included: !@#$%^<>&*() Make sure there is a blank line at the end to exit the program.
To run the program to include this as input, I run:
$ ./fprintfsample.exe < testinput.src
I then look at the difference of the input and output files, and should receive a result like:
$ diff file.txt testinput.src1,2d0 < Today's start is Thu Jun 16 16:05:51 2016 < 14d11 < Today's end is Thu Jun 16 16:05:51 2016
I expect the date line differences. It appears that I have two empty lines in the input file. I would go back into my testinput.src file and remove the second blank line. There are ways to check the first line for the proper format. That will be shown the next time I edit this file.
Conclusion
Hopefully, this has been helpful. I am enjoying writing this site. Let me know if there are some examples you would like.
Subroutines and Arrays
I was asked in Quora to show how to average a six element array. I like to make the code as general as possible, so I created a subroutine to handle the averaging. According to the literature I have read, the number of elements must be calculated by the calling program. There are at least two ways around this. The first is to put a terminator value at the end of the string. The second is to go through the list and check for an exception for when the loop goes past the end of the array. Neither is really elegant, so I have left this for the calling routine to calculate the number of elements an array has and then pass that information into the subroutine.
// AvearageListCnsl.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include#include #include /* Routine that takes an array of intergers and find the average */ double take_average(double* list, int elements) { // According to literature, sizeof(list) does not work in subroutine with list being referenced by a pointer. //int elements = sizeof(list)/sizeof(list[0]); double avg, total = 0; for (int i = 0; i < elements; i++) { total += list[i]; } printf("The list has %d elements with a total of %f\n", elements, total); avg = total / elements; return(avg); } /* Main routine*/ int main() { double list[6] = { 1,2,3,4,5,6 }; int i, elements = sizeof(list) / sizeof(list[0]);; //double list[6] = { 67.3, 75.6, 86.4, 34.8, 99.7, 76.6 }; printf("The list has %d elements\n", elements); double avg = take_average(list, elements); printf("The average of the list is: %f\n", avg); scanf_s("%d", &i); return 0; }