C Hints and TipsThe preprocessor and other fun toys
Operators and Types Most C operators can work with many different
types of numbers +, -, *, /, %, >>, ++, etc.. int, double, float, char, etc..
Understanding how the compiler interprets your instructions is crucial! it will perform some conversions, but they might not
always make sense (to you!) Like the rest of programming, experience is
essential
Type Conversions Types in C can be thought of as having a heirarchy
char -> int -> float -> double ie, double is the ‘broadest’ type, char is the ‘narrowest’
C will convert variables to broader types in certain conditions when the other operand of the operator is of a broader type when passing to a function which takes a larger type
It will NOT convert in other situations! some compilers will shrink values to fit into narrower types, but
data loss may occur It will ONLY consider the current operator, NOT any future
operators!! this means that unexpected results can be obtained in some
very common circumstances..
Common Errors Consider the following code fragment:
int a = 16 ;double d ;d = a / 3 ;
What will the final value of d be?!? 5.33333? 5? something else?
Keep in mind that ‘/’ and ‘=‘ are totally separate operators when determining what, if any, type conversions to perform on
the operands of ‘a/3’, the ‘=‘ operator which will follow is ignored!
More Examples.. What will be the result of these statements?
char c = ‘a’ ;int i = a << 8 ;double d = 15 / 3 ;d = 15 / 3.0 ; // is this different?!i = 15.632 * 4 ; // will this work?
You can write a simple program with printf statements after every line to display these values..
The C Pre-Processor Directives The C pre-processor is a program which is run on
a C source file before compiling Its main function is to modify the program in
certain ways removes comments substitutes certain identifiers includes other files
It is a very powerful tool, and knowing how to use it is an essential C programming skill!
Using Pre-Processor Directives Unlike normal C statements, spacing is important for pre-
processor directives each directive should be on a single line, with the first character
always ‘#’ directives are terminated by a newline character, and should
NOT have a ‘;’ at the end! The pre-processor is automatically run when you compile the
program the output is sent straight to the ‘real’ compiler
You can also run the pre-processor separately with the command ‘cpp’ test this on one of your C source files, and have a look at the
result, it might explain a few things!
#include Directives The most commonly used pre-processor directive is #include
inserts the named file at the current position <> brackets mean the file is in the system directories “” quotes mean that the filename is relative to the current directory
Examples:#include <stdio.h> includes the system header
/usr/include/stdio.h#include “blah.h” includes the file blah.h in the
current directory#include “headers/blah.c” includes the file blah.h in the
headers subdirectory Almost all #include statements you will use will be system header
files, so <> are used
#define Directive #define is used to declare constants and macros Syntax (constants):
#define NAME replacement text for the remainder of the file, NAME is replaced with
whatever follows on the #define line #define statements should be used for all constants for
which a name is more meaningful than a value
The macro form of #define is beyond the scope of this course..
#define Example#include <stdio.h>
#define ARRAY_SIZE 50
int main ( void ){int ar[ARRAY_SIZE], i ;
for ( i = 0 ; i < ARRAY_SIZE ; i++ ){……
}}
The value 50 is substituted for ARRAY_SIZE wherever it appears…
Why Use #define? Using constants is considered good programming practice,
for a number of reasons: it makes your code much more readable changing the value requires on ONE change, instead of
possibly hundreds throughout your program you do not have to remember the actual values when
programming consider colour tables, where each colour has a number – it is
MUCH easier to remember BLUE than a random pallete number.. A good rule of thumb:
if you use a value more than once (with the same meaning), #define it!
Many libraries (that you #include) will have many constants defined already: <math.h> has M_PI defined!
#undef Directive The opposite of #define Clears a previously #defined constant or
macro
#if Directive Simple control statement, allowing very basic comparisons to be
made Three basic forms:
#ifdef perform action if constant/macro exists
#ifndef perform action if constant/macro does NOT exist
#if CONST == VALUE perform action if constant has the given value
The actions are all the lines after the #if directive, until the #endif line is encountered
these can be C statements, other pre-processor directives, or anything else..
Basically, the lines between #if and #endif are only included if the condition of the #if directive are met
#if Example#include <stdio.h>
#define DEBUG 0
int main ( void ){
#if DEBUG == 1printf ( “The program has started\n” ) ;
#endif
}
The printf statement is ONLY run if DEBUG is equal to 1. In this example it is not, so nothing is printed! This is a very useful way to develop programs, as it allows for messages to be displayed when writing the code, then easily turned off for the ‘real’ program.
Warnings and Compiler Messages The C compiler is very forgiving
many ‘errors’ are not treated as such, and compilation is allowed to continue
In some cases, ‘warnings’ are generated indicate that your code has done something a little
naughty, but not serious enough to warrant an error Warning messages should not be ignored
they often mean you have made a mistake which will cause the program to crash when run, or do the wrong thing!
the only warnings you can safely ignore are the ones you expect, when you know your code is correct