Introduction

The compiler is responsible for processing a textual representation of algorithms and data, and changing it into a form that can be processed by the computer.  In the process, other tools such as linkers and librarians are used to manage large code bases and apply specific transformations required for an operating system to load the program.  This documentation will primarily focus on aspects of the C language, and will generally avoid discussion of such other tools.  Requirements imposed by operating systems will also be outside the scope, as will various extensions to the language which are available in this compiler.  This document goes to some effort to distinguish the difference between the 1999 version of the standard and previous versions.  Finally, this documentation is not meant to accurately reflect the terminology in the standards.  Certain leeway has been taken for explanatory purposes.

Before code is compiled, it is preprocessed.  The  preprocessor  strips  comments , determines which code is to be  conditionally compiled  and which code is to be ignored, and allows for macro expansion.  There are also compiler-specific instructions called  pragmas  which are handled by the preprocessor, as well as other miscellaneous source-code control functions.

At the outermost level, the compiler processes  declarations .  A declaration declares some type of data.  This can be a  variable  to store values in, a definition of an abstract  type , or a  function  declaration.  

Simple abstract types can be used to rename basic types.  More complex abstract types can define pointers or arrays, or gather simpler types together in a structure or union.  In general, an abstract type is always based on some combination of built-in types and other abstract types.

A function can be further broken down into  statements , which are a sequential listing of actions to take, and  expressions , which evaluate some mathematical quantity and optionally assign the result into a variable.


Preprocessor

The preprocessor controls the flow of source code into the compiler.  It starts by stripping  comments  out of the code, replacing  trigraphs  with their equivalent, and  concatenating lines  as necessary.  As it executes it evaluates various preprocessor directives, and performs  macro expansion .  Macro expansion is a primary use for the preprocessor.  It allows a textual substitution of one sequence of characters with another, and in this respect is similar to an editor search-and-replace function.  The textual substitution is however somewhat more powerful, allowing definition of macros as pseudo-functions with arguments.

Preprocessor directives occur when the first non-space character on a line is '#'.  The directive extends to the end of the current line, unless  line concatenation  is used.

The most often-used preprocessor directives are the one used for  including  the contents of another source or header file, and the ones used for macro expansion.  

Another often-used set of preprocessor directives involves  conditional compilation .  Conditional statements evaluated by the preprocessor are used to programmatically determine what part of the source code the compiler sees, and what part it ignores.

There are a couple of preprocessor directives which control part of the text of the  diagnostic messages  issued by the compiler.  Finally, there is a preprocessor  pragma  directive, which is used to relay compiler or platform-specific information to the compiler.
Declarations

The compiler starts by looking for declarations.  A declaration creates some type of data, such as a  variable  or  function .  It can also be used to create an abstract  type , which is itself later used to create variables, data, or other types.

Declarations that are not types have a scope and a linkage.  The scope of a declaration can be file, parameter, block scoped, or member.  The first declaration read by the compiler is always file scoped.  The linkage can be static, global, auto, register, extern, or member.   An addition found in the 1999 standard is an inline linkage that may be used with function declarations.

A file-scoped declaration can be used anywhere in the part of the file after the declaration is encountered, or in subsequently included files.  A parameter declaration is one of the listed parameters in a function.  A block-scoped declaration is somewhere within a function body.  A member declaration is a member of a structure or union type.

The linkage of a declaration determines some aspects of the storage for the declaration.  Static linkage persists, but is not visible outside the file or function it is declared in.  Global linkage is visible in other modules.  External linkage means no space is allocated; this is a directive to the linker that the space allocation will be found in another file.  Auto linkage is only found in function bodies; it means the declaration is allocated in non-persistent storage, each time the function is called.  This allows for recursive functions.  Register linkage is like auto linkage except it serves as a hint to the compiler that the value should be stored in a register.  Most modern compilers will silently ignore register linkage and calculate an optimal use for registers. Member linkage means the declaration exists within the scope of a structure or union definition.  A record of the amount of space is required but no actual space will be allocated until the type is used to create a variable.

As an example:

extern int moduleScopeExternalLinkage;
static int moduleScopeStaticLinkage;

int moduleScopeGlobalLinkage;

int moduleScopeExternalLinkedFunction();

struct _mystructTag {
	int memberScopeMemberLinkage;
};
int moduleScopeGlobalLinkedFunction(int parameterScopeAutoLinkage)
{
	static int blockScopedStaticLinkage;
	int blockScopedAutoLinkage;
}

In C, declarations inside a function body were originally restricted to the beginning of a block.  However, the 1999 version of the standard relaxed this restriction and allowed declarations anywhere within a block.
Variables

Variable declarations define some type of data.  The data may consist of some numeric value, a character, a string, an enumeration, or an entity of some aggregate type such as a structure or union.

The variable declaration has three parts: a base  type , qualifiers, and a variable name.  Optionally, the variable can be assigned an  initial value  as part of the declaration. 

The base type must be specified within a function body, but may be omitted at file scope.  When this is done, the base type defaults to 'int'.  Note that the 1999 standard disallows this defaulting of the base type to int.  

Qualifiers are optional and specify things like whether the variable is a pointer or array.  The variable name is used throughout the code as a reference to the allocated storage.

If a variable is not declared with an initial value, the initial value depends on the scope of the variable.  File scoped uninitialized variables are always initialized to zero before the program runs.  Block scoped uninitialized variables are considered to hold an undefined value.  Parameter scoped variables are generally initialized by the function call and may only become uninitialized in the absence of prototypes.  Member scoped variables are simply type declarations, and have no value until the enclosing structure or union type is used to create a variable.

A simple declaration is as follows:

int myInteger;

This declaration has different scope depending on where it is found.  For example if it is at file scope, this is a global declaration, visible from other modules.  If it is inside a function, its scope is the remainder of the block which it is found in.  If it is declared as part of a structure, its scope is the same as the instance of the structure it is referenced from.

Some scopes may be explicitly specified.  For example:

static int myPersistentStaticInteger;
extern int myExternalInteger;
auto int myAutoInteger;
register int myRegisterInteger;

The first is persistent storage.  It has static linkage, meaning it cannot be seen outside the function or file it is declared in.
The second is a reference to a variable found in another module.
The third is an explicit way of declaring variables within a function.  Normally the 'auto' keyword is omitted, and in the 1999 version of the standard its use is deprecated.
The fourth is a way of hinting that the variable should go in a register.  It may only be used within a function.

The above definitions simply define storage for an integral variable of standard size.  Several values may be declared on the same line:

int myInt1, myInt2, myInt3;

When declaring several variables on the same line, type modifiers may be specified with each variable.  For example with the following there is one basic type but each declared variable has different modifiers, making the variables actually have different types.

int myInt, *myIntPointer, myIntArray[10], volatile tickCount;
Initial Value

When a variable is declared at file or block scope, it may be given an initial value.  For example:

int treeCount = 5;

declares the variable treeCount and gives it an initial value of 5.   If this variable is an automatic variable, the value is reinitialized every time the statement is encountered, e.g. each time through a looping  control statement  in which it is  declared  and initialized.   If it isn't an automatic variable, the variable is preinitialized exactly once, before the program starts. 

Note that any uninitialized automatic variable has an undefined value until something is assigned to it.  However, uninitialized variables that are not auto variables are automatically preinitialized to zero, exactly once, before the program starts.

Initial values for automatic variables may consist of any expression of compatible type.  In that sense an initializer for an automatic variable is similar to the combination of an assignment statement and a declaration.  However initializers for file scoped variables variables must be  constant values , or  expressions  that may be evaluated to a constant value at compile time.

