Operators
are tokens that cause a computation to occur when applied to variables. C
provides the following operators;
& | Address |
* | Indirection |
+ | Unary plus |
- | Unary minus |
~ | Bitwise compliment |
! | Logical negation |
++ | As a prefix; preincrement |
As a suffix; postincrement | |
-- | As a prefix; predecrement |
As a suffix; postdecrement | |
+ | Addition |
- | Subtraction |
* | Multiply |
/ | Divide |
% | Remainder |
<< | Shift left |
>> | Shift right |
& | Bitwise AND |
| | Bitwise OR |
^ | Bitwise XOR |
&& | Logical AND |
|| | Logical OR |
= | Assignment |
*= | Assign product |
/= | Assign quotient |
%= | Assign remainder (modulus) |
+= | Assign sum |
-= | Assign difference |
<<= | Assign left shift |
>>= | Assign right shift |
&= | Assign bitwise AND |
|= | Assign bitwise OR |
^= | Assign bitwise XOR |
< | Less than |
> | Greater than |
<= | Less than or equal to |
>= | Greater than or equal to |
== | Equal to |
!= | Not equal to |
. | Direct component selector |
-> | Indirect component selector |
a ? x:y | "If a is true then x else y" |
[] | Define arrays |
() | Parenthesis isolate conditions and expressions |
... | Ellipsis are used in formal parameter lists of |
function prototypes to show a variable number of | |
parameters or parameters of varying types. |
To illustrate some more commonly used operators consider the following short
program;
main()
{
int a;
int b;
int c;
a = 5; /* Assign a value of 5 to variable 'a' */
b = a / 2; /* Assign the value of 'a' divided by two to variable 'b' */
c = b * 2; /* Assign the value of 'b' multiplied by two to variable 'c' */
if (a == c) /* Test if 'a' holds the same value as 'c' */
puts("Variable 'a' is an even number");
else
puts("Variable 'a' is an odd number");
}
Normally when incrementing the value of a variable you would write something
like;
x = x + 1
C provides the incremental operator '++' as well so that you can write;
x++
Similarly you can decrement the value of a variable using '--' as;
x--
All the other mathematical operators may be used the same, so in a C program
you can write in shorthand;
NORMAL | C |
x = x + 1 | x++ |
x = x - 1 | x-- |
x = x * 2 | x *= 2 |
x = x / y | x /= y |
x = x % 5 | x %= 5 |
and so on.
Functions are the source code procedures that comprise a C
program. They follow the general form;
return_type function_name(parameter_list)
{
statements
}
The return_type specifies the data type that will be returned by the function; char, int, double, void &c.
The code within a C function is invisible to any other C function, and jumps may not be made from one function into the middle of another, although functions may call other functions. Also, functions cannot be defined within functions, only within source modules.
Parameters may be passed to a function either by value, or by
reference. If a parameter is passed by value, then only a copy of the current
value of the parameter is passed to the function. A parameter passed by
reference however, is a pointer to the actual parameter that may then be changed
by the function.
The following example passes two parameters by value to a
function, funca(), which attempts to change the value of the variables passed to
it. And then passes the same two parameters by reference to funcb() which also
attempts to modify their values.
#include <stdio.h>
int funca(int x, int y)
{
/* This function receives two parameters by value, x and y */
x = x * 2;
y = y * 2;
printf
("\nValue of x in funca() %d value of y in funca() %d",x,y);
return(x);
}
int funcb(int *x, int *y)
{
/* This function receives two parameters by reference, x and y
*/
*x = *x * 2;
*y = *y * 2;
printf
("\nValue of x in funcb() %d value of y in funcb() %d",*x,*y);
return(*x);
}
main()
{
int x;
int y;
int z;
x = 5;
y = 7;
z = funca(x,y);
z = funcb(&x,&y);
printf ("\nValue of x %d value of y %d value of z %d",x,y,z);
}
Actually funcb() does not change the values of the parameters
it receives. Rather it changes the contents of the memory addresses pointed to
by the received parameters. While funca() receives the values of variables 'x'
and 'y' from function main(), funcb() receives the memory addresses of the
variables 'x' and 'y' from function main().
The following program passes an array to a function, funca(),
which initialises the array elements;
#include <stdio.h>
void funca(int x[])
{
int n;
for(n = 0; n < 100; n++)
x[n] = n;
}
main()
{
int array[100];
int counter;
funca(array);
for(counter = 0; counter < 100; counter++)
printf ("\nValue of element %d is %d",counter,array[counter]);
}
The parameter of funca() 'int x[]' is declared to be an array
of any length. This works because the compiler passes the address of the start
of the array parameter to the function, rather than the value of the individual
elements. This does of course mean that the function can change the value of the
array elements. To prevent a function from changing the values you can specify
the parameter as type 'const';
funca(const int x[])
{
}
This will then generate a compiler error at the line that
attempts to write a value to the array. However, specifying a parameter to be
const does not protect the parameter from indirect assignment as the following
program illustrates;
#include <stdio.h>
int funca(const int x[])
{
int *ptr;
int n;
/* This line gives a 'suspicious pointer conversion warning' */
/* because x is a const pointer, and ptr is not */
ptr = x;
for(n = 0; n < 100; n++)
{
*ptr = n;
ptr++;
}
}
main()
{
int array[100];
int counter;
funca(array);
for(counter = 0; counter < 100; counter++)
printf ("\nValue of element %d is %d",counter,array[counter]);
}
C allows parameters to be passed from the operating system to
the program when it starts executing through two parameters; argc and argv[], as
follows;
#include <stdio.h>
main(int argc, char *argv[])
{
int n;
for(n = 0; n < argc; n++)
printf ("\nParameter %d equals %s",n,argv[n]);
}
Parameter argc holds the number of parameters passed to the program, and the array argv[] holds the addresses of each parameter passed. argv[0] is always the program name. This feature may be put to good use in applications that need to access system files. Consider the following scenario:
A simple database application stores its data in a single file
called "data.dat". The application needs to be created so that it may
be stored in any directory on either a floppy diskette or a hard disk, and
executed both from within the host directory and through a DOS search path. To
work correctly the application must always know where to find the data file;
"data.dat". This is solved by assuming that the data file will be in
the same directory as the executable module, a not unreasonable restriction to
place upon the operator. The following code fragment then illustrates how an
application may apply this algorithm into practice to be always able to locate a
desired system file:
#include <string.h>
char system_file_name[160];
void main(int argc,char *argv[])
{
char *data_file = "DATA.DAT";
char *p;
strcpy(system_file_name,argv[0]);
p = strstr(system_file_name,".EXE");
if (p == NULL)
{
/* The executable is a .COM file */
p = strstr(system_file_name,".COM");
}
/* Now back track to the last '\' character in the file name */
while(*(p - 1) != '\\')
p--;
strcpy(p,data_file);
}
In practice this code creates a string in system_file_name
that is comprised of path\data.dat, so if for example the executable file is
called "test.exe" and resides in the directory \borlandc, then
system_file_name will be assigned with: \borlandc\data.dat
The command 'return' is used to return immediately from a
function. If the function was declared with a return data type, then return
should be used with a parameter of the same data type.
Prototypes for functions allow the C compiler to check that the type of data being passed to and from functions is correct. This is very important to prevent data overflowing its allocated storage space into other variables areas.
A function prototype is placed at the beginning of the program, after any preprocessor commands, such as #include <stdio.h>, and before the declaration of any functions.
Links: Home C Programming Guide C++ programming Guide