Books / C Language Primer for Java Developers / Chapter 2
Syntax & Practical Differences between C and Java
In the next few chapters, we’ll cover syntactical and practical differences between C and Java. None of these are particularly difficult to master, but you will need to get used to them.
In this chapter
Comments
Comments work the same way they do in Java, using //
for a single-line
comment, or /*
and */
for a multi-line comment. Since Javadoc is a
Java tool, there is no need to make formal Javadoc comments. (However,
it is always good practice to give your functions in-depth comments
that explain their use.)
y = f(x); // this is a comment
y = f(2); /* this is also a comment */
/* I'm a mult-line
comment */
Including Libraries in C
C’s equivalent to Java’s import
statements are #include
statements, that tell
the compiler where to find standard C functions that you haven’t defined
yourself.
For example, the first line of many C programs is:
#include <stdio.h>
This indicates that the compiler should include C’s standard
input/output library, including useful functions such as printf()
(which is described below). Notice that the line starts with a
hashmark, and has no semicolon at the end. This is because it is
technically a preprocessor directive, or a step that should be
followed before we begin the actual compilation. We will encounter
this again when we define constants.
Preprocessor directives are a way of making text processing with your C program before they are actually compiled. Before the actual compilation of every C program it is passed through a Preprocessor. The Preprocessor looks through the program trying to find out specific instructions called Preprocessor directives that it can understand. All Preprocessor directives begin with the # (hash) symbol. C++ compilers use the same C preprocessor. The preprocessor is a part of the compiler which performs preliminary operations (conditionally compiling code, including files etc…) to your code before the compiler sees it. These transformations are lexical, meaning that the output of the preprocessor is still text.
Other common libraries that your program might include are shown in Table 1. These .h files are called “C header” files, and they are used to compile together source code from multiple files into one working executable program. Advanced C programmers often make their own .h files, allowing a program’s source code to be spread among multiple .c files.
Header File | Use | Example |
---|---|---|
stdio.h | standard input/output | printf(), scanf() |
stdlib.h | common “standard library” functions | malloc() |
string.h | string manipulation functions | strlen(), strcmp() |
math.h | common math | sqrt(), sin(), log() |
time.h | system time & random numbers | time(), rand() |
Table 1: Common Header Files. These are some of the .h files
commonly used in an #include
directive at the beginning of a C file.
There are many more, and advanced C programmers often make their own.
Functions & Prototypes
Like in Java, a function is a self-contained area of code to perform some task, and they are the basic building blocks of a program. Because C has no classes, there’s absolutely no need to make a class block to contain the functions, like one does in Java. Further, it is incorrect to refer to C functions as “methods”, because by definition a method is inside a class.
Instead, a C program consists mostly of the #include
statements
followed by prototypes (see below), and finally all of the individual
functions.
Functions look very similar to they way they do in Java:
// this function takes 2 int arguments and returns an int int
doSomeTask(int arg1, int arg2) {
// internal code goes here
}
However C has an important rule: a function must be defined prior to its being called. That is, one function cannot call another function that is below it in the source code, since it hasn’t been defined yet. Of course Java is different: the compiler does not care about the order in which a class’s methods are defined.
This probably seems ridiculous and frustrating to modern programmers. The reason is that it makes the compiler quicker—it allows it to read through the source code fewer times while compiling the program. Given that compiling in the old days could take a minute or even longer, this is not unreasonable!
To get around this rule, every C function should have a corresponding prototype near the top of the source code. This is a definition of how to call the function, before it is fully defined. A function’s prototype looks like its signature line, but it ends with a semicolon rather than an opening brace:
int doSomeTask(int arg1, int arg2); // prototype
This prototype lets the compiler know how to call the function, even
when it’s not defined until later in the source code. You can write a
C program with no prototypes, but it’s not a good idea—a function
without a prototype can only be called by functions that occur later
in the source code. So, after your #include
statements, there should
be a master list of all your functions, one per line, followed by the
function definitions themselves.
Unlike in Java, a function that takes no arguments must have the
keyword void between its parentheses. This is because historically a
function with nothing between its parentheses meant that the
programmer wasn’t yet establishing whether or not the function took
any arguments. Putting void
in this position means that you are
positively stating that the function has no arguments.
Variable Type | Guaranteed Min Size | Usual Size | Usual Min Value | Usual Max Value |
---|---|---|---|---|
char | 1 byte | 1 byte | -128 | 127 |
unsigned char | 1 byte | 1 byte | 0 | 255 |
short int | 2 bytes | 2 bytes | -32,768 | 32,767 |
unsigned short int | 2 bytes | 2 bytes | 0 | 65,535 |
int | 2 bytes | 4 bytes | -2,147,483,648 | 2,147,483,647 |
unsigned int | 2 bytes | 4 bytes | 0 | 4,294,967,295 |
long int | 4 bytes | 8 bytes | ∼ \(−9.2 x 10^{18}\) | ∼ \(9.2 × 10^{18}\) |
unsigned long int | 4 bytes | 8 bytes | 0 | ∼ \(9.2 × 10^{19}\) |
long long int | 8 bytes | 8 bytes | ~ \(-9.2 × 10^{18}\) | ∼ \(9.2 × 10^{18}\) |
float | none | 4 bytes | ∼ \(−3.4 × 10^{38}\) | ∼ \(3.4 × 10^{38}\) |
double | none | 8 bytes | ∼ \(−3.4 × 10^{308}\) | ∼ \(3.4 × 10^{308}\) |
long double | none | 16 bytes | ∼ \(−1.2 × 10^{4932}\) | ∼ \(1.2 × 10^{4932}\) |
void | N/A | N/A | N/A | N/A |
Table 2: Basic variable types in C. Exact sizes may vary from system to system, but will be as least as big as indicated in the “Guaranteed Min Size” column.
The main()
function should return an int
. This int
should be 0 if the
program terminates normally, or nonzero (usually 1) if there was an
error.
Therefore, a typical main()
function might look like this:
// program starts here
int main(void) {
// code here
return 0;
}
Because there are no classes in C, there are no public
, private
, or
protected
keywords. There is a static
keyword, but it means
something different. (It allows a local variable to persist even when
it is out of scope.) C functions do not throw exceptions. The language
predates their common use.
Next up, we’ll look at variables in C vs Java.