Const-qualified integers are treated specially by the compiler:

int const NumberOfStations = 10;

is not assigned any storage, but instead the value ten is used wherever NumberOfStations is referenced.  This is similar to macro substitution of constants, but has the advantage that the compiler knows the type of the constant.

Pointers may be initialized at either file or block scope:

int myInteger;
int *p = &myInteger;

Arrays may be initialized as long as they are not automatic variables.  Note: in the 1999 standard automatic variables of type array may be initialized via  type casting .

int myArray[5] = { 1, 2, 3, 4, 5 };

In this case the outer set of braces is mandatory, however, if an array of multiple dimensions is initialized internal braces may optionally be omitted.  This is generally not a good idea, because the array initializer does not need to completely define the array contents.  Values between the last content and the end of the array are initialized to zero.  For example these declare the same data:

int myArray[5] = { 1, 2 };
int myArray2[5] = { 1, 2, 0, 0, 0 };

An array may be declared with the last dimension left empty if an initialization is going to occur, or in an external statement.  If such an array is initialized, its size reflects the number of elements actually initialized, in the following example space is allocated for three elements.

extern int myArray[];
int myArray[] = { 1, 2, 3};

In the 1999 version of the standard, array values may be specified in an arbitrary order, e.g. these declare the same data:

int myArray[5] = { [4] = 4, [2] = 2, [1] = 1 };
int myArray2[5] = { 0, 1, 2, 0, 4 };

Here the number in braces is the index to be initialized.  In the case that one value is specified in an arbitrary order and the next is not, the second specification occurs at the next linear point in the array.  e.g. for the following the values referenced by the 3rd and 4th indexes are set to 2 and 7 respectively.  Unspecified elements are set to zero.

int myArray[5] = { [2] = 2, 7 };

Structures may be initialized as long as they are not automatic variables.  Note: in the 1999 standard automatic variables of type structure or union may be initialized via  type casting .

struct _fruit {
	enum _type {apple, orange}; type;
	enum _color {red, green, yellow} color;
	int size;
};

struct _fruit myApple = { apple, red, 4 };

If any structure initializations is missing members at the end, they are filled with zeros.  The following declare the same data:

struct _fruit myApple = {apple, red };
struct _fruit myApple2 = {apple, red, 0};

When an initialization occurs for a variable with a type that has nested structures, the inner braces are not required, however, it is a good idea to use them because it clarifies what is going on.  And again, since they structure initializer does not need to enumerate all elements there could be problems if an element is skipped and there aren't braces.

In the 1999 standard, structure elements may be arbitrarily specified, in any order in an initialization:

struct _fruit myApple = { .color = red, .size = 2, .type = apple };

In the case that one value is specified in an arbitrary order and the next is not, the second specification occurs at the next listed element in the array, e.g. the following specifies the color and size.  Again, unspecified elements are set to zero.

struct _fruit_myApple = {.color = red, 4 };

Unions are initialized like structures, however, all values in the union share the same space, so only one value may be stored at a time.  Generally another mechanism is used to determine which member is being used, such as wrapping the union in a structure.  For example:

union value {
	int i;
	float f;
	complex c
	imaginary i;
};

declares a union which can be one of several types of numbers.

When initializing a union variable, only one member may be initialized.  The first member listed  in the union definition is the member that will be initialized:

union value myValue = { 4 };

In the 1999 standard, any one of the members may be selected to be initialized by using the same nomenclature as declaring structure values explicitly:

union value myValue = { .f = 4.3e20 };

It is possible to assign a value to a function pointer which is not an automatic variable:

int myFunc();
int (*myPtr)() = myFunc;

Arrays of these may be declared:

int (*myPtrArray[]) = { func1, func2, func3, func4 };

A string pointer is a special case.  It may be initialized as follows:

char *myGreeting = { "Hello World" };

which allocates a string, then initializes the myGreeting variable with a pointer to it.  The braces are not required in this case.  Also, such initializes may occur for automatic variables.
Types

All declared variables must have a type.  A type is used to govern the kinds of operations that are valid for the variable, as well as to provide hints to the compiler how to reserve space and process usage of the variable.  For example arrays may be indexed, pointers may be dereferenced, integers may have operations applied to them, and the compiler may need to generate different types of code for integers and floating point variables.

The compiler comes with a set of basic types.  The basic types can be extended through  aggregation , by adding modifiers, or with the  typedef keyword .

The basic types may hold differing amounts of storage depending on a compiler and platform.  This basically means that some times can store larger numbers and or have greater precision than other types.  Following is a list of basic types, with examples of using modifiers to create new types.

Character types:
char
unsigned char

Integer types:
short
unsigned short
int
unsigned int
long
unsigned long

Floating point types:
float
double
long double

Another type, void, is used to signify that a function returns no value, or to declare a pointer to a generic (non typed) entity.

In addition the 1999 standard introduces the _Bool keyword, the _Complex keyword, and optionally the _Imaginary keyword.  These types are spelled with an underscore to keep from name clashes with C++ compilers, but generally a header is included to make them more readable.  A further pair of integer types is also defined:long long and unsigned long long.

For example the system header stdbool.h declares the bool type, along with true and false, while the system header complex.h declares complex and imaginary. The types added by the 1999 standard are thus:

Boolean types:
bool

Integer types:
long long
unsigned long long

Imaginary Types:
float imaginary
double imaginary
long double imaginary

Complex Types:
float complex
double complex
long double complex

Each type may be modified with one or both of the const or volatile keywords.  const tells the compiler variables of the type won't change once initialized.  volatile tells the compiler the variables of the type may change due to external circumstances, and the compiler should be careful with it.  

The 1999 standard also adds a modifier restrict which may be used with pointers with parameter scope.  This modifier is used to indicate that the pointer will not point to any value that another parameter which is a pointer points to.  It helps compilers choose a better optimization for code within the function.

For example:

unsigned short const
double volatile

Additionally, types may be modified with pointer or array specifiers.  

A pointer is an indirect reference to another variable.  Dereferencing a pointer references the value of the other variable.  For example:

int *

is a pointer type;

double **

is a pointer to a pointer, and so forth.

A special case is a pointer to char:

char *

which is often used in the run-time library to denote a string of characters.  Generally, such strings will be terminated by a null character.

An array is a linear list of variables of the specified type.  For example:

int somename[5];

declares somename with a single dimension 5, e.g. it is a linear list of 5 integers.  The dimension on the array ranges from 0 to 4, or one less than the dimension specifier.  These specifiers may be mixed, for example:

long double **somename2[3][2];

const, volatile, and restrict may be freely mixed with pointers, In which case the specifier goes with the type to the left.  In the following example const goes with the long double, or *** somename.  And volatile goes with long double ** or **somename.

long double const *volatile** somename;

Sometimes an array declaration can be left with the last dimension unspecified:

int somename[];

This can happen when  initializing  the array, or if the array is declared with external linkage.  In the 1999 standard, it can also happen for arrays that are at the end of  structure  definitions.  In this case the structure can be arbitrarily extended, if memory has been allocated for it.  This is useful for storing variable-length character strings, for example.

In the 1999 standard, arrays are further extended for the possibility of Variable Length Arrays, or VLAs.  VLAs don't have a fixed size, but instead have a size which is generated on the fly by evaluating each dimension as an expression.  For example:

void myFunc(int m)
{
	int somename[m];
}

