4 - Some theoretical C programming with more basics, Makefiles and styles.
===============================================================================
This section will be quite short, here I'll explain how you should think
when you make ideas for a program and the difference between the Bell Labs
(K&R - Kerninghan and Ritche) style and student style.
So let's start with how to plan and think when you do a program.
-------------------------------------------------------------------------------
If you plan at all when you make a program, and you should if you want it
to work well, you should take a paper and a pen and draw what you want each
function to do, make each function as a little box and start, just free
dream thinking, let me illustrate what I mean with an example:
-------------------------------------------------------------------------------
___________________________
| |
| main function |
| calls the other functions |
|___________________________| ______________________
| | |
| | usage function... |
|-----------------------------| if ( argc != 2 ) {} |
| | return -1 if not. |
| |______________________|
|
| ______________________
| | |
| | funct1 should import |
|-----------------------------| an argc and return |
| that + itself -1 |
|______________________|
|
|
|
|
__________|___________
| |
| funct2 should printf |
| the resault of |
| funct1 |
|______________________|
-------------------------------------------------------------------------------
Here we have a number of boxes which each resembles a function, with little
notes in them, now let's make the program that this example illustrates:
//----------------------------------------------------------
#include
#include
void usage ( int i, char *myname );
int funct1 ( int i );
void funct2 ( int i );
int main ( int argc, char **argv )
{
usage ( argc, argv[0] );
funct2 ( funct1 ( argc ) );
return 0;
}
void usage ( int i, char *myname )
{
if ( i != 2 ) {
fprintf ( stderr, "Usage: %s
return -1;
}
}
int funct1 ( int i )
{
int x;
x = ( i + i - 1 );
return ( x );
}
void funct2 ( int i )
{
printf ( "The resault of argc + it self - 1 = %i\n", i );
}
//----------------------------------------------------------
There are 4 new things in this code that I will explain before doing anything
else, the new things are:
#include
This we include because we use the exit() function.
This means that the exit() function with syntax is described in unistd.h.
And then we have the:
exit ( -1 );
This means kill the program with status unsuccessful, exit doesn't return
anything.
And then:
funct2 ( funct1 ( argc ) );
and
return ( x );
Now, funct1( argc ) will return something from it's "return ( x );", which
in this case is an integer, and so we can use "funct1( argc )" as an integer,
so when we say "funct2 ( funct1 ( argc ) );" we use the output of
"funct1( argc )" as the input to funct2().
If you dont really understand it, read the code again and what I just said,
and if you still dont understand it, dont worry, I'll explain it again
later.
So now, if you read the source and compare it to the idea with the boxes
you'll come to the conclution that the boxes we drew are the same idea
as the program, just written in a human way instead of a computer way.
This way of thinking is also called "modular thinking", that every function
is a part and you can add as many parts as you want as long as the
main program allows them to integrate with it.
When you start to think modular, it will be really easy to make and think
like the functions, and it will make programming very much easyer.
Don't just read this as text and then continue, make sure to put the example
code in a file and compile it so you can try it, and play around with it, like
rewriting some of the code just to see what happens.
-------------------------------------------------------------------------------
Now let's take a look on how to make a Makefile.
Let's use our last program as an example, so let's cut it up in several
files (it's easyer if you make a dedicated directory for this):
program1.h:
//----------------------------------------------------------
#include
#include
void usage ( int i, char *myname );
int funct1 ( int i );
void funct2 ( int i );
//----------------------------------------------------------
main.c
//----------------------------------------------------------
#include "program1.h"
int main ( int argc, char **argv )
{
usage ( argc, argv[0] );
funct2 ( funct1 ( argc ) );
return 0;
}
//----------------------------------------------------------
usage.c
//----------------------------------------------------------
#include "program1.h"
void usage ( int i, char *myname )
{
if ( i != 2 ) {
fprintf ( stderr, "Usage: %s
exit ( -1 );
}
}
//----------------------------------------------------------
funct1.c
//----------------------------------------------------------
#include "program1.h"
int funct1 ( int i )
{
int x;
x = ( i + i - 1 );
return ( x );
}
//----------------------------------------------------------
funct2.c
//----------------------------------------------------------
#include "program1.h"
void funct2 ( int i )
{
printf ( "The resault of argc + it self - 1 = %i\n", i );
}
//----------------------------------------------------------
And then we make the Makefile:
//----------------------------------------------------------
HDRS = program1.h
OBJS = main.o usage.o funct1.o funct2.o
SRCS = main.c usage.c funct1.c funct2.c
CC = gcc -g -Wall -O3
all: $(OBJS) program1
main.o: $(HDRS) main.c
$(CC) -c main.c
usage.o: $(HDRS) usage.c
$(CC) -c usage.c
funct1.o: $(HDRS) funct1.c
$(CC) -c funct1.c
funct2.o: $(HDRS) funct2.c
$(CC) -c funct2.c
program1: $(OBJS)
$(CC) main.o usage.o funct1.o funct2.o -o program1
clean:
rm -rf *.o core program1 *~
//----------------------------------------------------------
Now let me explain the Makefile.
First, if there is more then 2 spaces in a row in the Makefile then it's
a TAB and not spaces.
Now first in the Makefile we set the variables.
HDRS, OBJS, SRCS and CC.
HDRS is the header files to be included, and here we made our own, so that
we can inclue it in every file, because the header file is in the same dir
as the source and not in any real include dir we have it enclosed by
quotes "" instead of <>.
Then we set OBJS, which is the object files that should be created from the
source..
Then we set the SRCS, which is the source files, that is all the files
we are going to compile.
And then we set CC, which is the C Compiler with or without flags..
You can set the flags separatly with "CFLAGS = -g -Wall -O3" or whatever
flags you want and use them as "$(CC) $(CFLAGS)", but here we set the
flags "-g -Wall -O3" in the CC, and just so you know, "-g" means
debugging information, I'll explain this later, and "-Wall" means "Warn all"
and it will warn about any error, even if it doesnt matter if it's there.
And then we have "-O3" wich menans "Optimize 3 levels", this will optimize
your bianry (compiled source - program).
Then we have:
"all" is the default action when you type "make" and it will run the program1
'function' later in the Makefile.
all: $(OBJS) program1
This tells the compiler how to do the main.o (object) file.
main.o: $(HDRS) main.c
$(CC) -c main.c
This tells the compiler how to do the usage.o file.
usage.o: $(HDRS) usage.c
$(CC) -c usage.c
And the same for funct1 and funct2 ......
And here is the program1 'function'.
This will link the object files to a binary program named "program1",
this Makefile is made so that if it dont find an object file at this point
it will make it from the source.
program1: $(OBJS)
$(CC) main.o usage.o funct1.o funct2.o -o program1
And finally the clean 'function', so if you type "make clean" it will
remove all object files, any core file etc.
clean:
rm -rf *.o core program1 *~
-------------------------------------------------------------------------------
That was is a recursive Makefile ....
The same Makefile but not recursive would look like this:
//----------------------------------------------------------
CC = gcc -g -Wall -O3
all: program1
program1:
$(CC) -c main.c
$(CC) -c usage.c
$(CC) -c funct1.c
$(CC) -c funct2.c
$(CC) main.o usage.o funct1.o funct2.o -o program1
clean:
rm -rf *.o core program1 *~
//----------------------------------------------------------
It's about the same thing, but it wont act as 'modular' as the recursive one.
-------------------------------------------------------------------------------
Now let's take a look at styles in C, I've already said that my examples here
are in K&R / Bell Labs style, but there is also student style, let's make a
short example first one in Bell Labs style and then the same in student style.
Bell Labs style:
//----------------------------------------------------------
#include
int main ( int argc, char **argv )
{
if ( argc != 2 ) {
fprintf ( stderr, "Usage: %s
return -1;
}
printf ( "The argument was %s\n", argv[1] );
return 0;
}
//----------------------------------------------------------
Student style:
//----------------------------------------------------------
#include
int main(int argc,char **argv)
{
if(argc!=2){fprintf(stderr,"Usage: %s
return -1;}
printf("The argument was %s\n",argv[1]);
return 0;}
//----------------------------------------------------------
Now as you can see, Bell Labs style is much cleaner and much easyer to read.
So what is the actual difference ?
Well, a Bell Labs style function looks like this:
function ( ) {
action;
}
While a Student style function looks like this:
function(){action;}
Bell Labs style has the opening { on the same line as the function
and the closing } on a line by it self, with the action inbetween.
And then Bell Labs style has alot of spaces and *air* in the code
to make it easy to read for your self and others if you're in a
coding team.
All professional programmers I've seen this far uses Bell Labs style.
Let's not go deeper into the difference of the styles, just know that
there are different styles so you dont get confused if you see codes that
look really bizzare and *home made*.
-------------------------------------------------------------------------------
So now on to some more new stuff in C, to make it even easyer, to do some
stuff.
I've said that "strlen ( pointer )" will return the string length of the
pointer as an integer, but there is another one just like that.
sizeof ( pointer );
This will return the bit size of a pointer, so say that a pointer contains
the string "1234", strlen will return 4, since it's 4 bytes long, while
sizeof will return 16 since 4 * 8 == 16 ( there is 8 bits for 1 byte ).
I've also talked about strcmp() and strcpy(), now I wanna say that there
is 2 more, just like those but that's more *secure*, strncmp and strncpy.
The difference is that strncmp and strncpy wants the strlen of the string too.
Let me show you:
strcpy ( ptr, "string" );
strncpy ( ptr, "string", 6 );
strncmp ( ptr, "string" );
strncmp ( ptr, "string", 6 );
Now this is really easy once we know about strlen().
Like this:
strncmp ( ptr, argv[1], strlen ( argv[1] ) );
See ?
Another thing is the write() function, it works almost as fprintf, but
it wants the strlen, let me show you:
write ( 0, argv[1], strlen ( argv[1] ) );
This will have the same effect as doing either:
printf ( "%s", argv[1] );
or rather:
fprintf ( stdout, "%s", argv[1] );
the 0 in write() is a file descriptor, 0 means none or default which is
stdout, but you can write to a file if you've opend it (I'll show you that
soon), or a socket (I'll take up that too) etc.
there is alot of those small functions, like:
strchr()
memmem()
strcat()
memcpy()
memcmp()
calloc()
realloc()
stat()
fstat()
fseek()
atol()
strtod()
strtol()
strtoul()
And so on for a good while .... It's pretty pointless trying to learn
all of them at once .... and this is a tutorial not a library refference.
So I will not go over all functions in the whole language, if you wanna
learn all functions there is, get a book or find some place online
that has them all.
This tutorial will have enough to teach you the actual language well
enough so you can make your own programs at need, and so that you
without problems can learn all the extra stuff if you want.
A trick to find a function you're looking for on a UNIX system is this:
Say that you want to find something that converts ASCII (characters)
to integers but you never heard about atoi(), then you can do this:
alien:~$ apropos integer
That will show you this:
Tcl_GetInt, Tcl_GetDouble, Tcl_GetBoolean (3) - convert from string to integer,
double, or boolean
Tcl_NewIntObj, Tcl_NewLongObj, Tcl_SetIntObj, Tcl_SetLongObj, Tcl_GetIntFromObj, Tcl_GetLongFromObj (3) - manipulate Tcl objects as integers
abs (3) - computes the absolute value of an integer.
atoi (3) - convert a string to an integer.
atol (3) - convert a string to a long integer.
div (3) - computes the quotient and remainder of integer division
labs (3) - computes the absolute value of a long integer.
ldiv (3) - computes the quotient and remainder of long integer division.
rint (3) - round to closest integer
strtol (3) - convert a string to a long integer.
strtoul (3) - convert a string to an unsigned long integer.
tixGetInt (n) - Get the integer value of a string. ' ' '
Math::BigInt (3) - Arbitrary size integer math package
integer (3) - Perl pragma to compute arithmetic in integer instead of double
line 1/14 (END)
And there we go, the 4th down says:
atoi (3) - convert a string to an integer.
press "q" to get the prompt back:
so now you can do:
alien:~$ man 3 atoi
The 3 after man comes from "atoi (3)", and "man" means manual.
Now you will get up a manual page that has this in it:
---------------------------------------
SYNOPSIS
#include
int atoi(const char *nptr);
---------------------------------------
That is the most important part for you.
First it tells you what header file to include, and then it tells you
the type and syntax of the function.
Here, the function is of type int so it may be used as an integer,
and the syntax of it is: const char *nptr, wich means a char pointer.
(press "q" to get out of the man page and back to the prompt)
So this means the following, illustrated in a small code:
//----------------------------------------------------------
#include
#include
int main ( void )
{
char something[10];
strcpy ( something, "123" );
printf ( "the pointer holds %i\n", atoi ( something ) );
return 0;
}
//----------------------------------------------------------
Not very hard is it ?
look up the man pages for funtions that you already know to learn
how the manual pages works and are written.
-------------------------------------------------------------------------------
Now it's time to move on to the next section:
No comments:
Post a Comment