We copied
The #include lines at the top are similar to an import in Java but they differ significantly. In C, the #include is a pre-compilation directive that instructs the compiler to replace (cut and paste) the #include line with the text from the indicated file. Thus, a #include increases the size of your compilation source since it causes (much) more code to be compiled. In Java, however, the import statement merely adds the file to the list of places to search when attempting to resolve a symbol.
The main function has some similarity to Java since, like a Java program, a C program must have exactly one main function (now that we are in C, we refer to what you know as methods as functions from now on). The difference is that while each class in a multi-class Java program can have its own main method, and you indicate at run-time which class has the main method that you wish to start from (and all the others are never executed), in C there can only be one main function anywhere in the set of files that are compiled and run. Main returns an int, but the utility of this returned value is mostly moot for our purposes since we do no script programming which might examine and use the value returned by main. We will defer our explanation of argc and argv until we study pointers, strings and arrays in a few lectures. Suffice to say, they need to be there, just like the signature for main in Java (public static void main (String[] args)).
Lastly, we copied, ran, and tinkered with
The char type is an 8-bit (usually) signed quantity that stores the ASCII code for a character. Thus a char is just an 8-bit int (signed/unsigned is compiler-dependent). Sometimes a char or array of chars is used to store plain numbers when the range of values is certain not to exceed 8 bits. I would not write such code!
Acceptable casting between types occurs in a manner similar to Java (but with no Class cast exceptions!). But assignments between dissimilar types is permitted and is not caught by the compiler. In particular, you can assign a double to an int and no one will care (the value will just be truncated). You should, however, make any casts explicit, i.e.,
double d; int i = (int)d;
There is NO boolean type in C. Anywhere an expression is expecting a boolean value, such as a conditional test, the value 0 is interpreted as false and ALL other values are interpreted as true.
printf("Please enter an integer: "); and printf("x= %d y= %d\n", x, y);In the first printf, we supply a string literal. What you see is what you get. In the second call to printf, we pass in a format string, followed by two actual arguments (the names of our variables, x and y). The format string contains the literal "x= " followed by a placeholder for an int, %d, followed by literal " y= " and another placeholder for an int. At the end of the format string is an escape character, \n, which is the newline char. After the format string come 2 arguments, the value of x and the value of y. For each % placeholder in the format string there must be a corresponding argument supplied after the format string. Your code may compile and run if you omit those arguments but the behavior is unpredictable (welcome to C!).
To read data from the keyboard our first C program uses:
scanf("%d", &x);And this where things get interesting. Note the ampersand character (&) before the x variable identifier in our call to scanf. This ampersand character is the address-of operator. This operator, when placed before a variable identifier, produces the address of that variable, rather than the value stored in the variable. This address value is a positive memory address between 0 and however much memory you have in your computer. scanf needs the address of of x and not the value of x because scanf wants to know where to store the numeric conversion of the string you typed into the keyboard.
Recall that when we just want the value of x we just use the name x in a statement such as:
x = 15; // assign a new value into x or y = x + 5; // look up the value of x or printf("value of x= %d", x ); // look up the value of x
This is the first time we have ever been concerned with the address of where a variable is stored in memory. Java intentionally shields us from any such concerns. C does not. Thus it is important to understand the distinction between the value of a variable, and the address of a variable. They are not the same! If we want the address we must put the & operator immediately to the left of the variable name.
scanf is a value-returning function. It returns the number of successful conversions it makes. If your format string has three % format placeholders, then scanf will return a number between 0 and three inclusive depending on how many good values the user provides. Unlike Java, scanf will not crash or throw any kind of exception if you enter your firstName where a number was expected. It will simply fail to convert that string to a number and fail to store any new value at the address specified! But the program will continue merrily along!
Later on, when you run lims.c, notice the different formats used in the various printfs, such as %d, %li, %u, %e. These are format specifiers that serve as placeholders for the values of the actual arguments of type decimal integer, long int, unsigned int, float (and double) respectively. Look in the K&R textbook on page 154 for a good summary of the data types and format strings used in printf (and its sibling functions such as fprintf, sprintf, etc.).
For now, let's modify demo1.c to prompt for x and y on the same line. Also, let's add some code to report the number of successful conversions. Then we'll run it a few times entering good and bad values.
Compilation: gcc -ansi -pedantic -Wall -Wextra demo1.c
(if successful, produces a.out, an executable binary file)
To do this, we'll pop out of emacs by using ctrl-z to pause that process. Once we compile our code, if there are no compilation errors, we can run the binary that's produced. If there are errors, we need to go back into emacs (fg), fix the errors and try it again. We can also compile from within emacs by using ctrl-x ctrl-e.
What's with those compilation options (flags)?
Execution: ./a.out (executes the binary file)
The ./ before the a.out is needed as it stands
for the current directory (otherwise your execution path would be searched for
a file called a.out to execute and would likely not be found). You can modify
your .login file to append . to your path.
C (unlike Java) is platform-dependent. Thus we should test code by compiling and executing on several platforms. This practice is a heuristic for maximizing our chances of discovering bugs in our code - particularly those dealing with dynamic memory and pointers. It is very common for your program to appear to run perfectly on one platform but crash (when I test it) on another. Students typically respond with "But it ran fine under Linux!", or, "It ran fine under Cygwin!" Such a response is naive and demonstrates a lack of understanding that differing platforms (or even the same platform under different runtime conditions) simply reveals flaws in your code - not flaws in the compiler or the OS. Simply put - you got lucky on Linux but a different OS caught you.
The gcc compiler is usually installed on all UNIX platforms. These gcc compilers are more modern versions which support C and C++ code. In this course will we emphasize coding standards that are specific to strict C and forbid C++ features as described above.