Such variables may only be placed in block or parameter scope.  At block scope, the dimension may be any expression, but at parameter scope, the dimension has to be one of the previously defined parameters.  For example:

void myFunc(int m, int arr[m]);

In special cases it may not be convenient to declare the size of the array as an earlier parameter.  In such cases a special syntax is used:

void myFunc(int arr[*]);

In this case the array has size equivalent to whatever is passed, and it is up to the author of the function to find a way to learn how long it is.  A pointer to a variable length array may be declared:

int (*arr_ptr)[m];

a variable length array may be typedef'd, in which case the typedef has a fixed size based on the size of the VLA as it would have been if it had been declared at the time of the typedef:

typedef int VLA_XXX[m];

Another often-used type is the pointer to function.   The below is a pointer to a function which has one parameter of type double and returns an int.

int (*functionPtr)(double x);
Functions

A function declaration is an entity capable of doing some type of sequential processing.  It has a prototype which declares the name, return type, and parameters, and optionally a body.  The prototype declares any return type, as well as the list of parameters to the function.  The body is composed of  declarations ,  statements , and  expressions  and defines actions to be taken when the function is invoked from another function.  In C there is a root function  main()  which gets executed automatically when the program starts.  Other functions are then invoked to make up the program.

The return type in a function body can be any basic or abstract type, except another function or an array.  The return value of a function can however be a pointer to another function.

Each parameter in the parameter list is declared similarly to any other declaration  A subtle difference is that in a normal declaration, the comma separator allows multiple variables with the same basic type to be declared, whereas in parameter lists the comma separator simply separates one parameter from the next.

This documentation will not attempt to explain the original K&R style of function prototypes, although it is supported by this compiler.  Prototypes are used for two purposes.  First, a prototype may be immediately followed by a function body to declare the execution of the function.  Second, a prototype may be a standalone entity used by other files so that the compiler may check for type mismatches.

An example of a function prototype is:

int getNumberOfCars(struct list *cars);

which has a single parameter which is a pointer to a list of cars, and returns a count of items on the list.  likewise the following has two parameters:

int displayFruit(char *name, struct fruit *fruit);

whereas

void ShutdownDrive(void);

is a function which has no parameters and returns no value.

struct fruit getLastEatenFruit();

is a function that isn't fully prototyped, but returns a fruit structure.

int printf(char *s, ...);

declares a function whose first parameter is a character string, and the ellipses(...) indicates an unspecified (variadic) list of parameters following it.  The method for determining the number of passed arguments is dependent on the purpose of the function.  For example the argument list could be null terminated, or the first unspecified argument could be a count, or the string could somehow specify the number of arguments.  This function returns some integer value.  The macros in stdarg.h can be used to extract the unspecified arguments, if necessary.

A function may optionally have a body.  Assuming struct list has a member next which points to the next linked item in the list we could write:

#include <stdlib.h> /* definition of NULL */
int getNumberOfCars(struct list *cars)
{
	int returnValue = 0;
	while (cars != NULL)
	{
		returnValue = returnValue + 1;
		cars = cars->next;
	}
	return returnValue;
}

Note that in C, parameters are reserved separate space from the original passed variable.  This means they can be used freely as local variables, without modifying the original value.  However if a pointer or array is passed and then dereferenced, it may be used to modify the same data the original pointer or array would access when dereferenced.  A statement can be prevented from accidentally modifying such data by use of the const keyword in the parameter declaration.

In the 1999 standard, function declarations may be qualified with the inline linkage type.  For example:

inline int incrementBirdCount()
{
	return ++birds;
}

If the compiler can, it will take such functions and instead of making them external standalone entities it will simply compile them in line in the current function.  For example:

while (watchingBirds())
{
	if (seenBird)
		printf("I've seen %d birds", incrementBirdCount());
}

will be as if the compiler was compiling:

while (watchingBirds())
{
	if (seenBird)
		printf("I've seen %d birds", ++birds);
}

However, the inline qualifier is not specified to absolutely require such behavior.  A compiler is free to take inline functions that are too complex and compile them as separate entities.  Also, even when a compiler honors the inline linkage, the fact that the function is recompiled in line every time it is used means the overall program may get a little bigger.  In general, inline functions should be kept simple because of these factors.
Statements

A statement is the a basic unit of execution.  Statements can be blocks containing other statements, evaluate arbitrary expressions, assign values to variables, or transfer control to a different place.

A block is statement, which is a list of other statements.  It is most often used in conjunction with control statements, to group the actions to be taken as a result of the control statement.  A block is surrounded by braces.  For example:

if (bSwitchedOn)
{
	countOfOnSwitches++;
	TurnOnLight();
	TurnOnFan();
}

In this case countOfOnSwitches++ is an  expression  being evaluated.  This particular expression has a side effect which makes it useful.   Evaluating an expression such as b+5 without an assignment could be done although it would not result in any type of meaningful result.  TurnOnLight() and TurnOnFan() are also expressions being evaluated.

Historically, any number of  declarations  may occur within a function body, but they must occur at the beginning of a block.  The 1999 standard relaxes this constraint and allows declarations to occur anywhere within a function body.  However in some cases, as with  VLAs , certain statements such as goto are not allowed to branch to a point within a block after the variable has been declared.  And in any case using goto to branch around declarations with  initializers  is problematic as the initializers will not be executed.

A value may be assigned to a variable within a statement or declaration at block scope:

int result, a = 7, b=3+a;
result = b + 5;

Pointers may be dereferenced as part of an assignment:

char *p = &myString;
(*p) = 'C';

Note that assignments themselves return values, so that:

int a,b,c = 5 ;

a = b = c;

sets both a and b to be equal to 5.
Expressions

Expressions are used to perform arithmetic operations.  Built-in  operators , combined with  variables  and  constants , may be use to evaluate an equation and store the result in a variable.  Expressions may also be use to form the selector in a  control statement , or to create an  initializer  for a variable.

An expression has a  type .  It may be converted from one type to another using  type casting .  Type casting may be used for example to truncate an integer or affect the precision of a floating point value.
Macro Expansion


Macro substitution is a text-based substitution of one set of characters with another.  Once a macro is defined, and any instance of the text of the macro name is replaced with the text of its definition prior to compilation. This is similar to the search-and-replace function of an editor.  However the macro expansion done by the preprocessor is somewhat more substantial.  It allows that a macro can be defined with arguments, which themselves will be replaced in the text to be substituted.  A preprocessor directive is used to define a macro, as follows:

	#define RED 3

defines the macro RED to be substituted with the text '3'.  For example if a later  statement  is like this:

	appleColor = RED;

The preprocessor would expand the statement to:

	appleColor = 3;

before handing it over to the compiler.  This is a common way to give untyped constants names.  Note: often macro names are written in upper case like this.

A more complex function-like macro might look like this:

	#define MUL(left, right) left * right

so that one could write:

	result = MUL(dollars, 100);

and the preprocessor would expand the statement to:

	result = dollars * 100;

before handing it to the compiler.

Note that the definition of MUL above is not quite adequate, because the preprocessor will faithfully substitute any text given in an argument to the macro argument.  For example if one wrote:

	result = MUL(a+b, c+d) * e;

the preprocessor would expand it to:

	result = a+b * c+d * e;

which does not have the desired meaning because of  operator precedence .  To be safe macros taking arguments should use parenthesis liberally, for example a better definition of MUL would be:

	#define MUL(left, right) ((left) * (right))

and the expansion of the above statement would be:

	result = ((a+b) * (c+d)) * e;

which is the desired result.

Macros may be undefined again.  For example:

	#undef	RED

