v3.0.1 / chapter 2 of 5 / 01 aug 05 / greg goebel / public domain
* C supports a flexible set of variable types and structures, as well as common arithmetic and math functions along with a few interesting operators that are unique to C. This chapter explains them in detail, and ends with a short discussion of preprocessor commands.
* C includes the following fundamental data types:
____________________________________________________________________
type use size range
____________________________________________________________________
char character 8 bits -128 to 127
unsigned char character 8 bits 0 to 255
short integer 16 bits -32,768 to 32,767
unsigned short integer 16 bits 0 to 65,535
int integer 32 bits -32,768 to 32,767
unsigned int integer 32 bits 0 to 65,535
long integer 32 bits -2,147,483,648 to 2,147,483,647
unsigned long integer 32 bits 0 to 4,294,967,295
float real 32 bits 1.2E-38 to 3.4E+38
double real 64 bits 2.2E-308 to 1.8E+308
long double real 128 bits 3.4E-4932 to 1.2E+4932
____________________________________________________________________
short <= int <= long
float <= double <= long double
Declarations are of the form:
int myval, tmp1, tmp2;
unsigned int marker1 = 1, marker2 = 10;
float magnitude, phase;
C allows several variables to be declared in the same statement, with commas
separating the declarations. The variables can be initialized when declared.
Constant values for declarations can be declared in various formats:
128: decimal int
256u: decimal unsigned int
512l: decimal long int
0xAF: hex int
0173: octal int
0.243: float
0.1732f: float
15.75E2: float
'a': character
"giday": string
'\a': alarm (beep) character
'\p': backspace
'\f': formfeed
'\n': newline
'\r': carriage return
'\t': horizontal tab
'\v': vertical tab
'\\': backslash
'\?': question mark
'\'': single quote
'\"': double quote
'\0NN': character code in octal
'\xNN': character code in hex
'\0': null character
#define PI 3.141592654
const int a;
int myarray[10];
unsigned int list[5] = { 10, 15, 12, 19, 23 };
float rdata[128], grid[5][5];
for( i = 0; i <= 127; i = i + 1 )
{
printf ( "\f\n", rdata[i] );
}
* Of particular importance are arrays of characters, which are used to store
strings:
char s[128];
strcpy( s, "This is a test!");
The curious reader may wonder why the "strcpy()" function is needed to
initialize the string. It might seem to be easier to do:
char s[128] = "This is a test!";
* C allows you to define pointers that contain the address of a variable or
an array. You could, for example, define a pointer named:
int *ptr;
*ptr = 345;
int tmp;
somefunc( &tmp );
Pointers are useful because they allow a a function to return a value through a parameter variable. Otherwise, the function will simply get the data the variable contains and have no access to the variable itself.
One peculiar aspect of C is that the name of an array actually specifies a
pointer to the first element in the array. For example, if you declare:
char s[256];
somefunc( s )
s[12]
There are more peculiarities to strings in C. Another interesting point is
that a string literal actually evaluates to a pointer to the string it
defines. This means that if you perform the following operation:
char *p;
p = "Life, the Universe, & Everything!";
char ch;
ch = "Life, the Universe, & Everything!"[0];
This is very well and good, but why care? The reason to care is because
this explains why the operation:
char s[128] = "This is a test!";
This will seem to work for a while, until the program tries to store more bytes into that block than can fit into the 16 bytes reserved for "This is a test!". Since C is poor about bounds checking, this may cause all kinds of trouble.
This is why "strcpy()" is necessary, unless you simply want to define a
string that will not be modified or will not be used to store more data than
it is initialized to. In that case, you can perform:
char *p;
p = "Life, the Universe, & Everything! ";
/* strparm.c */
#include <stdio.h>
#include <string.h>
char *strtest( char *a, char *b );
main ()
{
char a[256],
b[256],
c[256];
strcpy( a, "STRING A: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" );
strcpy( b, "STRING B: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" );
strcpy( c, "STRING C: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" );
printf( "Initial values of strings:\n" );
printf( "\n" );
printf( " a = %s\n", a );
printf( " b = %s\n", b );
printf( " c = %s\n", c );
printf( "\n" );
strcpy( c, strtest( a, b ));
printf( "Final values of strings:\n" );
printf( "\n" );
printf( " a = %s\n", a );
printf( " b = %s\n", b );
printf( " c = %s\n", c );
printf( "\n" );
}
char *strtest( char *x, char *y )
{
printf( "Values passed to function:\n" );
printf( "\n" );
printf( " x = %s\n", x );
printf( " y = %s\n", y );
printf( "\n" );
strcpy( y, "NEWSTRING B: abcdefghijklmnopqrstuvwxyz0123456789" );
return( "NEWSTRING C: abcdefghijklmnopqrstuvwxyz0123456789" );
}
/* struct.c */
#include <stdio.h>
#include <string.h>
struct person /* Define structure type. */
{
char name[50];
int age;
float wage;
};
void display( struct person );
void main()
{
struct person m; /* Declare an instance of it. */
strcpy( m.name, "Coyote, Wile E." ); /* Initialize it. */
m.age = 41;
m.wage = 25.50f;
display( m );
}
void display( struct person p )
{
printf( "Name: %s\n", p.name );
printf( "Age: %d\n", p.age );
printf( "Wage: %4.2f\n", p.wage );
}
You can copy a structure to another structure with a single assignment
statement, as long as the structures are of the same type:
struct person m, n;
...
m = n;
struct person group[10];
...
strcpy( group[5].name, "McQuack, Launchpad" );
struct trip_rec
{
struct person traveler;
char dest[50];
int date[3];
int duration;
float cost;
}
struct trip_rec t1;
...
strcpy( t1.traveler.name, "Martian, Marvin" );
setstruct( &mystruct );
strcpy( sptr->name, "Leghorn, Foghorn" );
sptr->age = 50;
sptr->wage = 12.98;
union usample
{
char ch;
int x;
}
* The following example program shows a practical use of structures. It
tests a set of functions that perform operations on three-dimensional
vectors:
vadd(): Add two vectors.
vsub(): Subtract two vectors.
vdot(): Vector dot product.
vcross(): Vector cross product.
vnorm(): Norm (magnitude) of vector.
vangle(): Angle between two vectors.
vprint(): Print out vector.
/* vector.c */
#include <stdio.h>
#include <math.h>
#define PI 3.141592654
struct v
{
double i, j, k;
};
void vadd( struct v, struct v, struct v* );
void vprint( struct v );
void vsub( struct v, struct v, struct v* );
double vnorm( struct v );
double vdot( struct v, struct v );
double vangle( struct v, struct v );
void vcross( struct v, struct v, struct v* );
void main()
{
struct v v1 = { 1, 2, 3 }, v2 = { 30, 50, 100 }, v3;
double a;
printf( "Sample Vector 1: " );
vprint( v1 );
printf( "Sample Vector 2: " );
vprint( v2 );
vadd( v1, v2, &v3 );
printf( "Vector Add: " );
vprint( v3 );
vsub( v1, v2, &v3 );
printf( "Vector Subtract: " );
vprint( v3 );
vcross( v1, v2, &v3 );
printf( "Cross Product: " );
vprint( v3 );
printf( "\n" );
printf( "Vector 1 Norm: %f\n", vnorm( v1 ) );
printf( "Vector 2 Norm: %f\n", vnorm( v2 ) );
printf( "Dot Product: %f\n", vdot( v1, v2 ) );
a = 180 * vangle( v1, v2) / PI ;
printf( "Angle: %3f degrees.\n", a );
}
void vadd( struct v a, struct v b, struct v *c ) /* Add vectors. */
{
c->i = a.i + b.i;
c->j = a.j + b.j;
c->k = a.k + b.k;
}
double vangle( struct v a, struct v b ) /* Get angle between vectors. */
{
double c;
c = vdot( a, b ) / ( vnorm( a ) * vnorm( b ) );
return( atan( sqrt( ( 1 - ( c * c ) ) / ( c * c ) ) ) );
}
void vcross( struct v a, struct v b, struct v *c ) /* Cross product. */
{
c->i = a.j * b.k - a.k * b.j;
c->j = a.k * b.i - a.i * b.k;
c->k = a.i * b.j - a.j * b.i;
}
double vdot( struct v a, struct v b ) /* Dot product of vectors. */
{
return( a.i * b.i + a.j * b.j + a.k * b.k );
}
double vnorm ( struct v a ) /* Norm of vectors. */
{
return( sqrt( ( a.i * a.i ) + ( a.j * a.j ) + ( a.k * a.k ) ) );
}
void vprint ( struct v a ) /* Print vector. */
{
printf( " I = %6.2f J = %6.2f K = %6.2f\n", a.i, a.j, a.k );
}
void vsub ( struct v a, struct v b, struct v *c ) /* Subtract vectors. */
{
c->i = a.i - b.i;
c->j = a.j - b.j;
c->k = a.k - b.k;
}
#include <stdio.h>
void testfunc( void );
void main()
{
int ctr;
for( ctr = 1; ctr < 8; ++ctr )
{
testfunc();
}
}
void testfunc( void )
{
static int v;
printf( "%d\n", 2*v );
++v;
}
0
2
4
6
8
10
12
14
* There are two other variable declarations that you should recognize though you should have little reason to use them: "register", which declares that a variable should be assigned to a CPU register, and "volatile", which tells the compiler that the contents of the variable may change spontaneously.
There is more and less than meets the eye to these declarations. The "register" declaration is discretionary: the variable will be loaded into a CPU register if it can, and if not it will be loaded into memory as normal. Since a good optimizing compiler will try to make the best use of CPU registers anyway, this is not in general all that useful a thing to do.
The "volatile" declaration appears ridiculous at first sight, something like one of those "joke" computer commands like "halt and catch fire". Actually, it's used to describe a hardware register that can change independently of program operation, such as the register for a realtime clock.
* C is fairly flexible in conversions between data types. In many cases, the type conversion will happen transparently. If you convert from a "char" to a "short" data type, or from an "int" to a "long" data type, for example, the converted data type can easily accommodate any value in the original data type.
Converting from a bigger to a smaller data type can lead to odd errors. The
same is true for conversions between signed and unsigned data types. For
this reason, type conversions should be handled carefully, and it is usually
preferable to do them explicitly, using a "cast" operation. For example, to
perform a cast conversion from an "int" value to a "float" value:
int a;
float b;
...
b = (float)a;
enum day
{
saturday, sunday, monday, tuesday, wednesday, thursday, friday
};
enum temps
{
zero = 0, freeze = 32, boil = 220
};
enum day today = wednesday;
* Finally, you can use the "typedef" declaration to define your own data
types:
typedef str ch[128];
str name;
* C supports the following arithmetic operators:
c = a * b; /* multiplication */
c = a / b; /* division */
c = a % b; /* mod (remainder division) */
c = a + b; /* addition */
c = a - b; /* subtraction */
++a; /* increment */
--a; /* decrement */
Finally, C supports a set of bitwise operations:
a = ~a; /* bit complement */
a = b << c; /* shift b left by number of bits stored in c */
a = b >> c; /* shift b right by number of bits stored in c */
a = b & c; /* b AND c */
a = b ^ c; /* b XOR c */
a = b | c; /* b OR c */
a = a * b; a *= b;
a = a / b; a /= b;
a = a % b; a %= b;
a = a + b; a += b;
a = a - b; a -= b;
a = a << b; a <<= b;
a = a >> b; a >>= b;
a = a & b; a &= b;
a = a ^ b; a ^= b;
a = a | b; a |= b;
a == b: equals
a != b: not equals
a < b: less than
a > b: greater than
a <= b: less than or equals
a >= b: greater than or equals
a = b * ( b < 2 ) + 10 * ( b >= 2 );
a = ( b < 2 ) ? b : 10;
There are similar logical operators:
!: logical NOT
&&: logical AND
||: logical OR
if(( A == 5 ) && ( B == 10 ))
{
...
}
int tvar;
...
printf ( "Size = %d\n", sizeof( int ) );
* The precedence of these operators in math functions -- that is, which ones
are evaluated before others -- are defined as follows, reading from the
highest precedence to the lowest:
() [] -> .
! ~ ++ -- (cast)* & sizeof - (minus prefix)
* / %
+ -
<< >>
< <= > >=
== !=
&
^
|
&&
||
?:
= += -= *= /= %= >>= <<= &=
^= |=
,
* Advanced math operations are available as library functions. These will be discussed in a later chapter.
* We've already seen the "#include" and "#define" preprocessor directives. The C preprocessor supports several other directives as well. All such directives start with a "#" to allow them to be distinguished from C language commands.
As explained in the first chapter, the "#include" directive allows you to
insert the contents of other files in your C source code:
#include <stdio.h>
#include "\home\mydefs.h"
* Also as explained in the first chapter, the "#define" directive can be used
to specify symbols to be substituted for specific strings of text:
#define PI 3.141592654
...
a = PI * b;
The "#define" directive can be used to create function-like macros that allow
parameter substitution. For example:
#define ABS(value) ( (value) >=0 ? (value) : -(value) )
printf( "Absolute value of x = %d\n", ABS(x) );
val = ABS(x++);
val = ( (x++) >=0 ? (x++) : -(x++) )
#undef PI
#if
#else
#elif
#endif
#if WIN == 1
#include "WIN.H"
#elif MAC == 1
#include "MAC.H"
#else
#include "LINUX.H"
#endif
#if defined( DEBUG )
printf( "Debug mode!\n);
#endif
#if !defined( DEBUG )
printf( "Not debug mode!\n);
#endif