So, in the last coding lesson post I covered how to make a simple message server.
The message that it sent only said Hello, but it doesn't take too much imagination to realise that you could make this read data from a text file, or make it get some system parameters, or any number of things. perhaps the weather from a weather station.
For now we'll stick with the simple hello message.
We tested the server program by running telnet and connecting to the server that was running on port 66.
Now we're going to create some client software to connect to the server and get our message.
Again this is pretty simple proof of concept type software so don't get too excited about the code samples that I'm creating, on the other hand do imagine the possibilities of what you can create with these building blocks, and do get excited about that!
so as last time I'm going to step through the code roughly explaining what each line does.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void error(const char *msg)
{
perror(msg);
exit(0);
}
First as before we set-up our required header files, and that place to go to in the case of a terminal error.
then we start our program.
int main(int argc, char *argv[])
{
The first thing that you should notice is that something is different, what is argc and argv?
They are simple command line arguments.
The idea here is that we don't know where our software will run, it's all very well saying that the server is always going to be called "my_home_server", but what abuot when it's not. what about when it's out on the internet and you need to contact it by name, or what about when you change the host that the server software runs on. you don't want to have to compile a new binary for every host that you want to connect to.
so we're going to accept arguments about what host to connect to from the command line when this program is run.
Argc is argument count, Argv is the argument values it's an array of values, I.e. what we write after the program name
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
next (as before) we set up all the little bits of data variables we'll use in the program.
Now as we are specifying the host name to connect to as a command line argument, we need to check if there are command line arguments.
if (argc < 2) {
fprintf(stderr,"You Must specify a hostname: usage %s hostname\n", argv[0]);
exit(0);
}
if the program runs on it's own with no arguments then argc = 1 -just the program name,
we're running the software in the format "program_name hostname" so there are two arguments.
We also can see what the value of that first argument is, as it's argv[0], it's the program name, in this way even if we rename the executable, this help message is going to be correct.
portno = 66;
Once again we specify the ports that we're going to use, (if you changed from port 66 in the server software part, remember to change this again!)
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
And we setup our socket the same as we did on the server, a simple TCP/IP stream
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
Next we get the address of the server that we're going to connect to.
this uses a special function called gethostbyname that enables us to have literal addresses (like www.google.com) as the host that we want to connect to. We check to make sure that the address exists, if it doesn't the gethostbyname function returns nothing. and if this happens how are we going to open a socket to nowhere? -we can't so we throw and error message and close the program.
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
then we setup out port using roughly the same sort of instructions as we used for the server port. specifying our protocols, where it's going to connect to, and the port number that the socket will be bound to.
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
In the server example the next thing we did was bind the port, and set it to listen.
this is where client software dramatically differs here. rather than binding the port to listen, we now tell this port to connect.
(and obviously trap an error if this fails)
This software isn't just sitting and waiting this software is activly going out and connectting.
bzero(buffer,256);
So the first thing we do now is zero our buffer, basically get rid of any data in it.
you remember how with files if we opened a file for reading and writing with the pointer at the start of a file.
if the file said
Goodbye
and we wanted to write
Hello
we'd end up with
Helloye
As the second string doesn't completely replace the first.
Same thing here, and we don't want to display garbage to the user, so we clear out the buffer first.
Then we read from the socket, (in just the same way as we read from files, (except using read, not fread.
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
and if we can't read from the socket, obviously we give an error!
but if we can read from the socket then we display what we received from that socket by printing the buffer to the screen.
printf("%s\n",buffer);
close(sockfd);
return 0;
}
then we close the socket and exit gracefully.
complete code is here:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void error(const char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
if (argc < 2) {
fprintf(stderr,"You Must specify a hostname: usage %s hostname\n", argv[0]);
exit(0);
}
portno = 66;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
bzero(buffer,256);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
close(sockfd);
return 0;
}
this code is compiled on a linux system with the command.
gcc -o client client.c
then you can run the software with the command
./client localhost
(where the server software is also running on your computer)
Showing posts with label Coding Lessons. Show all posts
Showing posts with label Coding Lessons. Show all posts
Monday, December 24, 2012
Monday, December 10, 2012
Coding lessons: Socket servers
So in the last lesson I looked at socket servers, I touched on a problem with these lessons that will now become very apparent.
Windows Vs. the world,
So, there is a problem here, windows sockets work different to the way that practically everyone else does it. I might come back to winsock later. and certainly if you want to learn how to program network aware programs on windows then those lessons will be useful. the changes are not insurmountable, but the code is not directly portable
The lessons that I'm writing here apply to Linux (every distribution to date) Unix, BSD Unix, Solaris, and I think MacOS.
Right now if you're reading this lesson thinking that you only have access to windows, then I suggest that you download VMware player, it's free and lets you host virtual machines on your PC, or Microsoft's virtual PC, or any other virtualisation software out there.
Then download a free copy of Linux to go with it. I'll leave it to you to decide which copy of Linux.
But I recommend Debian Linux, I wouldn't suggest using it as a desktop OS, (there are much more polished distributions based on Debian,) so I'd recommend it more based on what it forces you to learn more than because it's a really comfortable OS for a beginner to use.
Anyway, go somewhere else to figure out how to use Linux, when you've finished with learning how to do that, come back and learn how to make server software.
Libraries
There is going to be a whole heap of headers included in this program. these are needed because of the types of data that we're dealing with, and the sorts of data that we need, (that these libraries can provide).
Functions
We're going to be using the error function that was created in the last post so that we can provide some helpful errors in case of failure.
Program
So I figure the best way to do this is to go through the program line by line explaining what each line/block is doing then I'll paste the whole code at the end.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
So first we've included all the libraries that we're going to use, it's a reasonably large list! I won't go through what every library does, it'd be pointless without the context provided by showing the code later.
void error(const char *msg)
{
perror(msg);
exit(1);
}
We setup our error function just as we did in the last lesson.
Then onto our main program.
int main()
{
int n, sockfd, newsockfd, portno;
First we're setting up integers that are going to be used for some error checking, we also set-up an integer to decide what port we want to open the server program on.
socklen_t clilen;
This line is declaring a variable called clien, it's data type is socklen_t.
tis is a bit weird, so far we've only looked at variable that are ints, chars, floats etc, socklen_t is a new variable type it's defined in the socket.h header file that we included.
struct sockaddr_in serv_addr, cli_addr;
now we're setting up a structured data type called sockaddr_in, this struct is defined in the library file #include <netinet/in.h>
sockfd = socket(AF_INET, SOCK_STREAM, 0);
Now we get to the meat of the program, setting up a socket.
to set-up a socket first we use a return variable, (in this case the integer sockfd) then we use the socket function.
the socket function has three arguments.
the first of which is AF.
There are lots of AF types, (these are all defined in the header file socket.h) we're defining the socket as AF_INET this means internet protocol version 4. (notice I didn't say TCP/IP! just IP at this point).
we could also use AF_IPX to setup an IPX/SPX connecttion, or AF_APPLETALK to setup a socket for use with the apple talk protocol AF_INET6 for use with IP6 AF_ROUTE for using the software with the internet routing protocol.
(look in socket.h for an extensive list)
The next argument that socket function needs is instructions as to the type of socket it's going to be, in this case we say it'll be a stream, and we use it just like file streams, but we could have said it'll be raw.
and the final piece of information is the protocol used,
in this case we're setting it to 0, this means use the default type for the family and type of socket, (AF_INET and SCOK_STREAM default protocol is TCP) but we could always specify a different protocol.
Next we need to check if we're able to create a socket at all
if (sockfd < 0)
error("ERROR opening socket");
we check that sockfd (the returned integer from the socket function is greater than zero, (as a 0 or negative number would specify an error, -if there is an error the program is useless so we break off into our error routine.
if there is no error we carry on through the program.
bzero((char *) &serv_addr, sizeof(serv_addr));
Bzero is a function defined in the header string.h, it writes zeros to every location in the char array that makes up a string.
portno = 66;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
Now we need to set-up the data that's used in the structs for our socket creation.
again we're setting the socket family at AF_INET
Most machines will have more than one interface, this line :
serv_addr.sin_addr.s_addr = INADDR_ANY;
is telling the struct what address it will bind to, in this case the answer is any address
Then we set-up out port number, we do this using the function htons.
Htons is simply a function that converts the order or numbers.
the reason that it does this is because there are some machines on the internet that use big endian byte order, and some that use little endian byte order
If you consider how to store variables.
lets say we have a 8 bit number 00101101, and we need to store this 8 bit number, in a memory space that's only 2 bits wide.
we'll use 4 memory spaces, we'll number the memory spaces 1 - 4 with big endian number storage
1, - 00
2, - 10
3, - 11
4, - 01
so you see we read the most significant bits from memory location 1, then work through the memory locations until we reach the least significant bit.
little endian system store the least significant bit in the lowest addressed memory location. a map of the memory in this case would look like this.
1, - 01
2, - 10
3, - 11
4, - 01
the internet protocol specifies that big endian is the correct order, that means that anyone using a machine with an intel processor (for example) has to re-order the words that they speak in to devices on the internet.
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
so next we say if (this stuff) <0 br="" error="" function.="" so="" to="">we're binding our socket here if it fails we want to break out to our error function, we could have written:
ec = sockfd, (struct sockaddr *) & serv....... [so on]
if (ec < 0) { error(); }
but it takes a bit less space to combine it all on one line.]
so we're calling the bind function, this opens the socket and binds it to a port. we pass out serv_addr struct that contains all the data about the port and address etc to the bind function.
listen(sockfd,5);
Next we tell the socket to enter listen mode.
Now the socket is sitting patiently waiting for a client to make a connection.
Once a client connects, (as described in an earlier post) the client will have an address and a port for communications.
now we set-up a new socket for communication so that our existing socket can handover and continue to listen for new connections.
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
just as we setup our listening socket we set-up our communication socket.
if (newsockfd < 0)
error("ERROR on accept");
and we check that it's opened properly, if not they we'll break out of the main routine to our error function.
n = write(newsockfd,"Hello",5);
Now we'll send the client a message.
again we say n = some function so that we can see what that function returned, zero or less is error.
if (n < 0) error("ERROR writing to socket");
which again means break out to the error routine.
finally we need to close the sockets, just as we close files when we're done reading and writing.
close(newsockfd);
close(sockfd);
return 0;
}
here's the whole code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main()
{
int n, sockfd, newsockfd, portno;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 66;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
n = write(newsockfd,"Hello",5);
if (n < 0) error("ERROR writing to socket");
close(newsockfd);
close(sockfd);
return 0;
}
when compiled on a linux system using gcc
(gcc -o server server.c)
a new binary file called server is created.
You should run this with the command ./server
(you may need to use sudo)
then you can connect to the server using telnet
c:\>telnet server 66
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HelloConnection closed by foreign host.
you can see that the server is working, it's listening, it's accepting connections, displaying the string, and then closing the socket. (just as it was programmed to do so!)
0>
Windows Vs. the world,
So, there is a problem here, windows sockets work different to the way that practically everyone else does it. I might come back to winsock later. and certainly if you want to learn how to program network aware programs on windows then those lessons will be useful. the changes are not insurmountable, but the code is not directly portable
The lessons that I'm writing here apply to Linux (every distribution to date) Unix, BSD Unix, Solaris, and I think MacOS.
Right now if you're reading this lesson thinking that you only have access to windows, then I suggest that you download VMware player, it's free and lets you host virtual machines on your PC, or Microsoft's virtual PC, or any other virtualisation software out there.
Then download a free copy of Linux to go with it. I'll leave it to you to decide which copy of Linux.
But I recommend Debian Linux, I wouldn't suggest using it as a desktop OS, (there are much more polished distributions based on Debian,) so I'd recommend it more based on what it forces you to learn more than because it's a really comfortable OS for a beginner to use.
Anyway, go somewhere else to figure out how to use Linux, when you've finished with learning how to do that, come back and learn how to make server software.
Libraries
There is going to be a whole heap of headers included in this program. these are needed because of the types of data that we're dealing with, and the sorts of data that we need, (that these libraries can provide).
Functions
We're going to be using the error function that was created in the last post so that we can provide some helpful errors in case of failure.
Program
So I figure the best way to do this is to go through the program line by line explaining what each line/block is doing then I'll paste the whole code at the end.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
So first we've included all the libraries that we're going to use, it's a reasonably large list! I won't go through what every library does, it'd be pointless without the context provided by showing the code later.
void error(const char *msg)
{
perror(msg);
exit(1);
}
We setup our error function just as we did in the last lesson.
Then onto our main program.
int main()
{
int n, sockfd, newsockfd, portno;
First we're setting up integers that are going to be used for some error checking, we also set-up an integer to decide what port we want to open the server program on.
socklen_t clilen;
This line is declaring a variable called clien, it's data type is socklen_t.
tis is a bit weird, so far we've only looked at variable that are ints, chars, floats etc, socklen_t is a new variable type it's defined in the socket.h header file that we included.
struct sockaddr_in serv_addr, cli_addr;
now we're setting up a structured data type called sockaddr_in, this struct is defined in the library file #include <netinet/in.h>
sockfd = socket(AF_INET, SOCK_STREAM, 0);
Now we get to the meat of the program, setting up a socket.
to set-up a socket first we use a return variable, (in this case the integer sockfd) then we use the socket function.
the socket function has three arguments.
the first of which is AF.
There are lots of AF types, (these are all defined in the header file socket.h) we're defining the socket as AF_INET this means internet protocol version 4. (notice I didn't say TCP/IP! just IP at this point).
we could also use AF_IPX to setup an IPX/SPX connecttion, or AF_APPLETALK to setup a socket for use with the apple talk protocol AF_INET6 for use with IP6 AF_ROUTE for using the software with the internet routing protocol.
(look in socket.h for an extensive list)
The next argument that socket function needs is instructions as to the type of socket it's going to be, in this case we say it'll be a stream, and we use it just like file streams, but we could have said it'll be raw.
and the final piece of information is the protocol used,
in this case we're setting it to 0, this means use the default type for the family and type of socket, (AF_INET and SCOK_STREAM default protocol is TCP) but we could always specify a different protocol.
Next we need to check if we're able to create a socket at all
if (sockfd < 0)
error("ERROR opening socket");
we check that sockfd (the returned integer from the socket function is greater than zero, (as a 0 or negative number would specify an error, -if there is an error the program is useless so we break off into our error routine.
if there is no error we carry on through the program.
bzero((char *) &serv_addr, sizeof(serv_addr));
Bzero is a function defined in the header string.h, it writes zeros to every location in the char array that makes up a string.
portno = 66;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
Now we need to set-up the data that's used in the structs for our socket creation.
again we're setting the socket family at AF_INET
Most machines will have more than one interface, this line :
serv_addr.sin_addr.s_addr = INADDR_ANY;
is telling the struct what address it will bind to, in this case the answer is any address
Then we set-up out port number, we do this using the function htons.
Htons is simply a function that converts the order or numbers.
the reason that it does this is because there are some machines on the internet that use big endian byte order, and some that use little endian byte order
If you consider how to store variables.
lets say we have a 8 bit number 00101101, and we need to store this 8 bit number, in a memory space that's only 2 bits wide.
we'll use 4 memory spaces, we'll number the memory spaces 1 - 4 with big endian number storage
1, - 00
2, - 10
3, - 11
4, - 01
so you see we read the most significant bits from memory location 1, then work through the memory locations until we reach the least significant bit.
little endian system store the least significant bit in the lowest addressed memory location. a map of the memory in this case would look like this.
1, - 01
2, - 10
3, - 11
4, - 01
the internet protocol specifies that big endian is the correct order, that means that anyone using a machine with an intel processor (for example) has to re-order the words that they speak in to devices on the internet.
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
so next we say if (this stuff) <0 br="" error="" function.="" so="" to="">we're binding our socket here if it fails we want to break out to our error function, we could have written:
ec = sockfd, (struct sockaddr *) & serv....... [so on]
if (ec < 0) { error(); }
but it takes a bit less space to combine it all on one line.]
so we're calling the bind function, this opens the socket and binds it to a port. we pass out serv_addr struct that contains all the data about the port and address etc to the bind function.
listen(sockfd,5);
Next we tell the socket to enter listen mode.
Now the socket is sitting patiently waiting for a client to make a connection.
Once a client connects, (as described in an earlier post) the client will have an address and a port for communications.
now we set-up a new socket for communication so that our existing socket can handover and continue to listen for new connections.
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
just as we setup our listening socket we set-up our communication socket.
if (newsockfd < 0)
error("ERROR on accept");
and we check that it's opened properly, if not they we'll break out of the main routine to our error function.
n = write(newsockfd,"Hello",5);
Now we'll send the client a message.
again we say n = some function so that we can see what that function returned, zero or less is error.
if (n < 0) error("ERROR writing to socket");
which again means break out to the error routine.
finally we need to close the sockets, just as we close files when we're done reading and writing.
close(newsockfd);
close(sockfd);
return 0;
}
here's the whole code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main()
{
int n, sockfd, newsockfd, portno;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 66;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
n = write(newsockfd,"Hello",5);
if (n < 0) error("ERROR writing to socket");
close(newsockfd);
close(sockfd);
return 0;
}
when compiled on a linux system using gcc
(gcc -o server server.c)
a new binary file called server is created.
You should run this with the command ./server
(you may need to use sudo)
then you can connect to the server using telnet
c:\>telnet server 66
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HelloConnection closed by foreign host.
you can see that the server is working, it's listening, it's accepting connections, displaying the string, and then closing the socket. (just as it was programmed to do so!)
0>
Monday, December 03, 2012
coding lesson: error codes
So I was expecting to be posting a lesson on how to make a server program that listens for a connection and then responds to a connection by sending a message to the client.
But in order to reduce the amount of code in that program first I'm going to introduce a way or outputting errors.
A program could fail in a few ways, I guess out of memory etc, but it can be difficult to trap those errors in a program, what's more the system knows what the error is, like I can't open a listening port because the port is already in use.
So to trap these errors and report useful information we use a call to a function found in a standard header file:
The standard header is stdlib.h that is included in the same way as stdio.h
then we are able to call a function called perror, tell it how we want to message to start, then the function will add what the system knows the error as, and print that to the screen.
#include <stdio.h>
#include <stdlib.h>
int main()
{
perror("error_one");
printf(\r\n"program running");
return 0;
}
when this program runs it prints the error message (no error) then prints a message to say that the program is still running.
Some errors are going to mean that we just can't go on. For example in the
creation of a server program that's going to listen on a network socket, if you can't create a socket, then the program is pretty useless and should terminate.
#include <stdio.h>
#include <stdlib.h>
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main()
{
perror("error_one");
printf(\r\n"program running");
error("error_two");
printf(\r\n"program running");
return 0;
}
When this is run you can see that the message program running is only displayed once.
That's because when we call error there is a function called exit.
This does exactly what it says on the tin...
we can now call perror for non fatal errors saying hey you might want to fix this, any fatal errors now we can let the user know what's wrong and exist the program in a good way
But in order to reduce the amount of code in that program first I'm going to introduce a way or outputting errors.
A program could fail in a few ways, I guess out of memory etc, but it can be difficult to trap those errors in a program, what's more the system knows what the error is, like I can't open a listening port because the port is already in use.
So to trap these errors and report useful information we use a call to a function found in a standard header file:
The standard header is stdlib.h that is included in the same way as stdio.h
then we are able to call a function called perror, tell it how we want to message to start, then the function will add what the system knows the error as, and print that to the screen.
#include <stdio.h>
#include <stdlib.h>
int main()
{
perror("error_one");
printf(\r\n"program running");
return 0;
}
when this program runs it prints the error message (no error) then prints a message to say that the program is still running.
Some errors are going to mean that we just can't go on. For example in the
creation of a server program that's going to listen on a network socket, if you can't create a socket, then the program is pretty useless and should terminate.
#include <stdio.h>
#include <stdlib.h>
void error(const char *msg)
{
perror(msg);
exit(1);
}
int main()
{
perror("error_one");
printf(\r\n"program running");
error("error_two");
printf(\r\n"program running");
return 0;
}
When this is run you can see that the message program running is only displayed once.
That's because when we call error there is a function called exit.
This does exactly what it says on the tin...
we can now call perror for non fatal errors saying hey you might want to fix this, any fatal errors now we can let the user know what's wrong and exist the program in a good way
Monday, November 19, 2012
Client / Server Software
In this post, I'm going to do a brief sort of how it works explanation for client server software.
The reason that I'm doing this is that the next coding lesson will be about creating network sockets on Unix/Linux systems in order to create network software.
There is (as ever) a lot of theory that goes into exactly how network connections etc work.
Rather that try to show horn all that into a single project I thought it'd be easier to post some theory first.
So first, we'll define what is a client, and what's a server.
A server in the context of network software is a program that listens for connections, accepts connections and serves content to those connections, (or receives content from those connections).
A client in the context of network software is a program that connects to another piece of software.
The server listens on specified ports awaiting connections,
The client connects to software that it listening on specified ports.
The network model layers
There is a standard for connecting systems, this is a 7 layer stack called the OSI model, (OSI is Open Systems Interconnection)
Each layer serves the layer above it.
This means that no higher numbered level can exist without the layer below it also existing.
(but you can have lower levels existing withough higher levels in your communication.)
The layers of the OSI model are very well documented elsewhere on the internet, it's a pretty broad subject, and I would suggest reading about it by searching for OSI model, It's practically impossible to break down to a simple blog post.
What is important is that the lessons that I'll be posting now will be using Layer 5 -the session layer of the OSI model. we'll be creating sockets that can be either TCP or UDP, telling the sockets what port we want them to listen on or what port we want them to connect to.
On top of the session level is the presentation layer, this presentation layer sorts out how the data is passed to and from the application layer (your web browser using HTTP) to the session layer (where the IP address is applied) and from there passed down to the transmission layer where it goes off into the world. the presentation layer may not always be used, but when it is used, it generally is used for compression or encryption, or translating between different character sets.
Because we will be accessing the OSI model at level 5 what you do at level six is up to you.
for example you could produce a fairly simple encryption protocol by bit shifting characters such that they appear as gibberish to anybody looking at the packets.
hello world >> ifmmp vlsme
then at the other end, your socket receives gibberish, at layer 5, then your presentation layer (layer 6) sorts that gibberish into real text by bit shifting the other way. and then this data is passed up to your next layer.
The application layer.
The application layer is where many common protocols sit. in general these protocols implement a way for your applications to request data from servers.
A web browser for example uses the HyperText Transfer Protocol.
this is a protocol, (an agreed standard) for the transmission of hyper text.
In general it works a bit like this (very simplistic)
you write https://www.google.com in your address bar.
your application layer passes to your presentation layer.
access www.google.com on port 80 and GET ./ (get the default page at this address)
The presentation layer receives this command, and because it's https (secure) it encrypts your request (GET ./). and passes it to the session layer.
The session layer opens a socket connection to www.google.com on port 80, and passes the data, (which is now encrypted -though the session layer doesn't care all it sees it's a data stream).
If we hadn't encrypted the data then the presentation layer wouldn't have been used at all.
Another thing that is important in these upcoming lessons is that this code will not be portable.
That is that the code will be platform specific, for windows or Linux/Unix systems.
The Linux/Unix software should work on Macs, and will work on raspberry pi devices, but will not work on windows, and the windows software will work on windows, but nothing else.
However the general theory for each is the same.
How socket connections work.
A socket is basically like a data stream, in the same way that we could read or write files, or print to the console using a data stream, we can read and write data to a network socket.
Your server listens on a port, (for a web server this may be port 80).
Your client software creates a port with a random port number, and connects to the server on the service port, it sends information about the port it will be listening on, and it's address
The server accepts the connection on the service port and creates a new port to reply from,
The server send a response from the new port to the client on the port that it established communication to the server on.
The client may then send a message, and the server will respond.
This will be more obvious when I get into the code, but that's going to happen another time.
The reason that I'm doing this is that the next coding lesson will be about creating network sockets on Unix/Linux systems in order to create network software.
There is (as ever) a lot of theory that goes into exactly how network connections etc work.
Rather that try to show horn all that into a single project I thought it'd be easier to post some theory first.
So first, we'll define what is a client, and what's a server.
A server in the context of network software is a program that listens for connections, accepts connections and serves content to those connections, (or receives content from those connections).
A client in the context of network software is a program that connects to another piece of software.
The server listens on specified ports awaiting connections,
The client connects to software that it listening on specified ports.
The network model layers
There is a standard for connecting systems, this is a 7 layer stack called the OSI model, (OSI is Open Systems Interconnection)
Each layer serves the layer above it.
This means that no higher numbered level can exist without the layer below it also existing.
(but you can have lower levels existing withough higher levels in your communication.)
The layers of the OSI model are very well documented elsewhere on the internet, it's a pretty broad subject, and I would suggest reading about it by searching for OSI model, It's practically impossible to break down to a simple blog post.
What is important is that the lessons that I'll be posting now will be using Layer 5 -the session layer of the OSI model. we'll be creating sockets that can be either TCP or UDP, telling the sockets what port we want them to listen on or what port we want them to connect to.
On top of the session level is the presentation layer, this presentation layer sorts out how the data is passed to and from the application layer (your web browser using HTTP) to the session layer (where the IP address is applied) and from there passed down to the transmission layer where it goes off into the world. the presentation layer may not always be used, but when it is used, it generally is used for compression or encryption, or translating between different character sets.
Because we will be accessing the OSI model at level 5 what you do at level six is up to you.
for example you could produce a fairly simple encryption protocol by bit shifting characters such that they appear as gibberish to anybody looking at the packets.
hello world >> ifmmp vlsme
then at the other end, your socket receives gibberish, at layer 5, then your presentation layer (layer 6) sorts that gibberish into real text by bit shifting the other way. and then this data is passed up to your next layer.
The application layer.
The application layer is where many common protocols sit. in general these protocols implement a way for your applications to request data from servers.
A web browser for example uses the HyperText Transfer Protocol.
this is a protocol, (an agreed standard) for the transmission of hyper text.
In general it works a bit like this (very simplistic)
you write https://www.google.com in your address bar.
your application layer passes to your presentation layer.
access www.google.com on port 80 and GET ./ (get the default page at this address)
The presentation layer receives this command, and because it's https (secure) it encrypts your request (GET ./). and passes it to the session layer.
The session layer opens a socket connection to www.google.com on port 80, and passes the data, (which is now encrypted -though the session layer doesn't care all it sees it's a data stream).
If we hadn't encrypted the data then the presentation layer wouldn't have been used at all.
Another thing that is important in these upcoming lessons is that this code will not be portable.
That is that the code will be platform specific, for windows or Linux/Unix systems.
The Linux/Unix software should work on Macs, and will work on raspberry pi devices, but will not work on windows, and the windows software will work on windows, but nothing else.
However the general theory for each is the same.
How socket connections work.
A socket is basically like a data stream, in the same way that we could read or write files, or print to the console using a data stream, we can read and write data to a network socket.
Your server listens on a port, (for a web server this may be port 80).
Your client software creates a port with a random port number, and connects to the server on the service port, it sends information about the port it will be listening on, and it's address
The server accepts the connection on the service port and creates a new port to reply from,
The server send a response from the new port to the client on the port that it established communication to the server on.
The client may then send a message, and the server will respond.
This will be more obvious when I get into the code, but that's going to happen another time.
Monday, August 06, 2012
Coding Lessons: Structs (Lessons 18)
So we've dealt with Arrays and strings and passing arrays and fields to and from functions.
Now we're going to look at structured data.
Structured data is basically a list of fields, those fields can have any type, the total memory for that block is then the combined memory requirements of all the little bits of data inside that structured data block.
You can think of a struct like a row of a table.
Where we can give that table row or block of data a name.
It's probably best to write out some code and explain what's going on.
to start with structured data is created with the keyword struct.
struct product
Here we're going to create a structured data type called product, inside this data type will be a product name and a product price.
The data types that are contained in the struct declaration then follow inside curly brackets.
struct product {
float price;
int stocklevel;
};
So that's our definition of our structured data set-up.
we do this using the struct keyword again, then the name of the struct that we're referring to, (because we could define multiple struct types) then what we want to call it.
struct product apples;
now we want to poke some data into the struct, and read some data out of it:
apples.price = 0.49;
apples.stocklevel= 12;
printf("You have a stock of %d apple(s) costing $%.2f", apples.stocklevel, apples.price);
#include<stdio.h>
int main()
{
struct product { float price; int stocklevel; };
struct product apples;
apples.price = 0.49;
apples.stocklevel= 12;
printf("You have a stock of %d apple(s) costing $%.2f", apples.stocklevel, apples.price);
}
Now we're going to look at structured data.
Structured data is basically a list of fields, those fields can have any type, the total memory for that block is then the combined memory requirements of all the little bits of data inside that structured data block.
You can think of a struct like a row of a table.
Where we can give that table row or block of data a name.
It's probably best to write out some code and explain what's going on.
to start with structured data is created with the keyword struct.
struct product
Here we're going to create a structured data type called product, inside this data type will be a product name and a product price.
The data types that are contained in the struct declaration then follow inside curly brackets.
struct product {
float price;
int stocklevel;
};
So that's our definition of our structured data set-up.
we do this using the struct keyword again, then the name of the struct that we're referring to, (because we could define multiple struct types) then what we want to call it.
struct product apples;
now we want to poke some data into the struct, and read some data out of it:
apples.price = 0.49;
apples.stocklevel= 12;
printf("You have a stock of %d apple(s) costing $%.2f", apples.stocklevel, apples.price);
#include<stdio.h>
int main()
{
struct product { float price; int stocklevel; };
struct product apples;
apples.price = 0.49;
apples.stocklevel= 12;
printf("You have a stock of %d apple(s) costing $%.2f", apples.stocklevel, apples.price);
}
Monday, April 30, 2012
Coding lessons: Writing files in C
In the last lessons I looked at reading files using gets, fgets and fscanf.
to you should be fully aufait on how to read data from files, and how to recognise the end of a file.
Now we'll look at how to write data to files
we mentioned previously that files could be opened in a couple of ways, for reading, writing, or appending.
but that these did not mean exactly that.
reading meant that the file would be opened and the file stream pointer placed at the start of the file in order to start reading
writing meant that the file would be truncated to zero and that the file stream pointer would be placed at the start of the file read for writing
appending mean that the file would be opened and the file stream pointer placed at the end of the file ready for writing
there was also the opportunity to add a plus to these file mode operators and that would mean that the file was opened for reading and writing regardless of whether you said, I want to open this file for reading, or I want to open this file for writing.
Which sounds bit strange, why specify you want a file mode to be reading, or writing if you then say, I want to open for reading, but writing too, or I want to open for writing, but also reading.
it's more to do with the file pointers and where the file stream points to in the file.
First lets look at the basic functions for writing files, then a few code samples that will help solidify what the file opening modes actually mean.
When reading files we looked at getc first. so it makes sense to look at putc first.
in this example to make sense of the code you must realise that in a computer letters are represented by numbers.
for example A = 65, B =66
or more accurately A = 01000001 , b = 01000010
B is numerically on more than A.
so the loop for (letter = 'A'; letter<= 'Z'; letter++) means go through the alphabet starting at A and going to Z
When we used getc we said character = getc (resource)
putc needs arguments that are, what you want to put there, and the place you want to put it.
#include<stdio.h>
int main()
{
FILE * filepointer;
char letter;
filepointer=fopen("file.txt", "w");
for (letter = 'A'; letter <= 'Z'; letter++ )
{
putc(letter, filepointer);
}
fclose (filepointer);
}
when you run the code above a file is created in the same directory as the exe file, the file is called file.txt and contains the letters A - Z all on one line.
So lets try some examples to show those different modes for opening a file
first lets change the contents of the file add something to the end.
now run the application again.
open the file, it now only contains letters A-Z
That is because the file was opened in write mode.
where the file is truncated (reduced to Zero) and then written
now change the code
#include<stdio.h>
int main()
{
FILE * filepointer;
char letter;
filepointer=fopen("file.txt", "w");
for (letter = 'L'; letter <= 'Z'; letter++ )
{
putc(letter, filepointer);
}
fclose (filepointer);
}
compile and run, and your file now only contains charecters L - Z
so now lets change the lemthod of opening.
#include<stdio.h>
int main()
{
FILE * filepointer;
char letter;
filepointer=fopen("file.txt", "a");
for (letter = 'A'; letter <= 'Z'; letter++ )
{
putc(letter, filepointer);
}
fclose (filepointer);
}
compile and run, the file now has A-L from the last program, and A-Z added after that, each tile you run the program letters A-Z will be added to (end of) the file.
#include<stdio.h>
int main()
{
FILE * filepointer;
char letter;
filepointer=fopen("file.txt", "r");
for (letter = 'A'; letter <= 'Z'; letter++ )
{
putc(letter, filepointer);
}
fclose (filepointer);
}
now try compiling and running that, (it will compile and will run).
run as many times as you like, notice how nothing is added to the file, that's because the file is only opened for reading.
#include<stdio.h>
int main()
{
FILE * filepointer;
char letter;
filepointer=fopen("file.txt", "r+");
for (letter = 'A'; letter <= 'Z'; letter++ )
{
putc(letter, filepointer);
}
fclose (filepointer);
}
Compile and run this, the file is now opened for reading and writing.
run this a few times.
see what happens, -the file doesn't grown, is just continues to contain the letters A-Z
now leave the file alone and compile and run this code
#include<stdio.h>
int main()
{
FILE * filepointer;
char letter;
filepointer=fopen("file.txt", "r+");
for (letter = 'L'; letter <= 'Z'; letter++ )
{
putc(letter, filepointer);
}
fclose (filepointer);
}
now run this.
the file called file.txt did contain the following text
ABCDEFGHIJKLMNOPQRSTUVWXYZ
now it contains
LMNOPQRSTUVWXYZPQRSTUVWXYZ
If you go all the way to the top of the tutorial you see I talked about where the file pointer goes
When you open a file for Writing, the file is truncated (reduced to zero) and the file pointer is put at the start.
We see this because anything that we put in the file is lost and over written, not matter how big of how small only what the program says ends up in the file.
When you open a file for appending, the file is opened and the file stream pointer placed at the end of the file, this means that the characters that we're adding get put at the end of the file. so the file grows.
When we open a file for reading the file pointer is placed at the start of the file.
adding the plus doesn't change the modes main property which is where the file stream is in the file (at the beginning) so you see how additions to the file overwrite text that is in the file.
More ways of writing.
There are (of course) more ways to write to a file than simply putting individual characters into a file stream.
We can also put strings into files using fputs
fputs only has two arguments, like putc it just needs to know what you want to write, and where you want to write it.
#include<stdio.h>
int main ()
{
FILE * filepointer;
char string[] = "the quick brown fox jumps over the lazy dog";
filepointer = fopen("file.txt","a");
fputs (string,filepointer);
fclose (filepointer);
}
And finally, in the same way that we use printf to write to the console, we can use fprintf to write to a file.
#include<stdio.h>
int main ()
{
FILE * filepointer;
char string[] = "the quick brown fox jumps over the lazy dog";
int number = 13;
filepointer = fopen("file.txt","a");
fprintf(filepointer, "%s, well actually is was %d lazy dogs", string, number);
fclose (filepointer);
}
I added in some variables, fprintf works exactly the same way that printf does except that you need the first argument to be where you want to put the stuff, then what you want to put there.
in fact it works so much like printf that you can tell it this:
fprintf(stdout, "%s, well actually is was %d lazy dogs", string, number);
stdout is the console, you're telling it to write to the console as if it were a file.
Monday, April 09, 2012
Coding lessons: Working with Files, (reading) (Lessons16)
Anyone following these lessons as actual lessons might think finally something useful.
After all everything we dealt with so far has concerned putting data into a system, working with that data and spitting it out, nothing has been saved at all when the program completes running that's it, it closes and memory space used for the program that was containing any data goes back to the system where it's overwritten.
You might say to make a really useful program that you have to be able to store data, And of course you need to be able to open that file again, and possibly work with that data and save it in a slightly changed format.
So lets get right into it.
Ways of opening files
Files can be opened in a variety of ways, first and foremost the most basic way of opening a file is to open a file for reading only.
The second way of opening a file would be to open a file for writing, there are two ways of opening a file for writing.
open for overwriting, (where data will be added at the start of the file and overwrite existing data)
or open file for appending, (where data is added to the end of a file.
You select this mode of file opening by using r, w or a.
Files are opened by default in string mode. but files may also be opened in binary mode.
What this means is if you have a file that contains the decimal number 53, if you open this in string mode the number 5 will come out.
Decimal number 97 will be read as 'a' in string mode. (because that is the ASCII representation of the character)
If you have a file that is a series of numerical data then you should know that you want to open the file in binary mode to get your numerical data out.
to select to open the file in string mode, do nothing, (that's the default).
If you want to open the file in binary mode, add a b after the r/w/a file opening mode
if you want to open the file for both reading and writing add a + to the end.
for example if I want to open a file, read data, and then overwrite it, I open with the following mode operator
w+
if it's binary data I use
wb+
remember r opens the file for reading, and sets the file stream at the beginning of the file
w sets the file length to zero (and so overwrites) and points the file stream at the start of the file.
a, points the file stream to the end of the file, so that updates occur at the end of the file.
Reading files
when we access a file, just like when we access a memory resource we use a pointer to do it.
#include<stdio.h>
int main()
{
char c;
FILE *filepointer;
then we point our pointer at the file resource that we wish to access.
filepointer=fopen("D:\\coding\\lesson16\\file.txt", "r");
This is a file that contains a series of characters,
The file is a few characters long and then stops, you and I do not see the stop, but the end of the file is marked with a special marker called End Of File
while (c!=EOF)
{
so we say, while c, (which is the place we'll put characters as they are read from the file) is not the end of file marker, then do this (read the file)
c = getc(filepointer);
this says, c (the place where we're putting the characters read from the file) equals the result of the function getc(filepointer), getc is a function that says get the character pointed to by this pointer, the pointer is filepointer.
printf("%c", c);
}
fclose(filepointer);
}
Then we print the character and close the file
#include<stdio.>
int main()
{
int i=0;
char c;
FILE *filepointer;
filepointer=fopen("D:\\coding\\lesson16\\file.txt", "r");
while (c!=EOF)
{
i++;
c = getc(filepointer);
printf("%c", c);
}
printf("while loop ran %d times", i);
fclose(filepointer);
}
D:\coding\lesson16>source.exe
this
is
a
file! while loop ran 16 times
D:\coding\lesson16>
(and that is the contents of the file at the location.)
the file contains 16 characters
this(4) return(1) is(2) return(1) a(1) return(1) file!(5) return(1)
There are other ways of reading files,
in the same way as we get data from the keyboard using scanf, we can scan files using fscanf.
however, this time to make sure that we stop searching when we reach the end of the file we need to use the function feof(filepointer) to search for the end of the file.
when we use scanf we say scanf("datatype", &variable-to-store), when using fscanf we also need to include an argument to tell it what file to read data from, (in this way we can work with multiple file streams.
#include<stdio.h>
int main()
{
int i=0;
char string[50];
FILE *filepointer;
filepointer=fopen("D:\\coding\\lesson16\\file.txt", "r");
while (feof(filepointer)==0)
{
i++;
fscanf(filepointer, "%s", &string);
printf("%s", string);
}
printf("while loop ran %d times", i);
fclose(filepointer);
}
the loop runs four time, carriage returns are ignored, and there are 4 distinct inputs, in the same way we investigated the scanf statement and found that spaces are ignored, (when introducing flush() spaces are also ignored. so "Hello World" will appear as two strings.
and finally we can use fgets, earlier we used getc to read the file a single character at a time. now we're going to read the file as a string.
fgets requires us to provide three arguments
the charector array (string) in which the returned data will be stored.
the size chunk of data to bring back (number of characters)
the pointer to the file
#include<stdio.h>
int main()
{
int i=0;
char string[50];
FILE *filepointer;
filepointer=fopen("D:\\coding\\lesson16\\file.txt", "r");
while (feof(filepointer)==0)
{
i++;
fgets(string, 50, filepointer);
printf("%s", string);
}
printf("while loop ran %d times", i);
fclose(filepointer);
}
The loop runs 4 times. (as there are four strings)
if we had specified.
fgets(string, 3, filepointer);
then the loop runs 9 times
1, thi
2, s
3, [return]
4, is
5, [return]
6, a
7, [return]
8, fil
9, e!
After all everything we dealt with so far has concerned putting data into a system, working with that data and spitting it out, nothing has been saved at all when the program completes running that's it, it closes and memory space used for the program that was containing any data goes back to the system where it's overwritten.
You might say to make a really useful program that you have to be able to store data, And of course you need to be able to open that file again, and possibly work with that data and save it in a slightly changed format.
So lets get right into it.
Ways of opening files
Files can be opened in a variety of ways, first and foremost the most basic way of opening a file is to open a file for reading only.
The second way of opening a file would be to open a file for writing, there are two ways of opening a file for writing.
open for overwriting, (where data will be added at the start of the file and overwrite existing data)
or open file for appending, (where data is added to the end of a file.
You select this mode of file opening by using r, w or a.
Files are opened by default in string mode. but files may also be opened in binary mode.
What this means is if you have a file that contains the decimal number 53, if you open this in string mode the number 5 will come out.
Decimal number 97 will be read as 'a' in string mode. (because that is the ASCII representation of the character)
If you have a file that is a series of numerical data then you should know that you want to open the file in binary mode to get your numerical data out.
to select to open the file in string mode, do nothing, (that's the default).
If you want to open the file in binary mode, add a b after the r/w/a file opening mode
if you want to open the file for both reading and writing add a + to the end.
for example if I want to open a file, read data, and then overwrite it, I open with the following mode operator
w+
if it's binary data I use
wb+
remember r opens the file for reading, and sets the file stream at the beginning of the file
w sets the file length to zero (and so overwrites) and points the file stream at the start of the file.
a, points the file stream to the end of the file, so that updates occur at the end of the file.
Reading files
when we access a file, just like when we access a memory resource we use a pointer to do it.
#include<stdio.h>
int main()
{
char c;
FILE *filepointer;
then we point our pointer at the file resource that we wish to access.
filepointer=fopen("D:\\coding\\lesson16\\file.txt", "r");
This is a file that contains a series of characters,
The file is a few characters long and then stops, you and I do not see the stop, but the end of the file is marked with a special marker called End Of File
while (c!=EOF)
{
so we say, while c, (which is the place we'll put characters as they are read from the file) is not the end of file marker, then do this (read the file)
c = getc(filepointer);
this says, c (the place where we're putting the characters read from the file) equals the result of the function getc(filepointer), getc is a function that says get the character pointed to by this pointer, the pointer is filepointer.
printf("%c", c);
}
fclose(filepointer);
}
Then we print the character and close the file
#include<stdio.>
int main()
{
int i=0;
char c;
FILE *filepointer;
filepointer=fopen("D:\\coding\\lesson16\\file.txt", "r");
while (c!=EOF)
{
i++;
c = getc(filepointer);
printf("%c", c);
}
printf("while loop ran %d times", i);
fclose(filepointer);
}
D:\coding\lesson16>source.exe
this
is
a
file! while loop ran 16 times
D:\coding\lesson16>
(and that is the contents of the file at the location.)
the file contains 16 characters
this(4) return(1) is(2) return(1) a(1) return(1) file!(5) return(1)
There are other ways of reading files,
in the same way as we get data from the keyboard using scanf, we can scan files using fscanf.
however, this time to make sure that we stop searching when we reach the end of the file we need to use the function feof(filepointer) to search for the end of the file.
when we use scanf we say scanf("datatype", &variable-to-store), when using fscanf we also need to include an argument to tell it what file to read data from, (in this way we can work with multiple file streams.
#include<stdio.h>
int main()
{
int i=0;
char string[50];
FILE *filepointer;
filepointer=fopen("D:\\coding\\lesson16\\file.txt", "r");
while (feof(filepointer)==0)
{
i++;
fscanf(filepointer, "%s", &string);
printf("%s", string);
}
printf("while loop ran %d times", i);
fclose(filepointer);
}
the loop runs four time, carriage returns are ignored, and there are 4 distinct inputs, in the same way we investigated the scanf statement and found that spaces are ignored, (when introducing flush() spaces are also ignored. so "Hello World" will appear as two strings.
and finally we can use fgets, earlier we used getc to read the file a single character at a time. now we're going to read the file as a string.
fgets requires us to provide three arguments
the charector array (string) in which the returned data will be stored.
the size chunk of data to bring back (number of characters)
the pointer to the file
#include<stdio.h>
int main()
{
int i=0;
char string[50];
FILE *filepointer;
filepointer=fopen("D:\\coding\\lesson16\\file.txt", "r");
while (feof(filepointer)==0)
{
i++;
fgets(string, 50, filepointer);
printf("%s", string);
}
printf("while loop ran %d times", i);
fclose(filepointer);
}
The loop runs 4 times. (as there are four strings)
if we had specified.
fgets(string, 3, filepointer);
then the loop runs 9 times
1, thi
2, s
3, [return]
4, is
5, [return]
6, a
7, [return]
8, fil
9, e!
Monday, February 27, 2012
Coding lessons: Memory allocation in C
How and where you store things is pretty important, C will take care or a lot for you, it's a pretty high level language, but it does give you pretty enormous power too, direct access to memory is just one of those powers.
In the last coding lesson (some time ago now) I covered pointers and how not only could we give variables names and use the name to access those variables but we could also use a pointer to read the variable from the memory location for the address of where the variable is stored.
In this lesson I'm going to cover a bit more about memory usage.
Allocating memory
To allocate memory we use the memory allocate function called malloc
Malloc will allocate a certain amount of memory during the execution of a program, the function requests memory from the memory heap (not the stack), when the request is granted that memory is reserved for the program.
#include<stdio.h>
#include<string.h">
int main()
{
char words[]={"My Malloc String\n"};
char *p;
p = (char *)malloc(sizeof(words));
strcpy(p,words);
printf("p = %s\n",p);
printf("words = %s\n",words);
return(0);
}
I have introduced a ew function (sizeof())
the sizeof function returns an integer that is the size of the argument givern to is
so
char x[] = "hello";
int length = sizeof(x);
length = 6, five letters and the null terminator.
Anyway, the code, first we've created a string, -which we covered in earlier lessons.
then we declared a pointer.
then used malloc to reserve a space of memory that was the same size as the string, we then copied that string data into the memory that we'd reserved, (that pointer p pointed to).
(there is a bit missing from this program, so scroll to the bottom before you go crazy trying to figure out why your adapted code sample is doing funny things once you've run it loads of times.)
The next function that I'll look at is calloc,
Calloc is slightly different to malloc in that two arguments are needed.
with malloc you can reserve say 50 bytes by writing
pointer = malloc(50);
With calloc you reserve an array of memory, say for storing a list of numbers,
therefore you must give two dimensions (as you;re asking for a 2 dimensional array of space.
that's the amount of rows you plan to save, and the amount of bytes required in those rows.
#include<stdio.h">
#include<stdlib.h">
int main ()
{
int row,n;
int * ptr_data;
printf ("Enter amount: ");
scanf ("%d",&row);
ptr_data = (int*) calloc ( row,sizeof(int) );
for ( n=0; n{
printf ("Enter number #%d: ",n);
scanf ("%d",&ptr_data[n]);
}
printf ("Output: ");
for ( n=0; nprintf ("%d ",ptr_data[n]);
free (ptr_data);
return 0;
}
You see this program, we see that we're going to enter a list of variables, but the programmer does not necessarily know how many items are in the list, so the user is asked first.
this program takes integers,
D:\coding\lesson15>source.exe
Enter amount: 4
Enter number #0: 1
Enter number #1: 2
Enter number #2: 3
Enter number #3: 4
Output: 1 2 3 4
With this sample run we can see that we have a memory allocation of 4 rows, and each row has 4 bytes. (because they are 32 bit numbers)
hence this list was stored in 128bytes of memory.
The other difference between malloc and calloc is that calloc initializes the memory, (writes zeros to all locations) malloc does not.
This means that with malloc you could feasibly read random old data from the memory heap.
The above example miss two very important things.
the first is error checking.
Neither malloc nor calloc guarantee that the memory will be allocated, if the system is out of memory then the system is out of memory and cannot allocate memory, in this case a NULL pointer is returned.
when using the function you should check for a null being returned, because things will not work as expected if you don't get the memory that you asked for!
if (ptr_data==NULL)
{
printf ("Error allocating requested memory");
exit (1);
}
The other thing that you should do, (and if you're using a loop with memory allocation functions) is free the memory that you have reserved, because if you don't you will run out of memory.
in computer terms a program that requests memory from the heap, then never returns it, only ever requesting more is usually thought to have a memory leak. eventually the program will consume all the available resource.
to return memory to the heap once we're finished with it we use the free function.
When we're finished with the memory that our pointer is looking at we just write
free(pointer);
and that un-reserves and returns the memory ready to be used by other programs.
In the last coding lesson (some time ago now) I covered pointers and how not only could we give variables names and use the name to access those variables but we could also use a pointer to read the variable from the memory location for the address of where the variable is stored.
In this lesson I'm going to cover a bit more about memory usage.
Allocating memory
To allocate memory we use the memory allocate function called malloc
Malloc will allocate a certain amount of memory during the execution of a program, the function requests memory from the memory heap (not the stack), when the request is granted that memory is reserved for the program.
#include<stdio.h>
#include<string.h">
int main()
{
char words[]={"My Malloc String\n"};
char *p;
p = (char *)malloc(sizeof(words));
strcpy(p,words);
printf("p = %s\n",p);
printf("words = %s\n",words);
return(0);
}
I have introduced a ew function (sizeof())
the sizeof function returns an integer that is the size of the argument givern to is
so
char x[] = "hello";
int length = sizeof(x);
length = 6, five letters and the null terminator.
Anyway, the code, first we've created a string, -which we covered in earlier lessons.
then we declared a pointer.
then used malloc to reserve a space of memory that was the same size as the string, we then copied that string data into the memory that we'd reserved, (that pointer p pointed to).
(there is a bit missing from this program, so scroll to the bottom before you go crazy trying to figure out why your adapted code sample is doing funny things once you've run it loads of times.)
The next function that I'll look at is calloc,
Calloc is slightly different to malloc in that two arguments are needed.
with malloc you can reserve say 50 bytes by writing
pointer = malloc(50);
With calloc you reserve an array of memory, say for storing a list of numbers,
therefore you must give two dimensions (as you;re asking for a 2 dimensional array of space.
that's the amount of rows you plan to save, and the amount of bytes required in those rows.
#include<stdio.h">
#include<stdlib.h">
int main ()
{
int row,n;
int * ptr_data;
printf ("Enter amount: ");
scanf ("%d",&row);
ptr_data = (int*) calloc ( row,sizeof(int) );
for ( n=0; n
printf ("Enter number #%d: ",n);
scanf ("%d",&ptr_data[n]);
}
printf ("Output: ");
for ( n=0; n
free (ptr_data);
return 0;
}
You see this program, we see that we're going to enter a list of variables, but the programmer does not necessarily know how many items are in the list, so the user is asked first.
this program takes integers,
D:\coding\lesson15>source.exe
Enter amount: 4
Enter number #0: 1
Enter number #1: 2
Enter number #2: 3
Enter number #3: 4
Output: 1 2 3 4
With this sample run we can see that we have a memory allocation of 4 rows, and each row has 4 bytes. (because they are 32 bit numbers)
hence this list was stored in 128bytes of memory.
The other difference between malloc and calloc is that calloc initializes the memory, (writes zeros to all locations) malloc does not.
This means that with malloc you could feasibly read random old data from the memory heap.
The above example miss two very important things.
the first is error checking.
Neither malloc nor calloc guarantee that the memory will be allocated, if the system is out of memory then the system is out of memory and cannot allocate memory, in this case a NULL pointer is returned.
when using the function you should check for a null being returned, because things will not work as expected if you don't get the memory that you asked for!
if (ptr_data==NULL)
{
printf ("Error allocating requested memory");
exit (1);
}
The other thing that you should do, (and if you're using a loop with memory allocation functions) is free the memory that you have reserved, because if you don't you will run out of memory.
in computer terms a program that requests memory from the heap, then never returns it, only ever requesting more is usually thought to have a memory leak. eventually the program will consume all the available resource.
to return memory to the heap once we're finished with it we use the free function.
When we're finished with the memory that our pointer is looking at we just write
free(pointer);
and that un-reserves and returns the memory ready to be used by other programs.
Monday, January 16, 2012
Coding Lessons: C and pointers (lesson 14)
In the lessons last year we looked at a the basic variables in C, integers, longs, floats, chars, short integers, arrays or chars and strings (which are just null terminated arrays of chars).
What we didn't think about is what's going on inside the device.
This gets confusing quickly.
so lets try and take things slowly.
Lets declare a variable.
int num;
We've declared a variable we called that variable num, we say that variable will be an integer number.
Behind the scenes what has happened is that the program that we write will take a section of memory that is the size of an integer, (remember from lesson 2 int is 32bit numbers) so a section of memory, (a box) is set-up, that will contain the data than we assign to num.
The box is called num, we can put number data into that box.
num = 23;
Now the box called num has the data 23 in it.
But there is something missing from this whole idea: where is the box?
The box is "somewhere" in memory, it has an address.
So now lets declare a new variable
int *mem_ptr = #
now this is a strange one, first, what's that star all about, and second, how are we putting the string &num into an integer variable?!
Well, firstly, the star specifies that this is a pointer, it's going to point to a memory address.
what comes next is a little weird and interesting.
we're not butting a string into the box we're telling it, that pointer, points to the address of the variable num, that & symbol is the address operator.
so let's look at this in a program.
#include <stdio.h>
int main()
{
int num;
int *mem_ptr = #
num = 8;
printf("num = %d\r\nptr = %d\r\n", num, *mem_ptr);
}
When you run this program you get the following output:
D:\coding\lesson14>tcc source.c
D:\coding\lesson14>source.exe
num = 8
ptr = 8
which is pretty obvious.
you're writing, Make a variable called num, that's an integer.
but what you're really saying is go get me 32 bits of memory, I want to store something, I'll refer to it as num...
So then when you use the pointer and say, the pointer has the same value as the address of the variable num (it looks at the same memory location) of course the value is the same, it's looking at the same 32 bits of memory,
as well as looking at the variable by inspecting the same memory location, we can also, write to memory by writing to the memory address that the variable uses by writing to the pointer to that memory address.
#include <stdio.h>
int main()
{
int num;
int *mem_ptr = #
num = 8;
printf("num = %d\r\nptr = %d\r\n", num, *mem_ptr);
*mem_ptr = 1;
printf("num = %d\r\nptr = %d\r\n", num, *mem_ptr);
}
D:\coding\lesson14>source.exe
num = 8
ptr = 8
num = 1
ptr = 1
we can also change the memory address that the pointer looks at during the program.
in the following source code we'll set up two variables, one called num and the other called digit, then tell the pointer to look at the contents of the address space given to num, (and we'll compare that to num) then we'll look at the contents of the address space given to digit to show how the pointer can move.
#include <stdio.h>
int main()
{
int num, digit;
int *mem_ptr = #
num = 8;
digit = 9;
printf("num = %d\r\nptr = %d\r\n", num, *mem_ptr);
mem_ptr = &digit;
printf("num = %d\r\nptr = %d\r\n", num, *mem_ptr);
}>
D:\coding\lesson14>source.exe
num = 8
ptr = 8
num = 8
ptr = 9
Types of pointer.
We're comfortable that a pointer looks to a memory location, and that can be changed.
so far out pointer
What we didn't think about is what's going on inside the device.
This gets confusing quickly.
so lets try and take things slowly.
Lets declare a variable.
int num;
We've declared a variable we called that variable num, we say that variable will be an integer number.
Behind the scenes what has happened is that the program that we write will take a section of memory that is the size of an integer, (remember from lesson 2 int is 32bit numbers) so a section of memory, (a box) is set-up, that will contain the data than we assign to num.
The box is called num, we can put number data into that box.
num = 23;
Now the box called num has the data 23 in it.
But there is something missing from this whole idea: where is the box?
The box is "somewhere" in memory, it has an address.
So now lets declare a new variable
int *mem_ptr = #
now this is a strange one, first, what's that star all about, and second, how are we putting the string &num into an integer variable?!
Well, firstly, the star specifies that this is a pointer, it's going to point to a memory address.
what comes next is a little weird and interesting.
we're not butting a string into the box we're telling it, that pointer, points to the address of the variable num, that & symbol is the address operator.
so let's look at this in a program.
#include <stdio.h>
int main()
{
int num;
int *mem_ptr = #
num = 8;
printf("num = %d\r\nptr = %d\r\n", num, *mem_ptr);
}
When you run this program you get the following output:
D:\coding\lesson14>tcc source.c
D:\coding\lesson14>source.exe
num = 8
ptr = 8
which is pretty obvious.
you're writing, Make a variable called num, that's an integer.
but what you're really saying is go get me 32 bits of memory, I want to store something, I'll refer to it as num...
So then when you use the pointer and say, the pointer has the same value as the address of the variable num (it looks at the same memory location) of course the value is the same, it's looking at the same 32 bits of memory,
as well as looking at the variable by inspecting the same memory location, we can also, write to memory by writing to the memory address that the variable uses by writing to the pointer to that memory address.
#include <stdio.h>
int main()
{
int num;
int *mem_ptr = #
num = 8;
printf("num = %d\r\nptr = %d\r\n", num, *mem_ptr);
*mem_ptr = 1;
printf("num = %d\r\nptr = %d\r\n", num, *mem_ptr);
}
D:\coding\lesson14>source.exe
num = 8
ptr = 8
num = 1
ptr = 1
we can also change the memory address that the pointer looks at during the program.
in the following source code we'll set up two variables, one called num and the other called digit, then tell the pointer to look at the contents of the address space given to num, (and we'll compare that to num) then we'll look at the contents of the address space given to digit to show how the pointer can move.
#include <stdio.h>
int main()
{
int num, digit;
int *mem_ptr = #
num = 8;
digit = 9;
printf("num = %d\r\nptr = %d\r\n", num, *mem_ptr);
mem_ptr = &digit;
printf("num = %d\r\nptr = %d\r\n", num, *mem_ptr);
}>
D:\coding\lesson14>source.exe
num = 8
ptr = 8
num = 8
ptr = 9
Types of pointer.
We're comfortable that a pointer looks to a memory location, and that can be changed.
so far out pointer
Monday, December 26, 2011
Coding lessons: C and String functions (lessons 13)
Firstly, Merry Christmas!!
Two lessons ago I discussed arrays, and how to use them.
in the last lessons I discussed that Strings were in fact just arrays of characters.
We noted (with code) that it was easy enough to poke values into these arrays, and pretty simple to read them back out using the %s operator, in the same way that we'd used %f for reading floating point numbers, and %i for integers and %c for characters.
It's all very well looking at how a program can display strings, but what we really want is to learn how to manipulate those strings.
This lesson will introduce some functions for manipulating strings.
Firstly, it's important to say that the C language has pretty much no built in functions for using strings. so far we've only been including the standard input/output library (stdio.h), but now we're going to need to start using a library that's been written especially to use strings.
string.h
the first function that we'll look at is strlen.
strlen determines the length of a string, it's a number, clearly an integer number you don't get half a letter in a string.
Number = strlen(string);
so a quick program that makes use of this is
#include<stdio .h>
#include<string .h>
int main()
{
char hw[] = "Hello World";
int num;
num = strlen(hw);
printf("the string %s is %i characters long", hw, num);
}
Now let's look at copying strings between different arrays.
to copy a string, we need to use strcpy (string copy)
strcpy is a function that you pass two parameters to
strcpy(destination, source);
this is different from how you're used to copying thins saying copy this source to that destination.
here's a quick program to look at strcpy
#include<stdio .h>
#include<string .h>
int main()
{
char hw[] = "Hello World";
char ds[20];
strcpy(ds, hw);
printf("the start string is %s\r\n this is copied to string 2 which says %s", hw, ds);
}
The next thing that we're going to do is look at comparing strings.
The function for comparing strings is called strcmp
There are three possible results for the strcmp function
the are 0, (they are identical)
Less than zero, (the strings are different and the first string is alphabetically before the second string, (e.g A is alphabetically before B).
if more than zero is returned this tells you that the first string is alphabetically after the second string. (e.g C is alphabetically after B).
#include<stdio .h>
#include<string .h>
int main()
{
char string1[] = "hello";
char string2[] = "world";
int result;
result = strcmp(string1, string2);
if (result < 0)
{
printf("the result is less than zero (%d), so string 1 (%s) is alphabetically before string2 (%s)", result, string1, string2);
}
if (result == 0)
{
printf("the result is zero (%d), so string 1 (%s) is athe same as string2 (%s)", result, string1, string2);
}
if (result > 0)
{
printf("the result is greater than zero (%d), so string 1 (%s) is alphabetically after string2 (%s)", result, string1, string2);
}
}
Now we'll look at adding strings together, to add strings together, Or catenate strings we use the strcat function.
when using strcat, like strcpy the name of the destination string comes as the first variable passed to the function, then data you want to poke into it comes next.
#include<stdio .h>
#include<string .h>
int main()
{
char firstname = "john";
char lastname = "doe";
char fullname[50]; /*make an array at least big enough to hold the greatest expected data entry*/
/*Print the last name string*/
printf("uncorrected last name = %s\r\r\n", lastname);
/*see we need to write null values to each box of the string array -we'll use a for loop if we don't do this then some odd things can be displayed!*/
for (i=0;i<=49;i++)
{
fullname[i]='\0';
}
/*first put the firstname into the string*/
strcat(fullname, firstname);
/*now put last name in after that*/
strcat(fullname, lastname);
printf("fullname = %s", fullname);
}
And for now that's all I'm going to write about strings.
Two lessons ago I discussed arrays, and how to use them.
in the last lessons I discussed that Strings were in fact just arrays of characters.
We noted (with code) that it was easy enough to poke values into these arrays, and pretty simple to read them back out using the %s operator, in the same way that we'd used %f for reading floating point numbers, and %i for integers and %c for characters.
It's all very well looking at how a program can display strings, but what we really want is to learn how to manipulate those strings.
This lesson will introduce some functions for manipulating strings.
Firstly, it's important to say that the C language has pretty much no built in functions for using strings. so far we've only been including the standard input/output library (stdio.h), but now we're going to need to start using a library that's been written especially to use strings.
string.h
the first function that we'll look at is strlen.
strlen determines the length of a string, it's a number, clearly an integer number you don't get half a letter in a string.
Number = strlen(string);
so a quick program that makes use of this is
#include<stdio .h>
#include<string .h>
int main()
{
char hw[] = "Hello World";
int num;
num = strlen(hw);
printf("the string %s is %i characters long", hw, num);
}
Now let's look at copying strings between different arrays.
to copy a string, we need to use strcpy (string copy)
strcpy is a function that you pass two parameters to
strcpy(destination, source);
this is different from how you're used to copying thins saying copy this source to that destination.
here's a quick program to look at strcpy
#include<stdio .h>
#include<string .h>
int main()
{
char hw[] = "Hello World";
char ds[20];
strcpy(ds, hw);
printf("the start string is %s\r\n this is copied to string 2 which says %s", hw, ds);
}
The next thing that we're going to do is look at comparing strings.
The function for comparing strings is called strcmp
There are three possible results for the strcmp function
the are 0, (they are identical)
Less than zero, (the strings are different and the first string is alphabetically before the second string, (e.g A is alphabetically before B).
if more than zero is returned this tells you that the first string is alphabetically after the second string. (e.g C is alphabetically after B).
#include<stdio .h>
#include<string .h>
int main()
{
char string1[] = "hello";
char string2[] = "world";
int result;
result = strcmp(string1, string2);
if (result < 0)
{
printf("the result is less than zero (%d), so string 1 (%s) is alphabetically before string2 (%s)", result, string1, string2);
}
if (result == 0)
{
printf("the result is zero (%d), so string 1 (%s) is athe same as string2 (%s)", result, string1, string2);
}
if (result > 0)
{
printf("the result is greater than zero (%d), so string 1 (%s) is alphabetically after string2 (%s)", result, string1, string2);
}
}
Now we'll look at adding strings together, to add strings together, Or catenate strings we use the strcat function.
when using strcat, like strcpy the name of the destination string comes as the first variable passed to the function, then data you want to poke into it comes next.
#include<stdio .h>
#include<string .h>
int main()
{
char firstname = "john";
char lastname = "doe";
char fullname[50]; /*make an array at least big enough to hold the greatest expected data entry*/
/*Print the last name string*/
printf("uncorrected last name = %s\r\r\n", lastname);
/*see we need to write null values to each box of the string array -we'll use a for loop if we don't do this then some odd things can be displayed!*/
for (i=0;i<=49;i++)
{
fullname[i]='\0';
}
/*first put the firstname into the string*/
strcat(fullname, firstname);
/*now put last name in after that*/
strcat(fullname, lastname);
printf("fullname = %s", fullname);
}
And for now that's all I'm going to write about strings.
Monday, December 19, 2011
Coding lesson: Loops (lesson 12)
In the last lesson I spoke about conditional statements.
Now I'm going to have a look at how we do something repeatedly whilst something is true, or how we do something a finite number of times.
Firstly I'll look at the while function
the while function does something whilst a condition is satisfied.
for example
i = 1;
while (i<=10)
{
printf("i isn't 10!");
}
as i is never modified this statement is always true, and so the loop never finishes.
i = 1;
while (i<=10)
{
printf("i isn't 10!");
i = i+1;
}
you see we're modifying i now so that eventually the condition for the loop to end will be met.
the while statement checks the condition before it is executed.
if we want to run the code at least once, and also again and again whilst a condition is true we use the do function
do
{
printf("what's the password?: ");
scanf("%s", &passwordguess);
}
while(passwordguess!=password);
this will ask you for a password, and would keep running until the condition was false, (until the password guess matched the password)
The other kind of loops that we can use are for loops.
The for loop sets up conditions in the start statement, and runs until the condition is satisfied.
for(start value; condition; modified for each loop)
for
a = 0; /*start a at zero*/
a ==9; /*do this loop whilst this condition is true/*
a = a+1 /*add 1 to the value a on each loop*/
for(i=0;i<=9;i=i+1)
{
printf("A");
}
this prints the letter A ten times.
Now I'm going to have a look at how we do something repeatedly whilst something is true, or how we do something a finite number of times.
Firstly I'll look at the while function
the while function does something whilst a condition is satisfied.
for example
i = 1;
while (i<=10)
{
printf("i isn't 10!");
}
as i is never modified this statement is always true, and so the loop never finishes.
i = 1;
while (i<=10)
{
printf("i isn't 10!");
i = i+1;
}
you see we're modifying i now so that eventually the condition for the loop to end will be met.
the while statement checks the condition before it is executed.
if we want to run the code at least once, and also again and again whilst a condition is true we use the do function
do
{
printf("what's the password?: ");
scanf("%s", &passwordguess);
}
while(passwordguess!=password);
this will ask you for a password, and would keep running until the condition was false, (until the password guess matched the password)
The other kind of loops that we can use are for loops.
The for loop sets up conditions in the start statement, and runs until the condition is satisfied.
for(start value; condition; modified for each loop)
for
a = 0; /*start a at zero*/
a ==9; /*do this loop whilst this condition is true/*
a = a+1 /*add 1 to the value a on each loop*/
for(i=0;i<=9;i=i+1)
{
printf("A");
}
this prints the letter A ten times.
Monday, December 12, 2011
Coding lessons: C and conditions (Lessons 11)
So far I've discussed how to get some data into a program, and how to display data out of a program.
I looked at adding number data together. Different ways to store and group data.
but so far that's pretty useless without being able to make decisions about data pin software.
So in this lesson I'm going to discuss conditions and statements in C.
so first lets have a look at the kind of operators we use when looking at logical conditions.
Does A equal B?
Does A not equal B?
Is A greater than B?
Is A less than B?
Is A greater than or equal to B?
Is A greater and B or Greater than C?
Is A greater than B and greater than C?
in short we have, equal, not equal, greater than, less than, greater than or equal, less than or equal.
and we want a way of glueing multiple conditions together in an if this AND this, or and if this OR this kind of way.
we also want to know what to do:
if This then do this, otherwise do that instead.
what we're going to look at is IF
if is a function, and like all functions we have a function name, and then parameters to pass to it that are contained inside brackets.
if(conditions)
our conditions are all logic based
if 1 = 1 then...
except, we already have a function that happens when we write a=1;
so for the logical is a = b we say
if (a==b)
(double equals signs)
instead of saying then, we open new curly brackets
if (1==1)
{
printf("one equals one");
}
notice that the ilne with the if statement is not terminated with a semi colon
for not equal we use the symbol !=
if (1!=1)
{
printf("Oh dear, one doesn't equal one any more");
}
in some circumstances we may want to have alternative paths.
for this we use the else statement
if (1!=1)
{
printf("Oh dear, one doesn't equal one any more");
}
else
{
printf("It's OK, maths still works");
}
I also mentioned that we may like to use the greater than > or less than <
or greater than or equal to >= or less than or equal to <=
if (1<=5)
or more useful for programs would be saying is a variable something
if(a>=10)
Now there may be times when we want to glue a few things together.
In this case we say
is A equal to be, and equal to C?
if ((a==b)&&(a=c))
{
printf("A, B and C are all equal");
}
you can understand how the & symbol is used for AND statements, (well two of them)
for OR statements we use two pipe characters ||
if ((a>=b)||(a>=c))
{
printf("A is greater than or equal to either B or C");
}
you use brackets to contain statements in exactly the same way you would in regular mathematical expressions.
Finally, you may wish to have a lot of possible outcomes, in this case rather than writing out.
if (a==1)
{
...
}
if (a==2)
{
...
}
you use a switch function.
the switch function compares a variable against a series of outputs and runs code against a chosen output.
if no pre-defined output matches the variable then a default set of code can be run.
Lets say that variable A is some error code information.
where the following statements describe the error
if the error code is 0, the program is fine.
if the error code is 1, the program ran out of memory
If anything else happens then this is bad, because some kind of unknown condition has occurred.
so the code would look something like this:
switch (a)
{
case '0':
printf("no errors have been reported");
break;
case '1':
printf("This program requires more memory, you must have at least some memory to run this program");
break;
default:
printf("this program encountered an unexpected condition and should be shut down");
}
This should be enough information to get you writing some code that you can use to start manipulating data with.
a good example would be the body mass calculator made in a previous lab, rather than telling people what their body mass index is, then telling them to figure out for themselves if they have a problem you can instead use a nested if statement.
if (bmi>25)
{
printf("overweight");
}
else if(bmi > 20)
{
printf("ideal weight") /*at this point we know it's less than 25 else the previous statement would have been executed*/
}
else if(bmi>15)
{
printf("underweight");
}
I looked at adding number data together. Different ways to store and group data.
but so far that's pretty useless without being able to make decisions about data pin software.
So in this lesson I'm going to discuss conditions and statements in C.
so first lets have a look at the kind of operators we use when looking at logical conditions.
Does A equal B?
Does A not equal B?
Is A greater than B?
Is A less than B?
Is A greater than or equal to B?
Is A greater and B or Greater than C?
Is A greater than B and greater than C?
in short we have, equal, not equal, greater than, less than, greater than or equal, less than or equal.
and we want a way of glueing multiple conditions together in an if this AND this, or and if this OR this kind of way.
we also want to know what to do:
if This then do this, otherwise do that instead.
what we're going to look at is IF
if is a function, and like all functions we have a function name, and then parameters to pass to it that are contained inside brackets.
if(conditions)
our conditions are all logic based
if 1 = 1 then...
except, we already have a function that happens when we write a=1;
so for the logical is a = b we say
if (a==b)
(double equals signs)
instead of saying then, we open new curly brackets
if (1==1)
{
printf("one equals one");
}
notice that the ilne with the if statement is not terminated with a semi colon
for not equal we use the symbol !=
if (1!=1)
{
printf("Oh dear, one doesn't equal one any more");
}
in some circumstances we may want to have alternative paths.
for this we use the else statement
if (1!=1)
{
printf("Oh dear, one doesn't equal one any more");
}
else
{
printf("It's OK, maths still works");
}
I also mentioned that we may like to use the greater than > or less than <
or greater than or equal to >= or less than or equal to <=
if (1<=5)
or more useful for programs would be saying is a variable something
if(a>=10)
Now there may be times when we want to glue a few things together.
In this case we say
is A equal to be, and equal to C?
if ((a==b)&&(a=c))
{
printf("A, B and C are all equal");
}
you can understand how the & symbol is used for AND statements, (well two of them)
for OR statements we use two pipe characters ||
if ((a>=b)||(a>=c))
{
printf("A is greater than or equal to either B or C");
}
you use brackets to contain statements in exactly the same way you would in regular mathematical expressions.
Finally, you may wish to have a lot of possible outcomes, in this case rather than writing out.
if (a==1)
{
...
}
if (a==2)
{
...
}
you use a switch function.
the switch function compares a variable against a series of outputs and runs code against a chosen output.
if no pre-defined output matches the variable then a default set of code can be run.
Lets say that variable A is some error code information.
where the following statements describe the error
if the error code is 0, the program is fine.
if the error code is 1, the program ran out of memory
If anything else happens then this is bad, because some kind of unknown condition has occurred.
so the code would look something like this:
switch (a)
{
case '0':
printf("no errors have been reported");
break;
case '1':
printf("This program requires more memory, you must have at least some memory to run this program");
break;
default:
printf("this program encountered an unexpected condition and should be shut down");
}
This should be enough information to get you writing some code that you can use to start manipulating data with.
a good example would be the body mass calculator made in a previous lab, rather than telling people what their body mass index is, then telling them to figure out for themselves if they have a problem you can instead use a nested if statement.
if (bmi>25)
{
printf("overweight");
}
else if(bmi > 20)
{
printf("ideal weight") /*at this point we know it's less than 25 else the previous statement would have been executed*/
}
else if(bmi>15)
{
printf("underweight");
}
Monday, December 05, 2011
Coding lessons: C and strings (lesson 10)
In the last C lessons I talked about Arrays, I sort of gave some poor examples of how you may want to use an array for storing co-ordinate data, (for a map or a graph?) but really I just needed to introduce the concept.
In programming a string is a series of characters, each and every word I type is a string, this sentence is a string.
Strings are groups of characters all placed side by side in an array.
Boxes
The simplest way to describe an array is to look at a single input as a box, our Char variable allows only a single character to be put into the variable, it's like a single box that's 8 bits wide (so it can store the 8 bit char.)
Arrays
An array is like a load of boxes all placed next to each other.
The word "example" is a string, it's an array of characters, that's 7 characters in length
e x a m p l e
so we have seven 8 bit wide boxes (one for each letter) placed side by side.
basically, a string is an array of characters,
BUT what separates a string from an array of charecters is that the last "data pocket" in the array that makes up a string is a special NUL character. (this is just a regular char with value 0)
We can create strings in two ways.
Either declare an array, then enter in the data, and enter in the NUL character ourselves, or we can declare and fill the string when declaring it.
#include <stdio .h>
int main()
{
char hello[6];
hello[0] = 'H';
hello[1] = 'e';
hello[2] = 'l';
hello[3] = 'l';
hello[4] = 'o';
hello[5] = '\0';
printf("%s", hello);
return(0);
}
but that is a very difficult way of working with strings, in order to do more complex stuff with strings we will need to include the library string.h
in this next example we can see that we can have the compiler automatically see that we're creating a string, and not only create an array of the correct size of what we're trying to put in there, but also add the NUL character into the end of the array also.
#include <stdio .h>
int main()
{
char hello[6];
char world[] ="world";
hello[0] = 'H';
hello[1] = 'e';
hello[2] = 'l';
hello[3] = 'l';
hello[4] = 'o';
hello[5] = '\0';
printf("%s %s", hello, world);
return(0);
}
Notice in both these examples that when poking characters into the array they are contained in a single quote.
single quotes for characters, double quotes for strings.
hello[0] = "H";
Doesn't work because as H is contained inside double quotes it's data type is a string. you can't put a string of data into a single char space of data. trying to do this will result in a program that throws errors whilst compiling.
In programming a string is a series of characters, each and every word I type is a string, this sentence is a string.
Strings are groups of characters all placed side by side in an array.
Boxes
The simplest way to describe an array is to look at a single input as a box, our Char variable allows only a single character to be put into the variable, it's like a single box that's 8 bits wide (so it can store the 8 bit char.)
Arrays
An array is like a load of boxes all placed next to each other.
The word "example" is a string, it's an array of characters, that's 7 characters in length
e x a m p l e
so we have seven 8 bit wide boxes (one for each letter) placed side by side.
basically, a string is an array of characters,
BUT what separates a string from an array of charecters is that the last "data pocket" in the array that makes up a string is a special NUL character. (this is just a regular char with value 0)
We can create strings in two ways.
Either declare an array, then enter in the data, and enter in the NUL character ourselves, or we can declare and fill the string when declaring it.
#include <stdio .h>
int main()
{
char hello[6];
hello[0] = 'H';
hello[1] = 'e';
hello[2] = 'l';
hello[3] = 'l';
hello[4] = 'o';
hello[5] = '\0';
printf("%s", hello);
return(0);
}
but that is a very difficult way of working with strings, in order to do more complex stuff with strings we will need to include the library string.h
in this next example we can see that we can have the compiler automatically see that we're creating a string, and not only create an array of the correct size of what we're trying to put in there, but also add the NUL character into the end of the array also.
#include <stdio .h>
int main()
{
char hello[6];
char world[] ="world";
hello[0] = 'H';
hello[1] = 'e';
hello[2] = 'l';
hello[3] = 'l';
hello[4] = 'o';
hello[5] = '\0';
printf("%s %s", hello, world);
return(0);
}
Notice in both these examples that when poking characters into the array they are contained in a single quote.
single quotes for characters, double quotes for strings.
hello[0] = "H";
Doesn't work because as H is contained inside double quotes it's data type is a string. you can't put a string of data into a single char space of data. trying to do this will result in a program that throws errors whilst compiling.
Monday, November 14, 2011
Coding Lessons: C and Arrays (lesson 9)
Arrays are a pretty important part of the way in which information is stored. This is true of both programming on computers, and (as a hint of things to come) also when programming embedded micro controllers for projects that you may create.
You create arrays when you want to store many items of the same type of data, and you want them to be stored in adjoining memory locations to make locating or sorting them easy.
This could be (for example) a series of temperature measurements.
So we have the idea that an array is a series or collection of data, you can imagine it as a table row.
something like this
[1][2][3][4][5][6][7][8]
and we've covered that the data is of the same type, (all int, or all char, etc) so now lets have a look at how we define and element and how we use an element.
Remember we define a single value like this
int x;
that is I want this type of data (int) stored as this variable name (x)
defining an array is not much different.
int x[8];
I want to store integers (int) as this variable name (x) and I want this many pockets to put data in (8)
When you access an array it is treated like a normal piece of data.
for example.
If I had a program.
int x;
int y;
int z;
I have 3 bits of data.
I can say
x = 1;
y = 2;
z = 3;
and call them like this
printf("x = %d\r\n", x);
printf("y = %d\r\n", y);
printf("z = %d\r\n", z);
but if I use an array I can store all the data in adjoining memory.
now I say
int coordinates[3];
coordinates[0]=1;
coordinates[1]=2;
coordinates[2]=3;
and I can recall the data from it's storage location, in much the same way as using standard data;
printf("x = %d", coordinates[0]);
and so on.
Now this interesting thing for anyone paying attention there is that we're not starting counting from 1, Just as data pins on microchips start counting with pin0, pin1 etc in arrays we count our first data "pocket" as 0
Multi Dimensional Arrays
You may have noticed that a single table row is a little, well limiting, I gave an example of a coordinate above.
Imagine you're drawing a graph (x,y), what are you going to do? declare multiple arrays
int datapoint1[2];
int datapoint2[2];
...
int datapoint8[2];
or are you going to try to make an array that is:
int datapoints[16];
(where 0 = x1, 1 = y1, 2=x2, 3 = y2, 4 = x3... -far too confusing)
or instead would it make more sense to define your array in multiple dimensions. more like a table?
int datapoints[8][8];
Now you can store your x an y values in a way that makes sense to you.
The thing that you should note is that this is not an array that looks like a table 8 columns wide and 2 rows deep, this table is 8 wide, 8 deep, there are 256 data pockets.
storing 2d co-ordinates like this would be a bit of a waste of memory.
2 dimensional arrays are not the limit, you can create incredibly complex multi dimensional arrays.
int array[10][10][10]; = 1000 array pockets
int array[10][10][10][10]; = 10,000 array pockets
you see how it adds up fast.
On a modern computer with near limitless amounts of memory this is unlikely to be a problem, you can waste memory, like making an 8x8 256 pocket array for storing 16 values, to make 8 graph points on a line graph.
but on a lower spec'd device (like an embedded processor) you might want to be more careful with your memory use.
in this case your 2d array for a graph might be
int points[8][2];
and you'd have data
[x1][x2][x3][x4][x5][x6][x7][x8]
[y1][y2][y3][y4][y5][y6][y7][y8]
You create arrays when you want to store many items of the same type of data, and you want them to be stored in adjoining memory locations to make locating or sorting them easy.
This could be (for example) a series of temperature measurements.
So we have the idea that an array is a series or collection of data, you can imagine it as a table row.
something like this
[1][2][3][4][5][6][7][8]
and we've covered that the data is of the same type, (all int, or all char, etc) so now lets have a look at how we define and element and how we use an element.
Remember we define a single value like this
int x;
that is I want this type of data (int) stored as this variable name (x)
defining an array is not much different.
int x[8];
I want to store integers (int) as this variable name (x) and I want this many pockets to put data in (8)
When you access an array it is treated like a normal piece of data.
for example.
If I had a program.
int x;
int y;
int z;
I have 3 bits of data.
I can say
x = 1;
y = 2;
z = 3;
and call them like this
printf("x = %d\r\n", x);
printf("y = %d\r\n", y);
printf("z = %d\r\n", z);
but if I use an array I can store all the data in adjoining memory.
now I say
int coordinates[3];
coordinates[0]=1;
coordinates[1]=2;
coordinates[2]=3;
and I can recall the data from it's storage location, in much the same way as using standard data;
printf("x = %d", coordinates[0]);
and so on.
Now this interesting thing for anyone paying attention there is that we're not starting counting from 1, Just as data pins on microchips start counting with pin0, pin1 etc in arrays we count our first data "pocket" as 0
Multi Dimensional Arrays
You may have noticed that a single table row is a little, well limiting, I gave an example of a coordinate above.
Imagine you're drawing a graph (x,y), what are you going to do? declare multiple arrays
int datapoint1[2];
int datapoint2[2];
...
int datapoint8[2];
or are you going to try to make an array that is:
int datapoints[16];
(where 0 = x1, 1 = y1, 2=x2, 3 = y2, 4 = x3... -far too confusing)
or instead would it make more sense to define your array in multiple dimensions. more like a table?
int datapoints[8][8];
Now you can store your x an y values in a way that makes sense to you.
The thing that you should note is that this is not an array that looks like a table 8 columns wide and 2 rows deep, this table is 8 wide, 8 deep, there are 256 data pockets.
storing 2d co-ordinates like this would be a bit of a waste of memory.
2 dimensional arrays are not the limit, you can create incredibly complex multi dimensional arrays.
int array[10][10][10]; = 1000 array pockets
int array[10][10][10][10]; = 10,000 array pockets
you see how it adds up fast.
On a modern computer with near limitless amounts of memory this is unlikely to be a problem, you can waste memory, like making an 8x8 256 pocket array for storing 16 values, to make 8 graph points on a line graph.
but on a lower spec'd device (like an embedded processor) you might want to be more careful with your memory use.
in this case your 2d array for a graph might be
int points[8][2];
and you'd have data
[x1][x2][x3][x4][x5][x6][x7][x8]
[y1][y2][y3][y4][y5][y6][y7][y8]
Monday, September 26, 2011
Coding Lessons: C and included header files (lesson 8)
In the last lesson I introduced the idea of putting either complicated or repetitive lines of code into a function of their own in order to reduce the amount of typing that you may need to do.
Now, I'm going to introduce a radical new concept, what if as well as using a function multiple times in that program, you also want to use that same function in multiple programs, are you going to copy and paste your function hundreds of times into your new code?
In the same way as you don't re-write all the code to actually do the getting of characters onto the screen, (you just use the printf function, you don't write out all the code each time) you also don't want to have to re-write (or copy paste) the code for your repetitive functions.
You might be wondering, since I've just said that printf is a function, where is this function? where is it declared? where is it called?
Well, you'll notice that the first line of every program that we've written so far has #include, (that means include this file), then the name of a file, the fact that the file name is in little triangular brackets means that it's in the tcc includes folder.)
well, those functions, Printf, scanf (and a whole load more) are declared in that stdio.h file.
you see that exporting those functions to a header file is useful, what's contained in the header file is just code, (exactly like we write), but it's used again and again, (in every program we've written so far).
so lets have a look at including our own header files.
so we start the code in the usual fashion
(include the standard header files from the includes directory of the Tcc compiler)
now we'll include our own header file, but this time from the same source directory.
see how it's included in quote marks, and not brackets. that means from this path, rather than from your library.
of course, copying header files might not be your idea of a great time so you could put your header into the main includes folder in the tcc directory, or you might want to start a header library of your own and include like this:
After this we write the main part of the program exactly as before.
inside our source directory we also need to have the header file, (called bmi.h)
this header file is literally, a line to declare a function exists, and the function, exactly the same as the last lesson:
float bodymassindex(h, w);
float bodymassindex(int h, int w)
{
float result;
/*bmi = mass(kg) / height^2(m)*/
result = h * h;
result = result/10000;
result = w/result;
return(result);
}
in completion the source code looks like this:
File 1, source.c
#include <stdio.h>
#include "bmi.h"
int main()
{
int weight, height;
float bmi;
printf("BMI Calculator\r\n");
printf("Enter your weight in Kilos:");
scanf("%d", &weight);
printf("Please enter your height in centimeters:");
scanf("%d", &height);
bmi = bodymassindex(height, weight);
printf("your BMI is: %f\r\n", bmi);
printf("\r\n\r\nUnderweight = < 18 -="-" .5=".5" 24.9="24.9" 29.9="29.9" 30="30" br="br" greater="greater" n="n" nnormal="nnormal" nobesity="BMI" noverweight="25" of="of" or="or" r="r" weight="18.5">
}
File 2, bmi.h
float bodymassindex(h, w);
float bodymassindex(int h, int w)
{
float result;
/*bmi = mass(kg) / height^2(m)*/
result = h * h;
result = result/10000;
result = w/result;
return(result);
}
Complied and run, this program looks exactly the same as the program fro mthe last two examples.
Now, I'm going to introduce a radical new concept, what if as well as using a function multiple times in that program, you also want to use that same function in multiple programs, are you going to copy and paste your function hundreds of times into your new code?
In the same way as you don't re-write all the code to actually do the getting of characters onto the screen, (you just use the printf function, you don't write out all the code each time) you also don't want to have to re-write (or copy paste) the code for your repetitive functions.
You might be wondering, since I've just said that printf is a function, where is this function? where is it declared? where is it called?
Well, you'll notice that the first line of every program that we've written so far has #include, (that means include this file), then the name of a file, the fact that the file name is in little triangular brackets means that it's in the tcc includes folder.)
well, those functions, Printf, scanf (and a whole load more) are declared in that stdio.h file.
you see that exporting those functions to a header file is useful, what's contained in the header file is just code, (exactly like we write), but it's used again and again, (in every program we've written so far).
so lets have a look at including our own header files.
so we start the code in the usual fashion
(include the standard header files from the includes directory of the Tcc compiler)
#include <stdio.h>
now we'll include our own header file, but this time from the same source directory.
#include "bmi.h"
see how it's included in quote marks, and not brackets. that means from this path, rather than from your library.
of course, copying header files might not be your idea of a great time so you could put your header into the main includes folder in the tcc directory, or you might want to start a header library of your own and include like this:
#include "../../mylibrary/bmi.h"
After this we write the main part of the program exactly as before.
int main()
{
int weight, height;
float bmi;
printf("BMI Calculator\r\n");
printf("Enter your weight in Kilos:");
scanf("%d", &weight);
printf("Please enter your height in centimeters:");
scanf("%d", &height);
bmi = bodymassindex(height, weight);
printf("your BMI is: %f\r\n", bmi);
printf("\r\n\r\nUnderweight = <18 -="-" .5=".5" 24.9="24.9" 29.9="29.9" 30="30" br="br" greater="greater" n="n" nnormal="nnormal" nobesity="BMI" noverweight="25" of="of" or="or" r="r" weight="18.5">18>
}inside our source directory we also need to have the header file, (called bmi.h)
this header file is literally, a line to declare a function exists, and the function, exactly the same as the last lesson:
float bodymassindex(h, w);
float bodymassindex(int h, int w)
{
float result;
/*bmi = mass(kg) / height^2(m)*/
result = h * h;
result = result/10000;
result = w/result;
return(result);
}
in completion the source code looks like this:
File 1, source.c
#include <stdio.h>
#include "bmi.h"
int main()
{
int weight, height;
float bmi;
printf("BMI Calculator\r\n");
printf("Enter your weight in Kilos:");
scanf("%d", &weight);
printf("Please enter your height in centimeters:");
scanf("%d", &height);
bmi = bodymassindex(height, weight);
printf("your BMI is: %f\r\n", bmi);
printf("\r\n\r\nUnderweight = < 18 -="-" .5=".5" 24.9="24.9" 29.9="29.9" 30="30" br="br" greater="greater" n="n" nnormal="nnormal" nobesity="BMI" noverweight="25" of="of" or="or" r="r" weight="18.5">
}
File 2, bmi.h
float bodymassindex(h, w);
float bodymassindex(int h, int w)
{
float result;
/*bmi = mass(kg) / height^2(m)*/
result = h * h;
result = result/10000;
result = w/result;
return(result);
}
Complied and run, this program looks exactly the same as the program fro mthe last two examples.
Friday, September 23, 2011
Coding Lessons: C and functions (lesson 7)
OK so we learned a little about accepting inputs, and made a sort of useful tool into the bargain. -I say sort of useful because there are online versions everywhere, the point wasn't to create a useful tool, the point was to introduce manipulating the variables.
Now lets take a step back and look at some simple functions.
we always have a main function, this is where the "meat" of our program goes.
but lets says that we have a defined function
we'll take something simple, like A+B
now we can write C = A+B.
but what if this were actually a really complicated equation, and we're using it hundreds of times, are we going to write it out each time? copy and paste code?
what if we notice a mistake -then we'll hace to correct all the hundreds of times we've written this out.
I guess it's not easier to write c = add(a, b) but for a more complex function this is useful.
what if it weren't adding, what if it were calculating VAT, a change in rate means that you have to search all your code, but if you had a function for calculating VAT, you'd only need to change that function.
Lets' look again at BMI.
as before we include standard libraries:
#include <stdio.h>but this time there is a change, we don't jump right into our main function, we tell the program that there is another function.
float bodymassindex(h, w);
Everything in the first half of this example is the same as the last lesson, show prompts, gather data.
int main()
{
int weight, height;
float bmi;
printf("BMI Calculator\r\n");
printf("Enter your weight in Kilos:");
scanf("%d", &weight);
printf("Please enter your height in centimeters:");
scanf("%d", &height);
But in this example the data processing has been moved out to a function, we pass that function the numbers,
bmi = bodymassindex(height, weight); it returns a result, which we then carry on using as normal.
printf("your BMI is: %f\r\n", bmi);
printf("\r\n\r\nUnderweight = <18 -="-" .5=".5" 24.9="24.9" 29.9="29.9" 30="30" br="br" greater="greater" n="n" nnormal="nnormal" nobesity="BMI" noverweight="25" of="of" or="or" r="r" weight="18.5">}18>
the function is written underneath the main part of the program, but in reality it works just the same as the program.
in the main program we expect an integer error code to say if execution has completed sucessfully, so the main part of the program is declared as int main()
We're expecting a floating point to be returned, so we declare the function as a float.
float bodymassindex(int h, int w)
also we tell the function what sort of variables it'll be getting, notice that they have to be the same type (in this case integers), but they don't need the same names, so I shortened height to h and weight to w.
then we come to the function, just as in the last example this calculates the BMI number
{
float result;
/*bmi = mass(kg) / height^2(m)*/
result = h * h;
result = result/10000;
result = w/result;
Then the function returns it's result using the return function.
return(result);
}
Put all together the code looks like this: (and gives the same output as the last lesson).
#include <stdio.h>
float bodymassindex(h, w);
int main()
{
int weight, height;
float bmi;
printf("BMI Calculator\r\n");
printf("Enter your weight in Kilos:");
scanf("%d", &weight);
printf("Please enter your height in centimeters:");
scanf("%d", &height);
bmi = bodymassindex(height, weight);
printf("your BMI is: %f\r\n", bmi);
printf("\r\n\r\nUnderweight = <18 -="-" .5=".5" 24.9="24.9" 29.9="29.9" 30="30" greater="greater" n="n" nnormal="nnormal" nobesity="BMI" noverweight="25" of="of" or="or" r="r" span="span" weight="18.5">
}
float bodymassindex(int h, int w)
{
float result;
/*bmi = mass(kg) / height^2(m)*/
result = h * h;
result = result/10000;
result = w/result;
return(result);
} 18>
The difference is that this code is much more maintainable, you might not see it for a small program like this, (in fact it's arguable more work to put in functions than just write the calculations in the code), but if this were a part of a much larger, millions of lines of code program, using this function technique means that you can correct errors without having to search through all lines of code.
It can also reduce lines of code, say I was doing this calculation 3 million times, the calculation is only 3 lines long, but that means that over all this will take 3 million lines of code in my source.
if I decided that I needed height cubed instead of squared, I'd have to alter 1 million lines.
using a function I only need to call the function 1 million times so instead of three million lines of code, I have one million, plus the 9 lines involved in setting up and actually doing the function.
now if I decide that I want height cubed instead of squared, I change 1 line of code.
Now lets take a step back and look at some simple functions.
we always have a main function, this is where the "meat" of our program goes.
but lets says that we have a defined function
we'll take something simple, like A+B
now we can write C = A+B.
but what if this were actually a really complicated equation, and we're using it hundreds of times, are we going to write it out each time? copy and paste code?
what if we notice a mistake -then we'll hace to correct all the hundreds of times we've written this out.
I guess it's not easier to write c = add(a, b) but for a more complex function this is useful.
what if it weren't adding, what if it were calculating VAT, a change in rate means that you have to search all your code, but if you had a function for calculating VAT, you'd only need to change that function.
Lets' look again at BMI.
as before we include standard libraries:
#include <stdio.h>but this time there is a change, we don't jump right into our main function, we tell the program that there is another function.
float bodymassindex(h, w);
Everything in the first half of this example is the same as the last lesson, show prompts, gather data.
int main()
{
int weight, height;
float bmi;
printf("BMI Calculator\r\n");
printf("Enter your weight in Kilos:");
scanf("%d", &weight);
printf("Please enter your height in centimeters:");
scanf("%d", &height);
But in this example the data processing has been moved out to a function, we pass that function the numbers,
bmi = bodymassindex(height, weight); it returns a result, which we then carry on using as normal.
printf("your BMI is: %f\r\n", bmi);
printf("\r\n\r\nUnderweight = <18 -="-" .5=".5" 24.9="24.9" 29.9="29.9" 30="30" br="br" greater="greater" n="n" nnormal="nnormal" nobesity="BMI" noverweight="25" of="of" or="or" r="r" weight="18.5">}18>
the function is written underneath the main part of the program, but in reality it works just the same as the program.
in the main program we expect an integer error code to say if execution has completed sucessfully, so the main part of the program is declared as int main()
We're expecting a floating point to be returned, so we declare the function as a float.
float bodymassindex(int h, int w)
also we tell the function what sort of variables it'll be getting, notice that they have to be the same type (in this case integers), but they don't need the same names, so I shortened height to h and weight to w.
then we come to the function, just as in the last example this calculates the BMI number
{
float result;
/*bmi = mass(kg) / height^2(m)*/
result = h * h;
result = result/10000;
result = w/result;
Then the function returns it's result using the return function.
return(result);
}
Put all together the code looks like this: (and gives the same output as the last lesson).
#include <stdio.h>
float bodymassindex(h, w);
int main()
{
int weight, height;
float bmi;
printf("BMI Calculator\r\n");
printf("Enter your weight in Kilos:");
scanf("%d", &weight);
printf("Please enter your height in centimeters:");
scanf("%d", &height);
bmi = bodymassindex(height, weight);
printf("your BMI is: %f\r\n", bmi);
printf("\r\n\r\nUnderweight = <18 -="-" .5=".5" 24.9="24.9" 29.9="29.9" 30="30" greater="greater" n="n" nnormal="nnormal" nobesity="BMI" noverweight="25" of="of" or="or" r="r" span="span" weight="18.5">
}
float bodymassindex(int h, int w)
{
float result;
/*bmi = mass(kg) / height^2(m)*/
result = h * h;
result = result/10000;
result = w/result;
return(result);
} 18>
The difference is that this code is much more maintainable, you might not see it for a small program like this, (in fact it's arguable more work to put in functions than just write the calculations in the code), but if this were a part of a much larger, millions of lines of code program, using this function technique means that you can correct errors without having to search through all lines of code.
It can also reduce lines of code, say I was doing this calculation 3 million times, the calculation is only 3 lines long, but that means that over all this will take 3 million lines of code in my source.
if I decided that I needed height cubed instead of squared, I'd have to alter 1 million lines.
using a function I only need to call the function 1 million times so instead of three million lines of code, I have one million, plus the 9 lines involved in setting up and actually doing the function.
now if I decide that I want height cubed instead of squared, I change 1 line of code.
Tuesday, September 20, 2011
Coding Lessons: C A simple Program (lesson 6)
So far we've only looked at how to get values into and out of the console.
so lets look at a very simple program, we'll still accept inputs from the command line, and output them to the command line. but, the thing that makes this different is that it's a program that actually does something.
Since the world is becoming obsessive of its weight, lets make a BMI calculator.
BMI is a number, it is derived by knowing your weight in kilos, and dividing that by your height in meters squared.
BMI = M / H^2
We'll be including the stadard io library, and opening our main program as usual
{
After this we want to sort out our variables, there is weight and height, (you'd expect these to be whole numbers, so we'll declare them as integers).
float bmi;
After this we'll do some nice user prompts, and grab some inputs,
printf("BMI Calculator\r\n");
printf("Enter your weight in Kilos:");
scanf("%d", &weight);
printf("Please enter your height in centimeters:");
scanf("%d", &height);
now that we have those inputs we need to transform them,
to do this we'll square the height first, (multiply it by itself)
then we'll need to divide that number by 100 (squared) to get the height in meters, (from the height entered in centimeters.
In order to reduce the amount of variables that I'll be declaring, I'll re-use the floating point variable over and over, pushing the results of equations into it, then using it in the next equation:
/*bmi = mass(kg) / height^2(m)*/
bmi = height * height;
bmi = bmi/10000;
bmi = weight/bmi;
After this we'll display the BMI figure, and a explanation of what it actually means:
printf("your BMI is: %f\r\n", bmi);
printf("\r\n\r\nUnderweight = <18 -="-" .5=".5" 24.9="24.9" 29.9="29.9" 30="30" br="br" greater="greater" n="n" nnormal="nnormal" nobesity="BMI" noverweight="25" of="of" or="or" r="r" style="color: red;" weight="18.5">}18>
Put it all together and you have:
so lets look at a very simple program, we'll still accept inputs from the command line, and output them to the command line. but, the thing that makes this different is that it's a program that actually does something.
Since the world is becoming obsessive of its weight, lets make a BMI calculator.
BMI is a number, it is derived by knowing your weight in kilos, and dividing that by your height in meters squared.
BMI = M / H^2
We'll be including the stadard io library, and opening our main program as usual
#include <stdio.h>
int main(){
After this we want to sort out our variables, there is weight and height, (you'd expect these to be whole numbers, so we'll declare them as integers).
int weight, height;
Then we'll need something that will take the result of the equation above for BMI, there is a very good chance that this will be a number with decimal places, so we'll declare a floating point numberfloat bmi;
After this we'll do some nice user prompts, and grab some inputs,
printf("BMI Calculator\r\n");
printf("Enter your weight in Kilos:");
scanf("%d", &weight);
printf("Please enter your height in centimeters:");
scanf("%d", &height);
now that we have those inputs we need to transform them,
to do this we'll square the height first, (multiply it by itself)
then we'll need to divide that number by 100 (squared) to get the height in meters, (from the height entered in centimeters.
In order to reduce the amount of variables that I'll be declaring, I'll re-use the floating point variable over and over, pushing the results of equations into it, then using it in the next equation:
/*bmi = mass(kg) / height^2(m)*/
bmi = height * height;
bmi = bmi/10000;
bmi = weight/bmi;
After this we'll display the BMI figure, and a explanation of what it actually means:
printf("your BMI is: %f\r\n", bmi);
printf("\r\n\r\nUnderweight = <18 -="-" .5=".5" 24.9="24.9" 29.9="29.9" 30="30" br="br" greater="greater" n="n" nnormal="nnormal" nobesity="BMI" noverweight="25" of="of" or="or" r="r" style="color: red;" weight="18.5">}18>
Put it all together and you have:
#include <stdio.h>
int main()
{
int weight, height;
float bmi;
printf("BMI Calculator\r\n");
printf("Enter your weight in Kilos:");
scanf("%d", &weight);
printf("Please enter your height in centimeters:");
scanf("%d", &height);
/*bmi = mass(kg) / height^2(m)*/
bmi = height * height;
bmi = bmi/10000;
bmi = weight/bmi;
printf("your BMI is: %f\r\n", bmi);
printf("\r\n\r\nUnderweight = <18 -="-" .5=".5" 24.9="24.9" 29.9="29.9" 30="30" br="br" greater="greater" n="n" nnormal="nnormal" nobesity="BMI" noverweight="25" of="of" or="or" r="r" weight="18.5">} 18>
int main()
{
int weight, height;
float bmi;
printf("BMI Calculator\r\n");
printf("Enter your weight in Kilos:");
scanf("%d", &weight);
printf("Please enter your height in centimeters:");
scanf("%d", &height);
/*bmi = mass(kg) / height^2(m)*/
bmi = height * height;
bmi = bmi/10000;
bmi = weight/bmi;
printf("your BMI is: %f\r\n", bmi);
printf("\r\n\r\nUnderweight = <18 -="-" .5=".5" 24.9="24.9" 29.9="29.9" 30="30" br="br" greater="greater" n="n" nnormal="nnormal" nobesity="BMI" noverweight="25" of="of" or="or" r="r" weight="18.5">} 18>
Compile and run to see the following:
D:\coding\lesson6>source.exe
BMI Calculator
Enter your weight in Kilos:85
Please enter your height in centimeters:195
your BMI is: 22.353714
Underweight = <18 .5=".5" br="br">Normal weight = 18.5û24.9
Overweight = 25û29.9
Obesity = BMI of 30 or greater
D:\coding\lesson6>18>
BMI Calculator
Enter your weight in Kilos:85
Please enter your height in centimeters:195
your BMI is: 22.353714
Underweight = <18 .5=".5" br="br">Normal weight = 18.5û24.9
Overweight = 25û29.9
Obesity = BMI of 30 or greater
D:\coding\lesson6>18>
Subscribe to:
Posts (Atom)