will cause subsequent uses of RED to not be replaced.

In the 1999 C standard, macros have been extended to allow variable-length argument lists.  For example:

	#define printerror(text, ...)  fprintf(stdout, text, __VA_ARGS__);

could be used as:

	printerror("%d",errno);

to generate:

	fprintf(stdout, "%d", errno);

In this case ... means there is an unspecified number of arguments to follow, and __VA_ARGS__ means copy all remaining arguments to this point in the substitution.

There are a few macros that are always defined.  These macros can be used to detect standards conformance, or to get information about the entity being compiled.  They are as follows:

__FILE__	The name of the file being compiled, as a string.
__LINE__	The line number of the current line, as a number.
__DATE__	The date the file is being processed, as a string.
__TIME__	The time the file is being processed, as a string.

__CCDL__	The LADSoft compiler is being used. No value.
__386__	This is an x86 compiler. No value.
__i386__	This is an x86 compiler.  No value.
__STDC__	The value 1, this compiler is ANSI compliant.
__STDC_VERSION__	The numeric value 199901L, only defined when compiling in C99 mode.
Conditional Compilation

Conditional compilation is used to control what sections of source code actually get compiled.  It is possible to use conditional compilation directives to include some sections of the file, and exclude others.  Often this is done based on some configuration, e.g. including alternate functionality based on a compiler or operating system.  Many included files also have include guards, which are implemented with conditional compilation in case the included file accidentally gets parsed twice.

Configuration can be done through macros.  When they are expanded they guide the conditional compilation directives

#if	some constant  expression 
	evaluates the expression, and compiles the following sequence of statements if it is true.
#ifdef	MACRONAME
	if MACRONAME has been defined with a #define statement, the following sequence of statements is compiled
#ifndef MACRONAME
	if MACRONAME has not been defined with a #define statement, the following sequence of statements is compiled
#else
	switch the compilation mode: if the previous sequence was being compiled, the next sequence will not be
	if the previous sequence was not being compiled, the next sequence will be.
#elif	some expression
	combines #else and #if
#endif
	ends a conditional statement

As part of the expression for a #if or #elif, the keyword defined may be used.  For example:

#if defined(RED) && defined(BLUE)

compiles the following code sequence if the macro RED has been defined and the macro BLUE has been defined.

Note: while the #else normally swaps the compilation mode, this will not happen if the #else is nested in a higher-level directive pair that is not itself being compiled.

As an example:

#ifdef RED
	int color = RED;
#else
	int color = GREEN;
#endif

creates a variable color.  If RED has been previously defined with the #define directive, color is declared and set to RED, else color is declared and set to GREEN.
Pragma Statements

Pragma statements are directives to the compiler.  They can mean almost anything, and except for a standard pragma space defined by the 1999 standard, the definition is at the whim of the compiler designer.  Therefore this documentation will not go into detail about the things that may be done with pragma statements.

The basic definition of a pragma statement is:

	#pragma any text here

If the compiler recognizes the text, it will parse it and do something.  What it will do is dependent on the text and the compiler.  However, the 1999 standard specifies that if the first word is STDC, the directive is now reserved as part of the standard.  Currently, there are three such pragma statements:

#pragma STDC FENV_ACCESS OFF
#pragma STDC CX_LIMITED_RANGE OFF
#pragma STDC FP_CONTRACT ON

The final value of each can be either on or off; defaults are given above.

The FENV_ACCESS pragma, when on, indicates that the user may have gone around the compiler run-time system and changed settings in the floating point controller.  This is used in case the compiler needs to generate additional code to compensate.

The CX_LIMITED_CONTRACT pragma, when on, allows the compiler to assume that certain intrinsic functions such as multiply, divide, and absolute values of complex numbers can be done with a limited range.  This allows the compiler to choose a faster algorithm, on the assumption that intermediate results will not go out of range for the complex type being used.

The FP_CONTRACT pragma, when on, allows the compiler to calculate the result of constant expressions involving floating point numbers at compile time.  Turning it off can be useful to control the precision of the result.

Each of these pragma values, when used inside a function, is restored at the end of the current block.

In the 1999 standard, pragma values may also be invoked with a statement within a function body:

	_Pragma("STDC CX_LIMITED_RANGE_ON");
Preprocessor Diagnostic Directives

Diagnostic directives are used to change the characteristics reported for the file.

	#line	10, "filename.c"

causes the compiler to think it is at line 10 in filename.c, and increment lines from there until either the file is exhausted or another #line directive is encountered.  This effects diagnostics issued by the compiler, and also modifies the results of the __LINE__ and __FILE__  macros .

	#error some user error

causes the compiler to issue a severe diagnostic with the text 'some user error'.  This error will cause the compile to fail. It is usually used inside a  conditional compilation  directive, to signify that some type of configuration needed by the program is not correct.  For example if several macro definitions within a configuration file are in an inconsistent state #error could be used to notify the user.
Include Files

The include file directive is used to include the contents of one file into the current file being translated.  The file may be some system file that holds definitions of RTL or WIN32 functions, or it may be a project file that holds  macros  and  declarations  used in several different places.  When a file is included, all macros and declarations in the included file become visible within the file that performed the conclusion.

The general form is either:

#include <filename.h>

or

#include "filename.h"

The difference between the two forms isn't well defined, and in fact in this compiler they are very similar.  However, the basic idea is that the <> form is to be used for system files, for example the files that came with the compiler.  And the "" form is to be used for project-specific files.  In general it is a good idea to stick to this usage as well as possible, to maintain portability to other compilers.
Comments

A comment is a section of the file meant to augment source code with human-readable text.  Any text inside a comment is simply ignored by the compiler.  However there are some types of documentation tools which will parse specific text sequences within comments to make documentation, and other types of tools such as linting tools which look for keywords in comments to guide the tool's progress.

In the C language, comments were originally denoted like this:

/* This is a comment block */

/* This comment block
 * spans multiple lines
 */

where a comment extends from the /* to the */.   It is undefined as to whether comments can be nested.  Some compilers allow it and some don't.  It isn't generally a good idea to nest comment blocks.

The 1999 standard added the form:

// This is a comment line

Which extends a comment from the // to the end of the current line.  Many compilers were already supporting this prior to adoption of the 1999 standard.
Line Concatenation

Line concatenation may be used to concatenate several lines into one long string.  It may be used for example to make a long macro definition as follows:

#define MYFUNC(mybool) { if (mybool) { \
					printf("true"); \
				 } \
				 else { \
					printf("false"); \
				 } \
			     }

Where the backslash character is used to concatenate lines.  This is necessary with macro definitions since a preprocessor directive ends at the end of the current line.  However line concatenation may be used anywhere in a source file.
Trigraphs

Trigraphs are a long-hand representation of certain characters not always found on non-english keyboards.  They can be used to enter these characters, when no key sequence exists for them.  Trigraphs always start with two question marks.  The trigraphs are as follows:

Trigraph		ASCII Character
??(		[
??)		]
??<		{
??>		}
??/		\
??=		#
??!		|
??'		^
??-		~

Trigraphs will be substituted anywhere in the source file, including within  string constants .
Operator Precedence

The operator precedence determine what orders the operators are evaluated in when multiple operators appear in constant expressions.  The following table shows the precedence of each operator from highest to lowest.  Operators with the same precedence are listed in the same row.  Each operator is evaluated in the context of the associativity specified in the table.


