C Language
C Language
C Language
Soft ware
C Language manual
Rev. 1.1
Chapter 1
Historical Introduction
Chapter 2
C Language Overview
C Files ......................................................................................2-1
Lines ..................................................................................2-1
Comments..........................................................................2-2
Trigraphs............................................................................2-2
Lexical Tokens.........................................................................2-3
Identifiers...........................................................................2-3
Keywords...........................................................................2-3
Constants ...........................................................................2-4
Operators and Punctuators.................................................2-4
Declarations .............................................................................2-5
Integer Types .....................................................................2-5
Bit Type .............................................................................2-7
Real Types .........................................................................2-7
Pointers ..............................................................................2-8
Arrays ................................................................................2-9
Structures...........................................................................2-9
Unions................................................................................2-9
Enumerations...................................................................2-10
Functions .........................................................................2-10
Chapter 3
Declarations
Integers.....................................................................................3-2
Bits ...........................................................................................3-3
Pointers ....................................................................................3-3
Arrays.......................................................................................3-4
Modifiers..................................................................................3-5
Structures .................................................................................3-8
Unions....................................................................................3-10
Enumerations .........................................................................3-10
Functions................................................................................3-11
Storage Class..........................................................................3-13
Typedef ..................................................................................3-15
(i)
Variable Scope.......................................................................3-15
Absolute Addressing..............................................................3-16
Chapter 4
Expressions
Variables ..................................................................................4-2
Constants .................................................................................4-2
Strings......................................................................................4-5
Sizeof .......................................................................................4-7
Operators .................................................................................4-7
Arithmetic operators..........................................................4-8
Bitwise operators...............................................................4-9
Boolean operators............................................................4-10
Assignment operators ......................................................4-11
Addressing operators.......................................................4-13
Function call operator......................................................4-14
Conditional operator........................................................4-16
Sequence operator ...........................................................4-17
Conversion operator ........................................................4-17
Priorities.................................................................................4-19
Chapter 5
Statements
Block statement .......................................................................5-2
Expression statement ...............................................................5-2
If statement ..............................................................................5-3
While statement .......................................................................5-5
Do statement ............................................................................5-6
For statement ...........................................................................5-7
Break statement .......................................................................5-9
Continue statement ................................................................5-10
Switch statement....................................................................5-11
Goto statement .......................................................................5-13
Return statement ....................................................................5-14
Chapter 6
Preprocessor
Macro Directives .....................................................................6-2
Hazardous Behaviours.......................................................6-5
Predefined Symbols...........................................................6-7
Conditional Directives.............................................................6-8
Control Directives..................................................................6-10
(ii)
#include ...........................................................................6-10
#error ...............................................................................6-10
#line .................................................................................6-10
#pragma ...........................................................................6-11
(iii)
Preface
Chapter 1, “Historical Introduction”
Chapter 3, “Declarations”
- syntax for each kind of object
- modifiers const, volatile
- extra modifiers for pointers
- function declaration, arguments and local variables
- classes and scope
- type redeclaration (typedef)
Chapter 4, “Expressions”
- identifiers, operators and constants
- operators behaviour
- conversions, explicit, implicit
Chapter 5, “Statements”
- description of each statement
Chapter 6, “Preprocessor”
- description of each directives
- traps and solutions
- pragmas and their usage
Historical Introduction
The C language was designed in 1972 at the Bell Lab’s by Denis
Ritchie, mainly to rewrite the UNIX operating system in a portable way.
UNIX was written originally in assembler and had to be completely
rewritten for each new machine. The C language was based on previous
languages named B and BCPL, and is logically called C to denote the
evolution.
The success of C for those small processors is due to the fact that it is
not a high level language, as PASCAL or ADA, but a highly portable
macro assembler, allowing with the same flexibility as assembler
almost the same efficiency.
The C language has been normalized from 1983 to 1990 and is now an
ANSI/ISO standard. It implements most of the usefull extentions added
to the original language in the previous decade, and a few extra features
allowing a more secured behaviour.
C Language Overview
A C program is generally split in to several files, each containing a part
of the text describing the full application. Some parts may be already
written and used from libraries. Some parts may also be written in
assembler where the C compiler is not efficient enough, or does not
allow a direct access to some specific resources of the target processor.
C Files
Each of these C files has to be compiled, which translates the C text file
to a relocatable object file. Once all the files are compiled, the linker is
used to bind together all the object files and the required libraries, in
order to produce an executable file. Note that this result file is still in an
internal format and cannot be directly transferred to the target system. A
special translator is provided to convert the executable file to a down-
loadable format.
Lines
Each C text file contains a set of lines. A line contains characters and is
finished by a line terminator (line feed, carriage return). The C compiler
allows several physical lines to be concatenated in a single logical line,
whose length should not exceed 511 characters in order to strictly com-
ply with the ANSI standard. The COSMIC compiler accepts up to 4095
characters in a logical line. Two physical lines are concatenated into a
single logical line if the first line ends with a backslash character ‘\’
just before the line terminator. This feature is important as the C lan-
guage implements special directives, known as preprocessing direc-
tives, whose operands have to be located on the same logical line.
Comments
Comments are part of the text which are not meaningful for the com-
piler, but very important for the program readability and understanding.
They are removed from the original text and replaced by a single
whitespace character. A comment starts with the sequence /* and ends
with the sequence */. A comment may span over several lines but nest-
ing comments is not allowed. As an extension from the ANSI standard,
the compiler also accepts C++ style comments, starting with the
sequence // and ending at the end of the same logical line.
Trigraphs
The C language uses almost all the ASCII character set to build the lan-
guage components. Some terminals or workstations cannot display the
full ASCII set, and they need a specific mechanism to have access to all
the needed characters. These special characters are encoded using spe-
cial sequences called trigraphs. A trigraph is a sequence of three char-
acters beginning with two question marks ?? and followed by a
common character. These three characters are equivalent to a single one
from the following table:
??( [
??/ \
??) ]
??’ ^
??< {
??! |
??> }
??- ~
??= #
All other sequences beginning with two question marks are left
unchanged.
Lexical Tokens
Characters on a logical line are grouped together to form lexical tokens.
These tokens are the basic entities of the language and consist of:
identifiers
keywords
constants
operators
punctuation
Identifiers
An identifier is used to give a name to an object. It begins with a letter
or the underscore character _, and is followed by any letter, digit or
underscore character. Uppercase and lowercase letters are not equiva-
lent in C, so the two identifiers VAR1 and var1 do not describe the
same object. An identifier may have up to 255 characters. All the char-
acters are significant for name comparisons.
Keywords
A keyword is a reserved identifier used by the language to describe a
special feature. It is used in declarations to describe the basic type of an
object, or in a function body to describe the statements executed.
Constants
A constant is used to describe a numerical value, or a character string.
Numerical constants may be expressed as real constants, integer con-
stants or character constants. Integer constants may be expressed in
decimal, octal or hexadecimal base. The syntax for constants is
explained in the Expressions chapter.
When parsing the input text, the compiler tries to build the longest
sequence as possible for a token, so when parsing:
a+++++b
and not:
Declarations
A C program is a set of tokens defining objects or variables, and func-
tions to operate on these variables. The C language uses a declaration
to associate a type to a name. A type may be a simple type or a complex
type.
Integer Types
An integer type is one of:
char 1 byte
short 2 bytes
int 2 or 4 bytes
long 4 bytes
The type int is equivalent either to type short or to type long depending
on the target processor capabilities. For most of the existing microproc-
essors, int is equivalent to short because their internal registers are at
most 16 bits wide. The type int is an important one because it is used as
a reference type for the expressions and for the function arguments. It is
a source of problems when adapting a program to another target proces-
sor because its size may also change.
Note that these attributes do not change the object size in memory, but
alter the way the object is handled. For instance, comparing an unsigned
integer less than zero is meaningless, and will probably lead in a com-
piler error message. Other effects of these attributes will be discussed
with the expressions and conversions.
If these keywords are not used in an integer declaration, the object has a
default attribute depending on its type.
short
int are signed by default
long
Each of these identifiers becomes a new constant for the compiler, and
by examining the smallest and the largest values of the enumeration, the
compiler will choose the smallest integer type large enough to hold all
the values. The enumeration is a convenient way to define a set of codes
and at the same time the most efficient variable type to hold all of these
codes.
Bit Type
The bit type is
_Bool 1 bit
Objects of this type are packed into bytes or words in memory and allo-
cated to a memory section appropriate for access by efficient bit han-
dling instructions. Such objects have only the two possible values true
and false usually coded as 1 and 0.
Real Types
A real type is one of
float 4 bytes
double 8 bytes
long double more than 8 bytes
For some small target processors, only the type float is supported by the
compiler. In that case, all the types are allowed in the program syntax,
but they are all mapped to the type float internally. This mechanism is
also available as an option for larger targets if the application does not
require a very accurate precision. It reduces the memory usage and the
time needed to perform the operations.
Pointers
The C language allows more complex types than the simple numerical
types. The first one is the pointer which allows for simple handling of
addresses. A pointer is a variable which contains an address. In order
to know what to do with that address, a type is associated with the
pointer.
A pointer takes the type of the object pointed at by the pointer value. A
pointer in C is associated with the operator *, which is used to declare a
pointer and to designate the object pointed at by the pointer. A pointer
allows access to any location of the processor memory without any con-
trol other than any hardware control mechanism. It means that a pointer
is as convenient as it is dangerous. In general, the C language does not
verify anything at execution time, producing good efficiency, but with
the same security as assembler, meaning none. All values are possible,
but the C language reserves the value zero to designate an invalid
pointer, although nothing stops a program accessing memory at address
zero with a pointer. The size of a pointer in memory depends on the tar-
get processor. Most of the small microprocessors address 64K of mem-
ory and use 16 bit addresses. A pointer is then equivalent to an
unsigned short. Some processors allow several memory spaces with
various addressing mechanisms. To allow the best efficiency, the com-
piler supports three different pointer types by defining a size attribute
along with the pointer:
Arrays
The next complex type is common to several languages. An array may
be defined as a collection of several objects of the same type. All these
objects are allocated contiguously in memory and they may be accessed
with an index, designating the rank of an object within an array.
Structures
A structure may be defined as a collection of several objects of differ-
ent types. No index can be used as objects may have a different size.
Each object will be accessed individually by its name. Such a structure
member will be called a field. A structure is a convenient way of group-
ing together objects related to a specific feature. All the members are
allocated in memory in the order defined by the structure and contigu-
ously. Note that some processors have addressing restrictions and need
sometimes to align data in memory on even boundaries for instance.
The C compiler will respect these restrictions and will create holes if
necessary to keep alignment. This means that a structure may not be
allocated exactly in the same way depending on the target processor.
Most small microprocessors do not require any alignment. The size of a
structure in memory will be the sum of the size of each of its fields, plus
if necessary the size of the padding holes. A structure may contain spe-
cial fields called bitfields defining objects with a size smaller than a
byte. Such objects are defined with a bit size.
Unions
A union is a variant of a structure. In a structure, all the members are
individual objects allocated contiguously. In a union, all the members
refer to the same object, and are allocated at the same address. All the
members of a union are equivalent, and the size of a union will be the
size of its largest member. A union is a convenient way to save memory
space when a location may be used with different formats depending on
the context.
Enumerations
An enumeration is an integer object defined by the list of its possible
values. Each element is an integer constant and the compiler will
choose the smallest integer type to implement such an object.
Functions
The C language defines a function as an object. This allows the same
syntax to be used for a function declaration, and also allows a pointer to
point at a function. A function is a piece of code performing a transfor-
mation. It receives information by its arguments, it transforms these
input and the global information, and then produces a result, either by
returning a value, or by updating some global variables. To help the
function in its work, it is allowed to have local variables which are
accessed only by that function and exist only when the function is
active. Arguments and local variables will generally be allocated on the
stack to allow recursivity. For some very small microprocessors, the
stack is not easily addressable and the compiler will not use the proces-
sor stack. It will either simulate a stack in memory if the application
needs to use recursivity, or will allocate these variables once for ever at
link time, reducing the entry/exit time and code load of each function,
but stopping any usage of recursivity.
When a function call is executed the arguments are copied onto the
stack before the function is called. After the call, the return value is cop-
ied to its destination and the arguments are removed from the stack. An
argument or a return value may be any of the C data objects. Objects are
copied, with the exception of arrays, whose address is copied and not
the full content. So passing an array as argument will only move a
pointer, but passing a structure as argument will copy the full content of
the structure on to the stack, whatever its size.
Declarations
An object declaration in C follows the global format:
<class> is the storage class, and gives information about how the
object is allocated and where it is known and then accessible.
<type> gives the basic type of the object and is generally completed by
the name information.
Integers
An integer variable is declared using the following basic types:
char
short or short int
int
long or long int
float
double
long double
In most of the cases, type long double is equivalent to type double. For
small microprocessors, all real types are mapped to type float.
char c;
short val = 1;
int a, b;
unsigned long l1 = 0, l2 = 3;
Bits
A bit variable is declared with the following basic type:
_Bool
These variables can be initialised like a numerical type, but the assign-
ment rules do not match the integer rules as described in the next chap-
ter. Here are some examples:
_Bool ready;
_Bool led = 0;
Pointers
A pointer is declared with two parameters. The first one indicates that
the variable is a pointer, and the second is the type of the pointed object.
In order to match the global syntax, the <type> field is used to declare
the type of the pointed object. The fact that the variable is a pointer will
be indicated by prefixing the variable name with the character *.
Beware that declaring a pointer does not allocate memory for the
pointed object, but only for the pointer itself. The initialization field is
written as for a numerical variable, but the constant is an address con-
stant and not a numerical constant, except for the numerical value zero
which is conventionally representing the NULL pointer. Here are some
examples:
A pointer can be declared with the special type void. Such a pointer is
handled by the compiler as a plain pointer regarding the assignement
operations, but the object pointed at by the pointer cannot be accessed
directly. Such a syntax is interesting when a pointer has to share differ-
ent types.
Arrays
An array is declared with three parameters. The first one indicates that
the variable is an array, the second indicates how many elements are in
the array and the third is the type of one element. In order to match the
global syntax, the <type> field is used to declare the type of one ele-
ment. The fact that the variable is an array and its dimension will be
indicated by adding the dimension written between square brackets
[10] after the name. The dimension is an integer constant which may
be omitted in some cases. An array initialization will be written by an
equal sign = followed by a list of values placed between curly braces,
and separated by commas. Here are some examples:
The missing characters are filled with zeroes. Because a text string is
conventionally ended by a NULL character, the following declaration:
defines an array of 6 characters, 5 for the word hello itself, plus one
for the ending NULL which will be appended by the compiler. Note
that if you write the following declaration:
the compiler will declare an array of 5 characters, and will not com-
plain, or add any NULL character at the end. Any smaller dimension
will cause an error.
char *ptab[10];
char *tabp[10];
Unfortunately, this is the same declaration for two distinct objects. The
mechanism as described above is not enough to allow all the possible
declarations without ambiguity. The C syntax for declaration uses prior-
ities to avoid ambiguities, and parentheses to modify the order of prior-
ities. The array indicators [] have a greater priority than the pointer
indicator *. Using this priority, the above example will always declare
an array of 10 pointers.
char (*tabp)[10];
Modifiers
A declaration may be completed by using a modifier. A modifier is
either of the keywords const and volatile or any of the space modifiers
accepted by the compiler. A space modifier is written with an at sign @
followed by an identifier. The compiler accepts some predefined space
modifiers, available for all the target processors, plus several target spe-
cific space modifiers, available only for some target processors. The
COSMIC compiler provides three basic space modifiers for data
objects: @tiny, @near and @far. @tiny designates a memory space
for which a one byte address is needed. @near designates a memory
space for which a two byte address is needed. @far designates a mem-
ory space for which a four byte address is needed. The compilers are
provided with one or several different memory models implementing
various default behaviours, so if none of these space modifiers is speci-
fied, the selected memory model will enforce the proper one.
The const modifier means that the object to which it is applied is con-
stant. The compiler will reject any attempt to modify directly its value
by an assignment. A cross compiler goes further and may decide to
locate such a constant variable in the code area, which is normally writ-
ten in a PROM. A const object can be initialized only in its declaration.
When the initialised value of a const variable is known to the compiler,
because it is declared in the current file, an access to this variable will
be replaced by direct use of the initialised value. This behaviour can be
disabled by the -pnc compiler option if such an access must be imple-
mented by a memory access.
The volatile modifier means that the value of the object to which it is
applied may change alone, meaning without an explicit action of the
program flow. This is the case with an input port, or with a variable
updated by an interrupt function. The effect of such a directive is to stop
the compiler optimizing the accesses to such a variable. In the follow-
ing example:
char PORTA;
The first assigment will be optimized out by the compiler as the PORTA
variable is supposed to be a plain memory location which is not used
between the two assigments. If such a variable is matching a physical
output port, it must be declared as a volatile object:
placed before the * character, it affects the pointed object. If the modi-
fier is place after the * character, it affects the pointer.
The first assignment modifies the pointer itself and is allowed. The sec-
ond assignment tries to modify the pointed const object, and is then not
allowed.
The first assignment tries to modify a const pointer and is then not
allowed. The second assignment modifies the pointed object and is
allowed.
Structures
A structure is declared by declaring all of its fields. They are grouped
between curly braces and are preceded by the keyword struct. A struc-
ture, as a type, may be named to be reused later. This feature avoids
repeating the full content of the structure. Such a name is called a tag
name and is placed between the keyword struct and the opening curly
brace.
This set will fill the <type> field of the global declaration syntax. There
is no modification of the <name> field.
struct {
char a;
int b;
long c;
} st = {1, 2, 3};
If the initialization list contains less values than structure fields, the
missing fields are initialized to zero.
struct {
char a:4;
char b:3;
char c:2;
}
The ANSI standard does not define in which order bitfields are filled.
The COSMIC compiler fills bitfields from the less significant bit to the
most significant bit by default. This ordering can be reversed by using
the +rev compiler option. The ANSI standard also limits the allocation
unit type to int or unsigned int. The COSMIC compiler allows all the
integer types as an extension.
By default, the compiler does not allocate the unused part of the last bit-
field if its size is larger or equal to a byte. This process can be disabled
by using the -pnb compiler option.
Unions
A union is declared like a structure, but the keyword union replaces the
keyword struct. A union may be initialized, but as all the fields are
located at the same address, it is seen as a single variable for the initial-
ization, which will be done using the first field of the union.
union {
char a;
int b;
long c;
} u = 1;
A tag name may be specified on a union. Tag names are in the same
name space, so a union tag name cannot be the same as a structure tag
name.
Enumerations
An enumeration is declared with a syntax close to the structure/union
declaration. The list of fields is replaced by a list of identifiers. The key-
word enum replaces the keyword struct or union. A tag name may also
be specified, sharing the same name space than the structure and union
tag names. Each identifier will be assigned a constant value by the com-
piler. An enumeration variable will be allocated as a char, a short or a
long depending on the range of all the idenfiers. This means that the
compiler always needs to know all the enum members before it allo-
cates an enum variable. If the -pne option has been set, an enum varia-
ble is always allocated as an int and then there is no need to know the
enum members before to allocate the variable.
The names blue, white and red are three new constants. Values are
assigned to the names starting at zero, and incrementing by one for each
new name. So blue is zero, white is one and red is two. The variable
flag will be declared as a plain char as a byte is large enough to hold
values from zero to two.
blue is still zero, white is now ten, and red is eleven, as the internal
counter is incremented from the previous value. These names become
new constants and may be used in any expression, even if they are not
assigned to an enumeration variable. In the same way, an enumeration
variable may be assigned with any kind of integer expression. An enu-
meration may be initialized as an integer variable.
Functions
A function is declared with three parameters. The first one indicates
that the object is a function, the second gives the argument list, and the
third is the type of the returned value. In order to match the global syn-
tax, the <type> field is used to declare the type of the returned value.
The fact that the object is a function will be indicated by adding the
argument list written between parentheses () after the name. The
<type> field is used to declare the type of the returned value. If the
function has nothing to return, the <type> field is replaced by the key-
word void meaning that nothing is returned from the function. This
syntax will also allow the compiler to detect any invalid usage of the
function, if it is used in an expression or an assignment.
The argument list may be specified in two different ways. The first one
which is the oldest is known as the Kernigan and Ritchie (K&R) syn-
tax. The second has been introduced by the standardization and is
known as the prototyped syntax.
The K&R syntax specifies the argument list as a list of identifiers sepa-
rated by commas between the parentheses. This list is immediately fol-
lowed by the full declaration of the identifiers specified in the list. An
undefined identifier will be defaulted to an int argument.
int max(a, b)
int a;
int b;
The prototyped syntax offers extra features compared with the K&R
syntax. When a function is called with parameters passing, the compiler
will check that the number of arguments passed matches the number in
the declaration, and that each argument is passed with the expected
type. This means that the compiler will try to convert the actual argu-
ment into the expected type. If it is not possible, the compiler will pro-
duce an error message.
None of these checks are done when the function is declared with the
K&R syntax. If a function has no arguments, there should be no differ-
ence between the two syntaxes. To force the compiler to check that no
argument is passed, the keyword void will replace the argument list.
int func(void)
The compiler will check that there are at least two arguments, which
will be converted to int if they have a compatible type. It will not com-
plain if there are more than two arguments, and they will be passed
without explicit conversion.
Storage Class
It is possible in C to have a partial control over the way variables are
allocated. This is done with the <class> field defining the storage
class. This information will also control the scope of the variable,
meaning the locations in the program where this variable is known and
then accessible. The storage class is one of the following keyword:
extern
static
auto
register
extern means that the object is defined somewhere else. It should not
be initialized, although such a practice is possible. The extern keyword
is merely ignored in that case. The definition may be incomplete. An
array may be defined with an unknown dimension, by just writing the
bracket pair without dimension inside.
A function may be declared with only the type of its arguments and of
its return value.
Note that for a function, the extern class is implied if no function body
is specified. Note also that if a complete declaration is written, useless
information (array dimension or argument names) is merely ignored.
The COSMIC compiler may in fact use a dimension array to optimize
the code needed to compute the address of an array element, so it may
be useful to keep the dimension even on an extern array declaration.
static means that the object is not accessible by all parts of the program.
If the object is declared outside a function, it is accessible only by the
functions of the same file. It has a file scope. If an object with the same
name is declared in another file, they will not describe the same mem-
ory location, and the compiler will not complain. If the object is
declared inside a function, it is accessible only by that function, just like
a local variable, but it is not allocated on the stack, meaning that the
variable keeps its value over the function calls. It has function scope.
auto means that the object is allocated dynamically, and this implies
that it is a local variable. This class cannot be applied to an object
declared outside a function. This is also the default class for an object
declared inside a function, so this keyword is most of the time omitted.
Typedef
The C language allows the definition of a new type as a combination of
existing types. The global declaration syntax is used with the special
keyword typedef used in place of the <class> field. The <name> field
describes a new type equivalent to the type described by the declara-
tion.
PINT ptab[10];
int *ptab[10];
The typedef feature is a convenient way to redefine all the types used by
an application in a coherent and more verbose way than using only the
basic C types.
Variable Scope
Once declared, an object can be hidden locally by another declaration.
Absolute Addressing
The COSMIC compiler allows an object to be declared along with its
address when it is known at compile time (I/O registers). The address is
specified just after the declaration and is replacing the initialization
part. It is prefixed by the @ character:
Expressions
An expression is a set of variables, constants and operators which are
combined together to provide a result. C expressions are more extensive
than in other languages because the notion of a result is included in
operations such as assignments and function calls.
Variables
Variables are simply expressed by their name, which is a C identifier
which should have been previously defined, otherwise the compiler
does not know the type of that variable and does not know how to
access it. The only exception is that you can call a function which has
not been declared. The compiler will define that unknown function as
an external function returning an int. If the function is declared later in
the same file and the actual return type does not match, or if strict
checking options are used (-pp, +strict), the compiler will complain.
Constants
Constants can be expressed in several formats. Integer constants may be
expressed in decimal, octal, hexadecimal or character format.
For example:
For example:
Note that hexadecimal character constants are not limited to three digits
like the octal constants. This may be a problem when used in string con-
stants as shown below.
The control characters mapped (with their ASCII code) are the follow-
ing:
\a BELL (0x07)
\b BACKSPACE (0x08)
\t TAB (0x09)
\n LINE FEED (0x0A)
\v VERTICAL TAB (0x0B)
\f FORM FEED (0x0C)
\r CARRIAGE RETURN (0x0D)
L’A’
All these integer constants have at least the type int. If the entered con-
stant exceeds the resolution of an int (which is signed), the final type
will be the smallest one able to represent the constant. Note that for a
decimal constant, if it cannot be expressed by an int, but by an
unsigned int, the result will be of type long and not unsigned int, as a
decimal constant usually expresses signed values.
For example:
A floating point constant has the type double by default. The suffix
character f or F forces the compiler to define the constant with the type
float. The suffix l or L forces the constant to the type long double.
Note that types double and long double are the same for most of the tar-
gets.
For example:
Strings
The C language also defines a string constant to ease character strings
handling. Such a constant is written by a sequence of printable or non
printable characters enclosed by double quote characters. Non printable
characters are expressed the same way as individual character con-
stants. Such a constant is built in memory, and its value is in fact the
memory address of that location. A NULL character is appended to the
sequence of characters to conventionally end the text string.
For example:
“hello”
“\15\12” or “\x0d\0xa”
“\3123”
defines a string containing the two characters ‘\312’ and ‘3’, and not
the characters ‘\3’ , ‘1’, ‘2’, ‘3’. Such a string can be entered by
filling the octal constant up to three digits:
“\003123”
This trick cannot be used with hexadecimal constants because they have
no length limit. In case of any conflict, the only solution is to use the
octal syntax.
For example:
L”hello”
Sizeof
The C language also allows a special constant which is written:
sizeof expression
or
sizeof (type)
Operators
Operators are symbolic sequences which perform an operation from
one or two operands (three for one operator), and provide a result. The
C evaluation rules describe how the operands are prepared depending
on their type, and what is the type of the result. First of all, operands are
promoted to the int type. This means that if the type of any operand is
smaller than the type int, it is converted to type int. If the type of an
operand is larger or equal to type int, it is left unchanged. Then, if the
operator requires two operands, and if those two operands do not have
the same type, the operand with the smallest type is converted into the
type of the largest. Then the operation is performed, and the result type
is the same as the largest operand.
Converting an integer type into a larger one will keep the sign of the
original operand if the smallest type is signed. Otherwise, the original
value is zero extended to reach the size of the largest type. Converting
any type to a larger floating point type will keep the sign if the original
type is a floating point or a signed integer type, or will produce a posi-
tive value otherwise.
These rules imply that the result of any expression is at least an int
value. If the application handles variables with types smaller than int,
the code needed to evaluate expressions with these rules will contain a
lot of implicit conversions which may appear to be useless. Hopefully,
the compiler will shorten the code each time it detects that the final
result is the same than if the full rules were applied.
Operators may have different behaviour depending on the way they are
used and their operand types. This is a difficulty when learning C.
Operands may be subexpressions. Any conflict between operators is
resolved by priority rules, or by enclosing subexpressions in parenthe-
ses. For operators with the same priority, the grouping order allows
grouping from left to right, or right to left. For most of the operators, the
evaluation order of the operands for that operator is undefined, meaning
that you cannot assume that left operand will be evaluated before the
right operand. In most of the cases, it does not matter, but there are a
few situations where the evaluation order is importance. In such a case,
the expression should be split into several independent expressions.
Arithmetic operators
a + b returns the addition of operands a and b
+a returns the (promoted) value of operand a
a - b returns the difference of operands a and b
-a returns the negated value of operand a
a * b returns the product of operands a and b
a / b returns the division of operand a by operand b
a % b returns the remainder of the division of operand a
by operand b
All these operators apply to integer types. For these types, the division
operator gives as result the integer quotient of the operands division.
All these operators except the remainder % apply to floating point types.
Operators + and - also apply to pointers, but special rules are applied.
The possible constructs are:
For example:
short *p;
The result of such an operation is a pointer with the same type as the
pointer operand.
Subtracting a pointer from another first requires that both pointers have
exactly the same type. Then, after having computed the difference
between the two operands, the result is divided by the size in bytes of
the related object. The result is then the number of elements which sep-
arate the two pointers. The result is always of type int.
Bitwise operators
a & b returns the bitwise and of operands a and b
a | b returns the bitwise or of operands a and b
a ^ b returns the bitwise exclusive or of operands a and b
a << b returns the value of promoted operand a left
shifted by the number of bits specified by operand b
a >> b returns the value of promoted operand a right
shifted by the number of bits specified by operand b
~a returns the one’s complement of promoted operand a
All these operators apply to integer types only. The right shift operator
will perform arithmetic shifts if the promoted type of its left operand is
signed (thus keeping the sign of the operand). Otherwise, it will per-
form logical shifts.
Boolean operators
Boolean operators create or handle logical values true and false. There
is no special keyword for these values, numerical values are used
instead. False is value zero, whatever type it is. True is any value which
is not false, meaning any non zero value. This means that the result of
any expression may be used directly as a logical result. When produc-
ing a logical true, the compiler always produces the value 1.
Both operators && and || evaluate the left operand first. If the final
result is reached at that point, the right operand is NOT evaluated. This
is an important feature as it allows the second operand to rely on the
result of the first operand.
a < b < c
Assignment operators
Assignment operators modify memory or registers with the result of
an expression. A memory location can be described by either its name,
or an expression involving pointers or arrays, and is always placed on
the left side of an assignment operator. Such an expression is called
L-value. The expression placed on the right side of an assignment oper-
ator is then called R-value. Conversion rules differ from the ones used
for other operators. The R-value is evaluated using the standard rules,
and the compiler does not consider the L-value type. When the R-value
has been evaluated, its resulting type is compared with the L-value type.
If both are identical, the result is copied without alteration. If the
L-value type is smaller than the R-value type, the result is converted
into the L-value type, by truncating integers, or converting floating
point types. If the L-value type is larger than the R-value type, the result
is extended to the L-value type, by either sign extension or zero exten-
sion for integers, depending on the R-value type, or by converting float-
ing point types. When the +strict option is used, the compiler outputs
an error message when an assignment is truncating the R-value.
The COSMIC compiler allows pointers with different sizes, using the
special modifiers @tiny, @near and @far. By default, the compiler
will widen the size of a pointer, but will not narrow it, unless authorized
by a parser option (-np).
a = b = c;
a += b is equivalent to a = a + b
a -= b is equivalent to a = a - b
a *= b is equivalent to a = a * b
a /= b is equivalent to a = a / b
a %= b is equivalent to a = a % b
a &= b is equivalent to a = a & b
a |= b is equivalent to a = a | b
a ^= b is equivalent to a = a ^ b
a <<= b is equivalent to a = a << b
a >>= b is equivalent to a = a >> b
As for the previous operators, the type limitations are the same as for
the simple + and - operators.
++ i --
Those operators should not be used several times on the same variable
in the same expression:
i++ - i++
Addressing operators
The C language defines pointers, and a set of operators which can be
applied to them, or which allows them to be built.
The & operator returns the address of its operand. The operand can be a
variable, or any legal L-value. It is not possible to compute the address
of a variable declared with the register class, even if the compiler did
not allocate it into a physical register, for portability reasons. The type
of the result is a pointer to the type of the operand.
Each argument follows the standard evaluation rules and will be pro-
moted if necessary. This means that a char variable will be passed as an
int, and a float variable passed as a double if both types are supported.
For small processors, this mechanism may consume too much time and
stack space, so the COSMIC compiler allows this default mechanism to
be stopped by an option (+nowiden). In such a case, any variable is
passed in its basic type, although expressions will be passed with their
expected promoted resulting type, unless it is cast. When the function is
declared with a prototype, each argument is cast to the declared type
before being passed. Note that the widening control option is not
always available, depending on the target processor’s ability to stack
single bytes or not.
The result of such a function call is the value returned by the function
with the expected type.
The K&R syntax does not require that a function is defined before it is
called. The return type is assumed to be int. Using the ANSI prototypes
or the strict checking options avoid most of the errors created by that
kind of situation.
(*ptr_func)(arg1, arg2);
ptr_func(arg1, arg2);
Conditional operator
The conditional operator is equivalent to an if ... else sequence applied
to an expression. The expression:
Sequence operator
The sequence operator allows a single expression to be expressed as a
list of several expressions. The expression:
exp_1 , exp_2
Conversion operator
The cast operator allows the result of an expression to be converted into
a different type. The final type enclosed by parentheses, is prepended to
the expression to be converted:
( new_type ) expression
The first operation does not produce any code, as it is just an internal
type change in the compiler. The second one will produce actual code,
unless var already has type long. Note that a conversion between
signed and unsigned objects of the same type does not produce any
code. The cast result will behave with its new type.
char *pc;
int *pi;
pi = (int *)pc;
When using the +strict option, the cast operator will be used to force
the compiler to accept a truncating assignment without producing any
error message:
char c;
int i;
c = (char)i;
The cast operator may be used to override the default evaluation rules.
Assuming both variables a and b are declared as unsigned char, the
following expression:
a == ~b
a == (unsigned char)~b
Priorities
All these operators may be combined together in complex expressions.
The order of evaluation depends on each operator priority. The expres-
sion:
a + b * c
a + (b * c)
a + b + c
is
((a + b) + c)
a = b = c
is
(a = (b = c))
1 Left to Right
post increment/decrement i++ i--
array subscript tab[i]
function call func()
structure/union member str.a
pointer to a member
p->a
2 Right to Left
sizeof operator sizeof i
pre increment/decrement ++i --i
address of &i
content of *p
unary plus/minus
+i -i
binary/logical not
~i !i
cast
(type)i
3 Left to Right
multiply i * j
divide i / j
remainder i % j
4 Left to Right
Addition i + j
substract i - j
5 Left to Right
equal to i == j
not equal i != j
8 Left to Right
binary exclusive or i ^ j
10 Left to Right
binary or i | j
11 Left to Right
logical or i || j
13 Right to Left
conditional expression i ? j : k
14 Right to Left
assignment i = j
multiply assign i *= j
divide assign i /= j
remainder assign i %= j
plus assign
i += j
minus assign
i -= j
left shift assign
right shift assign i <<= j
and assign i >>= j
exclusive or assign i &= j
or assign i ^= j
i |= j
15 Left to Right
comma i, j
There are a few remarks about these levels. The shift operators have a
lower priority than the additive operators, although they are closer to
multiplication/division operations. This may produce an unexpected
result in the following expression:
Assuming that word is a short integer, high and low two unsigned
chars. This expression, which is supposed to combine two bytes con-
catenated into a word, will not produce the expected result. This is
because the addition has a higher priority than the left shift. The group-
ing is actually:
This is clearly wrong. The result will be correct if the binary or operator
is used:
Statements
Statements are language instructions which can be entered only inside
a function body. They describe the function behaviour and they are
placed in a function declaration:
return_type function_name(argument_list)
{
local_declarations
statement_list
}
The sequence entered between the two curly braces is called a block,
and a block is a statement. This will allow several statements to be
assembled together, and to behave syntactically as a single statement,
so in the following description, any indication of statement may be
replaced by any of the C statements, including a block. The statements
defined by the C language are as follows:
Block statement
The syntax of a block statement is:
{
declaration_list
statement_list
}
Expression statement
The syntax of an expression statement is:
expression;
a = b + 1;
func(1, x);
++y;
a;
is permitted by the syntax, and does not produce any code in most of the
cases. This is used when the variable is declared with the volatile
attribute to force the compiler to produce a load instruction. The varia-
ble is just read, which is important for some peripheral registers which
need to be read in order to clear interrupt flags for instance.
and will be used each time a statement has to be entered, but where
nothing has to be done.
Note that when used alone, the ++ and -- operators have the same
behaviour on both sides of the modified objects:
x++;
++x;
If statement
The syntax of an if statement is:
if ( expression )
statement
or
if ( expression )
statement
else
statement
In these two syntaxes, the parentheses around the expression are part of
the syntax, and not subexpression parentheses, so they need to be
entered. Note that there is no then keyword, as the closing brace
behaves exactly the same way. The behaviour of such a statement is as
follows. The expression is first evaluated, and the result is checked
against the two possible cases: true or false. If the expression is a com-
parison, or a set of combined comparisons with logical and/or operators
&&, ||, the result is the result of the comparison evaluation. If the
expression does not use any comparison, the result has to be a numeri-
cal value or an address (from a pointer). The result is true if the value is
not zero, and false is the result is zero. This is equivalent to an implied
comparison with zero :
if (a + b) is equivalent to if ((a + b) != 0)
In the first syntax, the following statement is executed only if the result
of the expression is true. Otherwise it is skipped.
if (a > b)
b = a;
if (a < 10)
++a;
else
a = 0;
if (a < 10)
if (a > 5)
b = 1;
else
b = 0;
the text formatting has no influence on the compiler , and the program
will behave as if it were written:
if (a < 10)
{
if (a > 5)
b = 1;
else
b = 0;
}
if (a < 10)
{
if (a > 5)
b = 1;
}
else
b = 0;
Now, the second if is part of a full block which becomes the first state-
ment of the first if. The else statement can only be associated with the
first if statement.
While statement
The syntax of a while statement is:
while ( expression )
statement
while (p < q)
*p++ = ‘\0’;
In this example, assuming that p and q are two pointers to char varia-
bles, the loop will clear all the characters between pointers p and q if at
the beginning, p is smaller than q. This can be expanded in such a way:
while (p < q)
{
*p = ‘\0’;
++p;
}
Do statement
The syntax of a do statement is:
do
statement
while ( expression ) ;
do
ok = do_it();
while (!ok);
In this example, the function do_it() is executed until its return value
is not zero.
For statement
The syntax of a for statement is:
The for statement is also used to implement a loop, but in a more pow-
erful way than the previous ones. This instruction is equivalent to the
following construct:
expression_1 ;
while ( expression_2 )
{
statement
expression_3 ;
}
In this example, the loop initialization (i = 0), the loop control (i <
10) and the next element control (++i) are displayed on the same line
and the code reading and understanding is enhanced. More complex
controls may be implemented using this syntax, such as list walking:
which walks though a linked list and resets a field, assuming for
instance the following declarations:
struct cell {
struct cell *next;
int value;
} *p, *list_head;
The for statement allows some variations. Any of the three expressions
may be omitted. The behaviour is simple for the first and the third
expressions. If they are omitted, they do not produce any code. This is
different for the second expression because it controls the loop iteration.
If the second expression is omitted, it is replaced by an always true con-
dition, meaning that this creates an endless loop.
main()
{
for (;;)
operate();
}
while (1)
operate();
The sequence operator is useful when a for loop uses several control
variables:
Break statement
The syntax of a break statement is simply:
break ;
This statement has to be placed inside the body statement (a block usu-
ally) of a while, do or for instruction. It stops the execution of the body
statement and jumps to the end of the statement, behaving as if the con-
trolling expression was giving a false result. The remaining instructions
of the including block are simply skipped. The break statement is usu-
ally associated with an if statement to decide if the loop has to be exited
or not.
while (p < q)
{
if (!*p)
break;
*p++ = ‘A’;
}
In this example, the loop body sets a buffer to the character ‘A’ while the
p pointer is smaller than the q pointer. In the body statement, the break
instruction is executed if the current character is a zero. This will exit
the loop and the execution will continue from the statement following
the while block. The break statement in such a case can be considered
as a and condition combined with the while test, as the previous code
could have be written:
The not operator ! has been removed as the while condition is a contin-
uation test, and not a termination test.
When applied to a for loop, the break statement exits the equivalent
body statement from the expanded while construct, meaning that the
third expression, if any was specified, is not evaluated.
In this example, the break instruction will stop the while loop only, thus
continuing the execution with the statement following the while block
(tab[i] = 0).
Continue statement
The syntax of a continue statement is simply:
continue ;
Switch statement
The syntax of a switch statement is:
switch ( expression )
{
case constant_1:
statement_list
case constant_2:
statement_list
default:
statement_list
}
If no case label matches are found, there are two possible behaviours. If
a default label has been defined, the execution continues from the next
statement following it. Otherwise, the full block is skipped.
switch ( get_command() )
{
case ‘L’:
load();
break;
case ‘E’:
edit();
break;
case ‘X’:
save();
case ‘Q’:
quit();
break;
default:
bark();
break;
}
In this example, the break statement will exit the switch statement, and
continue execution at statement ++tab[i], while the continue state-
ment will be applied to the for statement, as it has no meaning for the
switch statement. The execution will continue at the ++i of the for loop.
In this case, it is not possible to use a break statement inside the switch
block to exit the for loop. This can be achieved simply only with a goto
statement, as explained below.
Goto statement
The syntax of a goto label is:
goto label ;
label : statement
{
...
if (test)
goto exit;
...
exit:
}
Here, the label exit is followed by the closing curly brace, and this is a
syntax error. This has to be written by using the empty statement:
exit:
;
}
Return statement
The syntax of a return statement is:
return expression ;
or
return ;
return any value. When the strict option is used (+strict), the compiler
also checks that a function which has a return type is actually returning
something.
There are no other statements in C. All the other features you can find
in some other languages (input/output, file control, mathematics, text
strings) are implemented by library routines. The C standard has also
normalized the basic libraries, thus guaranteeing that those features can
be used regardless of the compiler origin.
Preprocessor
The C preprocessor is a text processor which operates on the C source
before it is actually parsed by the compiler. It provides macro and con-
ditional features very closed to the ones available with most of the
existing assemblers.
Macro Directives
The three macro directives are:
#undef IDENT
The two first syntaxes allow a macro to be defined, and the third syntax
allows a previous definition to be cancelled.
IDENT is a word following the rules for a C identifier, and may use low-
ercase or uppercase characters. For readability reasons, most macro
names are entered uppercase only.
The second syntax allows a replacement with parameters. Note that the
opening brace has to follow immediately the last character of the macro
name, without any whitespace. Otherwise, it is interpreted as the first
syntax and the parameter list along with the parentheses will be part of
the replacement sequence. Each parameter is an identifier, separated
from the others by a comma.
#define SUM(a, b) a + b
This macro defines the word SUM along with two parameters called a
and b. Parameters should appear in the replacement part, and the macro
should be used in the remaining C source with a matching number of
arguments.
x = SUM(y, z);
x = y + z;
The operator # placed before a parameter name will turn it into a text
string by enclosing it by double quotes:
will transform:
ptr = STRING(hello);
into
ptr = “hello”;
will transform:
BIT(port, 3) = 1;
into
port.b_3 = 1;
Without this operator, it would have been impossible to get rid of the
white space between the base name and the bit number, and the com-
piler would have been unable to get the proper syntax.
INC(stdio)
will expand to
\#include “stdio.h”
and will be re-executed after the removal of the prefixing \ thus includ-
ing the file stdio.h.
Hazardous Behaviours
It is important to keep in mind that this replacement is done only on a
text basis, without any attempt to understand the result. This may lead
in a few unexpected side effects.
a = ABS(-b);
a = -b > 0 ? -b : --b;
and now, the last expression will be evaluated as b negated twice, and
optimized in a direct load of b.
x = SUM(y, z) * 2;
x = (y) + (z) * 2;
and now, the priority rules change the expected behaviour to:
x = ((x) + (y)) * 2;
x = ABS(*p++);
In this expression, the pointer will be incremented twice and the result
is wrong coming from the element following the one tested. Unfortu-
nately, there is no syntax trick to avoid this one.
Most of these errors are very difficult to find, because they do not pro-
duce errors at compile time, and because you do not see what is actually
expanded in reading the C source. By reading the example getting the
absolute value of *p++, there is only one increment seen. The extra one
is implied by the macro expansion, but you have to look at the macro
definition to find that. That is why it is important to immediately check
that a name is a macro, this is made easier using only uppercase names
for macros as a convention.
Predefined Symbols
The preprocessor predefines a few symbols with a built-in behaviour.
Those symbols cannot be undefined by a #undef directive and then can-
not be redefined to any other behaviour.
Conditional Directives
The conditional directives are:
#ifdef IDENT
#ifndef IDENT
#if expression
#else
#endif
#elif expression
#ifdef DEBUG
printf(“trace 1\n”);
#endif
If the symbol DEBUG has been defined previously, the line printf... will
be compiled. Otherwise, it is simply skipped.
#if TERM == 1
init_screen();
#else
init_printer();
#endif
If the symbol TERM has been previously defined equal to 1, the line
init_screen(); is compiled, and the line init_printer is
skipped. If TERM is define to anything else, or if TERM is not defined, the
behaviour is the opposite (if TERM is not defined, it is replaced by 0 and
the expression 0 == 1 is false).
#if TERM == 1
...
#elif TERM == 2
...
#elif TERM == 3
...
#else
...
#endif
Control Directives
The control directives are:
#include
#include "filename"
or
#include <filename>
The preprocessor replaces such a line by the full content of the file
whose name is specified between double quotes or angle brackets. A
file specified between double quotes is searched first in the current
directory. A file specified between angle brackets is searched first in
some predefined system directories, or user specified directories. An
error will occur if the file is not found in any of the specified directo-
ries. An included file may contain other #include directives.
#error
#error rest_of_the_line
#ifndef TERM
#error missing definition for TERM
#endif
If the symbol TERM is not defined, the compiler will output an error
message containing the text “missing definition for TERM”, and
will fail to compile the source file.
#line
#line number "filename"
#pragma
#pragma rest_of_the_line
If this field is empty, all the classes are selected, except const.
[] specifies variables
() specifies functions
#pragma space []
Refer to the specific compiler manual for the list of supported space
modifiers.
The #pragma section directive allows the compiler to modify the sec-
tion in which objects are allocated. The compiler splits the various pro-
gram components in the following default sections:
Each of these sections can be renamed. The compiler then creates a new
assembler section with the proper name and attributes, and produces
there the matching objects. The compiler will prepend a dot . to the
provided name, and will check that the final name is not longer than 14
characters.
@tiny { name } changes the .bsct section name (or @dir depend-
ing on the actual target)
Note that [] may be used instead of {} with the same effect for sections
which are not sensitive to the initialization or not (.const and .eeprom).
If the name part is omitted between the parentheses, the matching sec-
tion name turns back to its default value.
The compiler creates a section named .mor which replaces the default
.data section. The variable MOR is then created in the .mor section. The
compiler reverts to the original .data section for the next initialized var-
iables.
The interrupt vectors can be located in a separate section with the fol-
lowing code:
By default, the assembly lines entered between the #asm and #endasm
directives are not preprocessed. It is possible to apply the preprocessor
#defines to the assembly text by specifying the -pad compiler option.
Index 1
cast operator 4-17 first expression 5-7
comment end 2-2 float 2-7
comment start 2-2 float type 3-2, 4-5
Comments 2-2 for statement 5-7, 5-8
conditional directives 6-1 function 2-10, 3-11
conditional feature 6-1 function body 5-1
conditional operator 4-16 function call operator 4-14
configuration directive 6-11
const keyword 3-5 G
const modifier 3-6 global variable 3-15
constant 2-4, 3-6 goto label 5-13
Constants 4-2 goto statement 5-13
constants 2-3
continue statement 5-10 I
control directives 6-1, 6-10 identifier 2-3
current line number 6-10 identifiers 2-3
if statement 5-3, 5-4
D index 2-9
declaration 2-5 indexing operator 4-14
declaration, of an object 3-1 initial value 3-1
decrementation 4-13 initialization of a function 3-13
default label 5-12 initialization, of an array 3-4
do statement 5-6 int 2-5, 3-2
double 3-2 int type 4-4
double type 4-5 integer type 2-5
integer variable 3-2
E
eeprom 6-12 K
else keyword 5-4 K&R syntax 3-12
else statement 5-4 Kernigan and Ritchie syntax 3-11
enum keyword 3-10 keyword 2-3
enumeration 2-6, 2-10, 3-10 keyword name 2-3
error message 6-10 keywords 2-3
escape sequence 4-3
exclusive or, bitwise 4-9 L
expression 4-1, 5-2 label 5-14
extern keyword 3-13 leave, a function 5-14
line terminator 2-1
F load instruction 5-3
false, logical value 4-10 local variable 3-15
field 2-9 local variables 3-13
file scope 3-14
2 Index
logical true 4-10 pointer keyword 3-3
long 2-5 pointer return 4-13
long double 2-7, 3-2 pointer to void 4-11
long double type 4-5 pointers to arrays 3-5
long enumerations 3-10 pointers to pointers 3-5
long int 2-5 post incrementation 4-13
loop body 5-7 pre incrementation 4-13
loop initialization 5-7 predefined space modifiers 3-5
loop iteration 5-7 preprocessing directives 2-2
lowercase 2-3 preprocessor 6-1
L-value expression 4-11 preprocessor directive 6-1
prototyped syntax 3-11
M punctuation 2-3
macro 6-1 punctuators 2-4
macro directives 6-1
main 2-10 R
memory model 2-8 real variable 3-2
memory modification 4-11 register class 5-2
memory space 6-11 register keyword 3-14
modifier 3-5 register object 3-14
register variable 3-14
N registers modification 4-11
name 3-1 return 5-14
nop instruction 5-2 return statement 5-14
not operator 5-9 returned value type 3-11
numerical constant 3-3 right shift operator 4-9
R-value expression 4-11
O
object class 6-11 S
operands 4-7 scope, of variable 3-13
operator 2-4, 4-14 second expression 5-7
Operators 4-7 semicolon 5-2
operators 2-3 sequence operator 4-17
or operator 5-4 set of variables 4-1
or, bitwise 4-9 short 2-5
short int 2-5
P signed 2-5
parameters 3-12 signed keyword 3-2
peripheral registers 5-3 size of a pointer 2-8
physical register 3-14 sizeof type 4-7
pointer 2-8 space modifiers 3-5
special ponctuator 3-12
Index 3
Statements 5-1
static keyword 3-14, 3-15
stop compiler optimizing 3-6
storage class 3-1, 3-13
string constant 4-5
struct keyword 3-8
structure 2-9, 3-8
structure initialization 3-8
switch statement 5-11
T
tag name 3-8
terminator character 5-1
then keyword 5-4
third expression 5-7
true, logical value 4-10
type 3-1
type equivalent 3-15
typedef keyword 3-15
U
union 2-9, 3-10
unsigned 2-5
unsigned char 3-2
unsigned int 3-2, 4-4
unsigned keyword 3-2
unsigned short 2-8
uppercase 2-3
useless expression 5-3
V
variable number of arguments 3-12
Variables 4-2
void 5-14
volatile attribute 5-3
volatile keyword 3-5
volatile modifier 3-6
W
while statement 5-5
wide character 4-4, 4-7
wide string 4-7
4 Index