Operator	Associativity
--------------------------------------------------------------------------------------------------------------------------------
(expr)    [index]    ->	Left ==> Right
!    ~    ++    --    (type)    sizeof   Unary operator:    +    -    * &	Right <== Left
*    /    %	Left ==> Right
+    -	Left ==> Right
<<    >>	Left ==> right
<    <=    >    >=	Left ==> Right
==    !=	Left ==> Right
Binary operator:    &	Left ==> Right
Binary operator:    ^	Left ==> Right
Binary operator:    |	Left ==> Right
&&	Left ==> Right
||	Left ==> Right
expr ? true_expr :  false_expr	Right <== Left
+=    -=    *=    /=    <<= &=   ^=    |=   %=   >>=    =	Right <== Left
,

According to these rules, the expression

a = b + c * d;

is evaluated as if it were written:

a = ( b + (c * d));

As an example of associativity:

b * c * d;

is evaluated from left to right as if it were written:

(b * c) * d;

whereas

b = c= d ;

is evaluated from right to left, as if it were written:

b = (c = d);
sizeof operator

The sizeof operator may be used in a number of ways to get the size of an entity.  The two most used methods get the size of a type, or the size of a variable.  However, within a function body sizeof will get the size of any expression.  Care needs to be taken with this as sizeof is evaluated at compile time; if there are side effects in an expression within a sizeof operator, they will never be executed.

Everything sizeof returns is in units of sizeof(char).  sizeof(char) is defined to be one.

For example:

int size = sizeof(int);

gets the size in characters of the int type.

int size = sizeof(struct fruit);

gets the size of the structured type tagged as fruit.

int size = sizeof(TYPEDEFFED_TYPE);

gets the size of a type that was declared with typedef.

int size = sizeof(void *) ;

gets the size of an untyped pointer;

int size = sizeof(myVariable);

gets the size of a variable.

Sometimes sizeof does not need parenthesis. In general, if it is a variable or basic type the parenthesis aren't necessary..

int size = sizeof int;
Type Aggregation

Type aggregation is used to combine a set of types into a larger type.  The three types of aggregation in the C language are struct, union, and enum.  An aggregate type definition consists of the associated aggregate keyword, an optional tag, and the declaration of the type.  Once the type is declared, it may optionally be used to declare variables immediately.  Such variables can optionally have initializers.

The tag is used to access the aggregate type.  It is in a different space than normal names, so for example the following declarations will not collide:

int _colors;
enum _colors;

enum gathers related integer constants together, giving them a name and value.  For example:

enum _colors { RED, GREEN, BLUE};

declares an enumerated aggregate with a tag _colors, which may have one of the values RED, GREEN, or BLUE.  These are mapped to integers starting with 0, so RED = 0, GREEN = 1, BLUE = 2.  The enumeration constants may be given explicit values:

enum _colors { RED, GREEN=10, BLUE};

When a mapping to an integer is specified, subsequent values will be indexed starting with the value.  For example in the above, RED=0, GREEN=10, BLUE=11.

A variable may be declared immediately during the declaration, or by using the tag:

enum _colors { RED, GREEN, BLUE} rgbVar;
enum _colors rgbVar;

struct gathers a list of variables into a larger type.  Within the larger type, space will be reserved for each member variable in the order the variables are listed.  For example:

struct fruit {
	enum _fruittype {apple, orange} type;
	enum _colors color;
	int size;
};

has three variables - the first is the type of fruit, the second is its color, and the third is its size.

Structures can hold any other type, including other structures, and arrays.  In the 1999 version of the standard, the last element of a structure may be a dimensionless array:

struct myStruct {
	int size;
	char data[];
};

The size of such as structure as reported by sizeof() does not include any size for the data member.  However, if the structure is dynamically allocated the array may be any required length.

Unions are declared like structures, however, all values in the union share the same space, so only one value may be stored at a time.  Generally another mechanism is used to determine which member is active, such as wrapping the union in a structure.  For example:

union value {
	int i;
	float f;
	float complex c
	float imaginary i;
};

declares a union which may be one of several types of numbers.

structures and unions may be declared without a body:

struct fruit;

This allows them to be referenced before they are declared, or in situations where a full declaration is not necessary.  This also allows circular references between two or more structures:

struct _auto {
	struct _tires *tires;
};

struct _tires {
	struct _auto *owner;
};

However the structure declaration can only be used to declare pointers unless it has been given a body.

An added feature of structures and unions is that bit fields may be declared within them.  For example the following defines a structure with three bit fields.  The first holds a 3 bit value, the second holds a one-bit value, and the third holds a 4-bit value.

struct _myBitFields {
	int field1 : 3;
	int field2 : 1;
	int field3 : 4;
};

From a standards viewpoint the only basic type a bit field may have is int.  However, compilers that take advantage of processor-based instructions to actually pack these fields within a single machine word sometimes allow the use of other integer types to specify a machine-specific data sizes.  However, such packing is beyond the scope of the standard and inherently non portable.
Typedef Keyword

The typedef keyword is used to create a type with a new name.  In some cases it is used to simply give a generic name to a built-in type; in other cases it is used as a shorthand to a more complex type.  The typedef has two parts; a type, and a name.  Neither part is optional.

In general, the type declaration is the same as a variable declaration, except that instead of allocating storage the name given may be used as an alias for the type, when creating other types or declaring variables.

For example:

typedef unsigned U32;

declares the type name U32 to be equivalent to unsigned.

typedef unsigned *U32_ptr;

declares the type name U32_ptr to be equivalent to unsigned *.

typedef void (*MYFUNC_ptr)();

declares a pointer to a function that has no parameters and returns no value.

typedef void MYFUNC(int a);

declares a function prototype and so on.

In each case a variable of the type may be defined simply by using the name in the typedef as the type.  For example:

MYFUNC_ptr funcptr;

declares a pointer to a function using the previous type definition.
Constants

There are several types of constants in the C language.  For example:

40, -40 are decimal integer constants.

0x65 is a hexadecimal (base 16) unsigned constant because it has a leading '0x'.

047 is an octal (base 8) unsigned constant because it has a leading zero.

'&' is a character constant for the ampersand character.
L'&' is the wide character version of the above.

"hello world" is a string constant.  It implicitly has extra storage for a trailing null character.
L"hello world" is a wide character version of the above.

4.0, -0.0437, 10.77e-22 are floating point constants.
In the 1999 version of the standard, 0x1.5p23 is also a floating point constant, specified in hexadecimal.

'I' as defined in complex.h, is the constant imaginary 1.  It may be combined with real values to create complex and imaginary values.

Numeric constants can have various suffixes to specify the size of the constant:  These can be specified in either lower or upper case.  For example:

7UL

is the unsigned long constant 7.  

By default integer constants are of int type.  The suffixes for integers are as follows:

U		- unsigned
L		- long
UL		- unsigned long
LL		- long long
ULL		- unsigned long long

By default floating point constants are doubles.  The suffixes for floating point values are as follows:
F		- float
L		- long double.

Functions and variables declared at file scope have a fixed location, and this location is a constant.  Additionally, the location of a statically-linked variable within a function body is a constant.  The location of auto variables and parameters are never constant.
String Constants

String constants are made up of a combination of ASCII characters and control characters.  Each control character is signified by the character '\' followed by a control character specifier.  The sequence "\\" is used as a representation of the plain ASCII character '\'.  Each string constant has enough space reserved for an additional null terminator.

Control characters may also be used in character constants.

Control characters are as follows:

\a	beep
\b	backspace
\f	form feed
\n	line feed
\r	carriage return
\v	vertical tab
\t	horizontal tab
\'	the ' character.  Used when ' is to be a character constant: '\''
\"	the " character.  Used when " is to be embedded in a string constant
\?	the ? character. Used to prevent trigraphs from being contracted
\x	an embedded hexadecimal value.  For example \x24 is the '$' symbol.
\0-7	an embedded octal value with three digits.  (the 1999 standard allows for less than three).   For example \044 is the '$' symbol.

an example is:

printf("\a\044Error\x24\nHi");

which beeps and prints the following message:

$Error$
Hi
main

The function main() is the first function called in a standard C program.  It is given a list of command line arguments, and returns a value back to the OS for external processing.  Every program must have a main.  (Note, windows Gui programs and dlls somewhat break this standard). The prototype for main is:

int main(int argc, char *argv[]);

main returns a value, which is often handed off to some operating system command processor for external use.  The constants EXIT_SUCCESS and EXIT_FAILURE in stdlib.h give basic return codes, however, other codes can be used.

The argument argc gives the number of command line arguments, and argv is a null-terminated array with pointers to the arguments.  Note that the first argument is the name of the program which is running.   So argc will always be one or greater and the first actual command line argument is stored in argv[1].
Control Statements

There are several types of control statements.  Many types of control statements take a selecting  expression , which governs the use of the statement e.g. by choosing between selections or setting exit conditions for a loop.  Conditional statements may be used to choose an action, or between two or more sets of actions based on a selection.  Looping statements may be used to repeat an action until some condition is met.  Transfer statements simply transfer control to another point in the program.

There are two types of conditional statements.  These are the if statement and the switch statement.  The if statement is used to conditionally select an action or choose between two alternative actions, whereas the switch statement is used to choose between an arbitrary number of actions.

There are three types of loop statements.  The for statement is usually used to loop through a known quantity, such as an array or linked list.  The while statement and do-while statement are used for more generic loops which have a boolean exit condition.  The difference between the while statement and the do-while statement is that in the while statement, the exit condition is evaluated at the beginning of each loop, whereas in the do-while statement the exit condition is evaluated once at the end of each loop.  This means a while statement can be shorted circuited by setting the exit-condition false before entering the loop, whereas a do-while statement is guaranteed to execute at least once.

There are a variety of transfer statements.  The goto statement transfers control to an arbitrary point in the function, by accessing a label placed in the function.  The break and continue statements are used in loops; break means to exit the loop whereas continue means go to the beginning or end of the loop and re-evaluate the selecting expression.  The break statement is also used in conjunction with switch statements, to delimit one set of actions from the next.  The case statement is used in conjunction with switch statements, to specify constant selection values associated with actions.  Finally, the return statement is used to exit the current function, and optionally specifies a value to be returned to the caller.
If Statement

The if statement takes an  boolean, integer, or pointer as a selecting  expression .  It controls access to one statement.  An optional else clause is executed if the first statement is not executed.  

If the value of the selecting  expression  is true, nonzero, or non null, the statement or block is executed.  Otherwise, the statement or block is not executed, and if an else statement immediately follows, that is executed instead.

For example:

if (stoplightIsRed)
{
	StopCar();
}
else
{
	StartCar();
}

This example may also be written without the block scoping:

if (stoplightIsRed)
	StopCar();
else
	StartCar();


Switch Statement

A switch statement uses a selecting  expression  to choose between an arbitrary list of actions.  In a switch statement, the selection must be an integer or enum type.  Case statements nested within the switch statement serve to choose between the selector values, and an optional default statement is used to catch any values that have not been explicitly listed in case statements.  For example:

enum _colors { RED, YELLOW, GREEN } currentLight;
	...
switch (currentLight)
{
	case RED: /* if currentLight == RED */
		StopCar();
		break;
	case YELLOW: /* if currentLight == YELLOW */
		SlowDown();
		break;
	case GREEN: /* if currentLight == GREEN */
		KeepGoing();
		break;
	default: /* if currentLight == any other value */
		printf("currentLight has an unknown value");
		break;
}

Note that the selection values in case statements must be integer or enumerated constants.
For Statement

The for statement is a looping construct often used for iterating through arrays or lists.  It may be used for any iterative looping that has a known exit condition.  An example follows which fills an array with sequentially increasing numbers:

int i;
int myArray[10];
for (i=0; i < sizeof(myArray)/sizeof(myArray[0]); i++)
{
	myArray[i] = i;
}

In the for statement three  expressions  are listed.  The first expression is a comma separated list of values to initialize.  In this case we are initializing the single variable i to 0, which is the first index in an array.

The second expression is the selecting expression.  As long as it evaluates to true, nonzero or non null the loop will keep executing.

The third expression is a comma separated list of expressions to evaluate at the end of the loop but before the selecting expression is evaluated.  In this case, we are simply incrementing the index into the array.

In the 1999 standard, the first expression may also serve as a declaration.  For example the following increments i and decrements j until j is less than i, and fills each array element with a value.

int myArray[10];
for (int i=0, j= 10; i <= j; i++, j--)
{
	myArray[i] = (i + j)/2;
	myArray[j] = myArray[i];
}

Any of the three expressions in the for loop may be omitted.  If the second expression is omitted, the loop has no explicit way to exit.  Sometimes infinite or never ending loops are written as:

for ( ; ; )
{
	do something here
}
Do-While Statement

The do while statement repeatedly executes a loop.  After each execution of the loop, the selecting  expression  is checked.  As long as it evaluates to true, nonzero or non null the loop will keep executing.

do
{
	BlinkChristmasLights();
} while (!IsSunrise());

which means, start by blinking the Christmas lights, and continue until it is sunrise.

While Statement

The while statement checks the selecting  expression .  If it is false, nonzero, or non null, the loop isn't executed.  Otherwise the loop is executed, and at the beginning of each execution of the loop the exit condition is checked again.  For example:

while (IsDark())
{
	BlinkChristmasLights();
}

This checks to see if it is dark, and if so the Christmas lights blink until it is not dark any more.
Goto Statement

The goto statement transfers control to a specific point in the function.  For example:


while (IsDark())
{
	if (PowerFail())
		goto exit;
	BlinkChristmasLights();
}
exit:

where exit is a label control is to be transferred to.  When a power fail occurs, the loop is exited.  

Goto statements don't have to be used with loops, they can be placed anywhere inside a function.

In many cases the goto statement can be avoided with judicious structuring of other control statements.  In this particular case there are two alternate ways of writing the statement:

while (IsDark() && !PowerFail())
{
	BlinkChristmasLights();
}

or 

while (IsDark())
{
	if (PowerFail())
		break;
	BlinkChristmasLights();
}
Break Statement

The break statement is used to transfer control out of a loop.  For example:

while (IsDark())
{
	if (PowerFail())
		break;
	BlinkChristmasLights();
}

When a power fail occurs, the loop exits and there are no more attempts to blink the lights.

Continue Statement

The continue statement is used to transfer control back to the selecting  expression  of a loop.  It may be used to ignore the following statements in a loop and restart execution of the loop.  In the case of for loops, the continue statement also executes the third expression before checking the selecting expression.

The following stays in the loop if there is a power failure, but doesn't try to blink the lights during a power fail.  However it resumes blinking the lights when the power comes back - as long as the loop hasn't exited from a lack of darkness.


while (IsDark())
{
	if (PowerFail())
		continue;
	BlinkChristmasLights();
}

The following prints an index value if a condition is true:

for (i=0; i < 10; i++)
{
	if (!Exists(i))
		continue;
	printf("%d\n",i);
}
Return Statement

The return statement is used to exit a function.  If the function was declared to return a value, the return statement also specifies the value to return.  The returned value may be any  expression  with a  type  that is compatible with the return type found in the function prototype.  If the function is prototyped with a return value of void, the return statement may not return a value.

For example, the following returns the number of flowers from 0 to 20, or -1 if there are more than 20 flowers.

int GetNumberOfFlowers(struct _flower * flowers)
{
	int count = 0;
	for ( ; flowers; flowers = flowers->next)
	{
		if (count > 20)
			return -1;
		count++;
	}
	return count;
}
Operators

Operators are built-in functions that take one or two arguments, perform an operation on the arguments, and return a result.   Arithmetic operators  perform basic math functions and work with integer and floating point values.    Bit wise operators  work with Bit wise logical data.   Logical operators  work with boolean values.   Shift operators  work with powers of two.  Assignment operators  are used for assigning results to variables.   Equality operators  test whether the values of two variables are equal.   Relational operators  are used to compare the relative values of two quantities.   Pointer operators  are used for assigning and accessing the values that pointers reference.   Member operators  allow access to the members of structures and unions.   Auto increment operators  is used to add or subtract one from a value.  The  hook operator  is used to embed the equivalent of a simple  if statement  inside an expression.  The  comma operator   is used to evaluate several expressions sequentially.  The  sizeof operator  is used for determining the size of an entity.  Finally, function calls are used to evaluate more complex quantities.

a simple expression which assigns a value to a variable is:

a = b + c;

Operators may be aggregated into larger expressions, for example:

a = b + c * d;

The order in which operators are evaluated is determined by the  operator precedence ; in the above example the multiplication occurs before the addition.  The precedence may be modified or clarified with parenthesis.  The following example causes the addition to occur before the multiplication:

a = (b + c) * d;

The operands to any given operator may themselves be larger expressions:

a= (b + c + d) * (e + f + g);
Type Casting

Type casting is used to convert an  expression  of one  type  into an expression of another type.  In the 1999 standard, it may also be used as the basis for initialization of structures and arrays.

There are two types of type casting: implicit casts and explicit casts.  Implicit casts are casts done automatically by the compiler.  For example in the following assignment there is an implicit cast from the integer to the floating point variable:


float a;
int b;
a = b;

In the following case if a out of the range that a char can hold, it will automatically be truncated before being stored in b:
char b;
int a;

b = a;

Other implicit casts may occur with various operators.  In the following a and b are both cast to int before doing the addition, then cast back to short.  This does make a difference because if the sum of a and b is too large to store in a or b,  we still get an accurate result.  If the casting were not done prior to the operation, the result would be a truncated value.  In general, if an operator takes two operands both are cast either to the size of the larger operand, or to int, whichever is larger.

unsigned char a,b;
short c;

c = a + b;

The other type of casting is explicit casts.  In this case the program specifically states what type to use.  An explicit type cast is any type specifier, surround by parenthesis.

For example in:

long double a, b, c;

a = b + c;

will perform both operations in the long double type.  But in the following the addition will be done in long double, then explicitly converted to float, then implicitly converted back to long double.  Assuming the result wasn't too big to fit in a float, this has the effect of reducing the precision of the result.

a = (float)(b + c);

Types may also be cast up.  In the following b is explicitly cast to long double, forcing c to be implicitly cast to long double.  The subtraction is then done in the long double type, and the result is implicitly converted back to float before storing it in a;

float a,b,c;

a = (long double)b - c;

When the type specifier would normally require an identifier to name a variable, the identifier can be left out:

void (*myFuncPtr)() = (void (*)())NULL;

Type casts may also be used to convert a pointer of one type to a pointer of another:

char *ch_ptr;
struct _fruit *fruit_ptr;

ch_ptr = (char *)fruit_ptr;

Historically such explicit casting of pointer types hasn't been imperative, although it clarifies the code and removes warnings from the output of some compilers.  It may become more of an issue in a future standard.

In the 1999 standard, type casting may be used to  initialize  automatic variables that are structures or arrays.  For example:

struct _car {
	int tires;
	int cylinders;
} ;

struct _car myCar = (struct _car){ 4, 4};

This follows the same general semantics as other initializers in the 1999 standard.  However, for automatic variables generic expressions may be used for each initialized value.  As a simple example using variables:

int tires = 4;
int cylinders = 6;

struct _car myCar = (struct _car){tires, cylinders);

Array initialization proceed along similar lines.
Arithmetic Operators

Arithmetic operators form the building blocks for equations by allowing basic mathematical operations.  With the exception of %, which is only defined for integers, they may be used in conjunction with any combination of numeric quantities.  The arithmetic operators are as follows:

Unary operators
+	takes one operand, does nothing with it other than implicitly converting it to int if required.
-	takes one operand, returns the result of negating it

Binary operators
+	takes two operands, returns the result of adding them
-	takes two operands, returns the result of subtracting the right from the left
*	takes two operands, returns the result of multiplying them
/	takes two operands, returns the result of dividing the left by the right

An additional mathematical operator defined for integers is as follows:

%	takes two operands, returns the remainder when dividing the left by the right

Before one of these operator is applied, one or both operands may be implicitly cast to the result type.  The result type for these operations is defined as an int, if both operands are less than the size of an int.  Otherwise it is the size of the larger quantity.

These operators would be used as follows:

a * b	multiplies two variables and returns the result.

Note that the first two binary operators may also be used with pointers.  Care should be taken here because there is an implicit assumption that such operations will be done in units of the size of the entity the pointer is pointing to.  For example:

int *p = &myVar;;
p += 4;
*p = 3;

accesses the fifth integer in a linear array of integers that p is pointing to.

Two pointers may be subtracted, for example (p + 4) - (p + 2)  results in the value 2.  This is true no matter what the size of p is, as the result is the number of objects between the two pointers.
Bit wise Operators

Bit wise operators are used for Bit wise logic functions.  These operators only work with integer quantities.  They are as follows:

Unary operators
~	takes one operand, returns Bit wise complement

Binary operators
&	takes two operands, returns Bit wise and
|	takes two operands, returns Bit wise or
^	takes two operands, returns Bit wise exclusive or

Before one of these operator is applied, one or both operands may be implicitly cast to the result type.  The result type for these operations is defined as an int, if both operands are less than the size of an int.  Otherwise it is the size of the larger quantity.

for example:

a ^ b

returns the exclusive or of a and b.
Logical Operators

Logical operators have one or two arguments, which may be numeric values, pointers, or booleans:  The logical operation is applied, and the result is either 0 or 1 depending on the operation.


Unary operators
!	takes one argument, returns 1 if the value is zero, null, or false, and 0 otherwise

Binary operators
&&	if one argument is either nonzero, non null, or true and the other argument is either nonzero, non null or true, return true, else return false;
||	if one argument is either nonzero, non null, or true or the other argument is either nonzero, non null or true, return true, else return false;

For example:

a = bb && cc;

if (quittingTime || lunchTime || tired)
	Stop();

Unlike with other operators, the program may not evaluate the right-hand side of binary logical operators.  For example in the following, when the operand (light == RED) evaluates as true, the program knows the result of the logical operator || is going to be true and doesn't evaluate (laps++ > 20).  In this case, that will also mean that laps does not get incremented.

if ((light == RED) || (laps++ > 20))
	stop();
Shift Operators

Shift operators multiply or divide an integer by a power of two.  They are:

Binary operators
>>	takes two arguments, returns the result of dividing the left by the power of two on the right
<<	takes two arguments, returns the result of multiplying the left by the power of two on the right

The left operand to the shift operator is first converted to an integer, if its size happens to be less than that.  No conversion is performed on the right operand.

They would be used as follows:

a >> 4; // divide by 16

Note that some computers differentiate dividing signed quantities by powers of two and dividing unsigned quantities by powers of two.  Other computers do not make such a distinction.  Therefore, using shift operators on extremely large or negative numbers may cause portability issues.

Shift operators are useful for clarifying certain types of algorithms, in hardware register interactions, or in getting data to and from network packets.  However, most modern compilers will take a multiply or divide statement, and turn it into a shift statement when such a statement is warranted and available.  For example,  an x86 compiler could reasonably be expected to take the expression a * 16 and turn it into the faster expression a << 4 as an optimization.
Assignment Operators

A variety of assignment expressions are available.  The simplest simply assigns the value of an expression to a variable.  The others take the left-hand operand, perform an operation on it based on the value of the right-hand operand, and stores the result back in the left hand side. 

=	simple assignment.  the left hand side is a quantity to assign to, and the right hand side is some other expression.
+=	add the right hand side to the left and assign the left the new result
-=	subtract the right hand side from the left and assign the left the new result
*=	multiply the left hand side by the right and assign the left the new result
/=	divide the left hand side by the right and assign the left the new result
%=	divide the left hand side by the right and assign the left the remainder
&=	the left hand side gets the result of the Bit wise and of the two operands
|=	the left hand side gets the result of the Bit wise or of the two operands
^=	the left hand side gets the result of the Bit wise exclusive or of the two operands
>>=	the left hand side gets the result of shifting right by the right hand side
<<=	the left hand side gets the result of shifting left by the right hand side

An assignment operand performs the same implicit casts as the associated operator: e.g. one or both arguments may be implicitly cast to a higher type before performing the operation.  The result is then cast to the type of the left-hand side and stored.

Assignments operators return a value.  Then can be cascaded, or used within the context of other expressions.  For example the following stores the result of b*c in both b and a.
a = b *= c;

Some care needs to be taken because of the implicit casts that go on, for example the above assignment is really:

a = (b *= c);

which means a would be truncated to the size of b and could lose precision when b cannot hold the entire result, even if the type of a is larger than the type of b.  This is true because an implicit cast is done to the size of type of b.

Note that if two structured variables are of the same type, a simple assignment will copy the members of one to the members of the other:

struct _fruit apple, orange;

apple = orange;
Equality Operators

Equality operators test the values of two variables or constants, and return an integer value which is zero or one.

Binary Operators
==	tests if two booleans, pointers, or numbers are equal, returns 1 if they are equal
!=	tests if two booleans, pointers, or numbers are not equal, returns 1 if they are not equal.

When comparing numbers, both sides are first cast to the size of the larger type, or to int if both types are smaller than that.

For example:

if (myCounter == 5)
	stop();

bKeepGoing = stopLight != RED;

Relational Operators

Relational operators are used to compare the relative values of two numbers or pointers.  They return an integer value of 1 if the relationship is true, or 0 if not.

Binary operators
>	tests if the left is greater than the right
<	tests if the left is less than the right
>=	tests if the left is greater than or equal to the right
<=	tests if the left is less than or equal to the right

When comparing numbers, both sides are first cast to the size of the larger type, or to int if both types are smaller than that.

Comparing pointers is only defined if both pointers point to within the same array object.  Pointers to unlike quantities, to non-array objects, or to different arrays, should not be compared.

For example:

if (butterflyCounter > NUMBER_OF_BUTTERFLIES_IN_MASSACHUSETTS)
	printf("we have too many butterflies");
Pointer Operators

Pointer operations convert normal variables into pointers, or dereference pointers to allow access to the value being pointed to.

Unary operations
&	turns a variable into an entity that may be assigned to a pointer
*	dereferences a pointer, allows access to the value the pointer references
[]	reference an array element.

For example:

int b;
int *b_ptr = &b;

*b_ptr = 4;

results in the value of 'b' being 4. As another example:

int array[100];

int *arr_ptr = &array[0];
arr_ptr += 17;
*arr_ptr = 4;

has the same result as:

int array[100];
array[17] = 4;

which also has the same result as:

int array[100];
int *arr_ptr = &array[0];
arr_ptr[17] = 4;

Member Operators

The member operators are used to access a member of a structure or union;

.	access a member of a structured variable
->	access a member of a structure which is pointed to

For example:

struct _fruit
{
	enum _type { apple, orange} type;
} *myFruit ;

myFruit = malloc(sizeof(struct _fruit));

myFruit->type = apple;

sets the type of myFruit to be apple.

on the other hand:

struct _fruit myOrange;
myOrange.type = orange;

sets the type of myOrange to be an orange.

When structures are nested, such operators may be cascaded and combined with other operators.  For example:

car->tires[1].hubcap->color

is a valid expression which goes through three structures and an array to get to the final data field.
Auto increment Operators

The Auto increment functions take a single argument, which may be a number or pointer..  They are as follows:

Unary operators:
++	add one
--	subtract one

These operators simply adds or subtracts one from the argument, and stores the result back into the argument.  In the case of pointers, the addition or subtraction is based on the size of an object.  The following accesses the second integer in an array of integers p is pointing to.

int *p= &b, value;
p++;
value = *p;

Auto increment functions return a value.  Depending on whether the operator is placed before or after the variable, the value returned can be the number prior to the operation or after it.

For example:

int a, b , c = 7;
a = c++;		//a = 7, c = 8;
c = 7;
b = ++c;		//b = 8, c = 8;

Note that the point at which the ++ or -- is evaluated is somewhat ambiguous.  It is all right for a compiler to perform the operation at almost any point during the evaluation of an expression.  For example:

a = i++ + i;

has an undefined result because there the point at which the ++ is evaluated is undefined.
Hook Operator

The hook operation is a simple way to perform any  if statement  that looks similar to this:

if (snowIsMuddy)
	myVariable = a + b;
else
	myVariable = a - b;

This may be written as an expression using a hook operator:

myVariable = snowIsMuddy ? a + b : a - b;

where the ? : characters separate the condition from the if and else clauses.  Note that unlike a normal if statement, the else close is mandatory.


Comma Operator

The comma operator may be used to cascade multiple  expressions  for sequential evaluation.  The comma operator isn't available in function arguments or declarations, but may otherwise be used in expressions.  For example:

if (toIncrement)
	counter++,pointer++;

The comma operator is often used in the selecting expressions for complex for loops, for example:

for (i=0,j=20; i < 20; i++, j--)
{
}

The comma operator does return a value.  Its value is the value of the left hand most expression.  For example:

myInteger = a++, b++;

assigns the value of a to myInteger, prior to the increment.
Function Calls

Function calls are used to invoke a previously defined  function .  A function is given arguments, the operations in the function are performed, and a result may be returned.  As an example with the function:

long double addFloatToInt(int a, long double b)
{
	return a + b;
}

we could write:

short shortValue;
float floatingValue;
long double result = addFloatToInt(shortValue, floatingValue);

In this case we are also taking advantage of implicit type conversions that occur when evaluating the arguments.  We could also write:

int a,b;

result = addFloatToInt(a+b, floatingValue);

e.g. any argument can be a full-fledged expression, as long as that expression does not have a comma operator in it.

If we want to call a function but it doesn't return a value, or we simply want to ignore the value it returned, we could write:

addFloatToInt(a+b, floatingValue);

This particular function doesn't do anything useful when we do that, however.