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 code. Show all posts
Showing posts with label code. 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, 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 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, 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>
Tuesday, September 06, 2011
Coding Lessons: C and more inputs (Lesson 5)
So in the last lesson we dealt with only integers, in the same way as lesson 2 introduced the idea of variables, and we focused on one type of input (mostly so that I could keep my focus and the lesson wouldn't go on too long).
Now we're going to take a look at all the other types that are available to us.
Other Variables
We did integers pretty much to death in the last lesson, looking at how to declare, then how to read them, how to over flow them.
Remember how I said that the scanf statement looked like the printf statement. and worked in much the same way too?
Well just like the printf statement you can specify the width of the number, you can add modifiers (long, short signed and unsigned) to the type...
For example
%9ld would take an input that was 9 characters in width, and was a long integer.
So lets go over getting variables into a program.
We'll of course start by making a program, (so create a new lesson 5 folder and a new source file etc).
Then start writing the source in the normal way:
#include<stdio.h> int main() {
and declare some variables of different types that we'll be using to store the data entered by users in.
short int whole_number; char character1, character2; float not_whole_number;
Now lets start asking the user of the program to enter data,
Again we'll start by asking the user to enter the first char that we defined:
printf("enter char1:"); scanf("%c", &character1);
Then we'll ask the user to enter that short int:
printf("enter the short int:"); scanf("%hd", &whole_number);
remember we use the modifier h after the % sign to specify that it's a short integer.
Now we'll ask the user to enter the next character, (chracter2):
printf("Enter the char2:"); scanf("%c", &character2);
And finally get them to enter that floating number:
printf("Enter the float:"); scanf("%f", ¬_whole_number);
now let's display back to the user the values that they entered:
printf ("the values entered were\r\n"); printf ("char1:%c, shortint:%hd, char2:%c, float:%f",character1, whole_number, character2, not_whole_number);
and finish our main function by closing the curly bracket:
}
The complete code should look like this:
#include<stdio .h>
int main()
{
short int whole_number;
char character1, character2;
float not_whole_number;
printf("enter char1:");
scanf("%c", &character1);
printf("enter the short int:");
scanf("%hd", &whole_number);
printf("Enter the char2:");
scanf("%c", &character2);
printf("Enter the float:");
scanf("%f", ¬_whole_number);
printf ("the values entered were\r\n");
printf ("char1:%c, shortint:%hd, char2:%c, float:%f",character1, whole_number, character2, not_whole_number);
}
Now you need to compile your program, and run it to make sure that it works.
D:\coding\lesson5>tcc lesson5.c
D:\coding\lesson5>lesson5.exe
enter char1:a
enter the short int:1
Enter the char2:
Enter the float:1.2
the values entered were char1:a, shortint:1, char2: , float:1.200000
D:\coding\lesson5>
Something strange happened there, we were able to enter the first character input, and the integer, but after that we were only able to enter the floating point number, and when displayed the program seemed to put a line break in unexpectedly.
Buffers
What happened there was no mistake, it looks a little weird, but what happened there was that when we entered that that short integer, we entered the number then hit enter.
so what we actually entered was
1\n
Now we told the program to look at the keyboard buffer, and read a short integer from it, so it took the one leaving \n in the keyboard buffer.
then we told the program to look for a char in the keyboard buffer, and it found \n and used that.
So the reason that the program didn't wait for us to enter a value, is because it already found one.
the reason that there is a line break in the output results is because that's what the character is (\n), so as I said, there is no mistake, the program is just doing what it was asked.
So how can we get data into that variable?
well, we can either just fill the keyboard buffer with what we want: (entering all the variables at once) and the scanf statements will just pick out the data that they are interested in.
D:\coding\lesson5>lesson5.exe enter char1:a4b6.7 enter the short int:Enter the char2:Enter the float:the values entered were char1:a, shortint:4, char2:b, float:6.700000
or, we can do it the proper way and flush the input buffer before reading it so that we know that it's empty and only getting what we want.
Flushing buffers
to flush the buffers in a program, (that is erase them all and make them ready to accept new input, we use the function;
flushall();
So all we need to do now is re-visit that code, and flush the buffers before reading inputs.
#include<stdio .h>
int main()
{
short int whole_number; char character1, character2;
float not_whole_number;
printf("enter char1:");
flushall();
scanf("%c", &character1); printf("enter the short int:");
flushall();
scanf("%hd", &whole_number); printf("Enter the char2:");
flushall();
scanf("%c", &character2); printf("Enter the float:");
flushall();
scanf("%f", ¬_whole_number);
printf ("the values entered were\r\n");
printf ("char1:%c, shortint:%hd, char2:%c, float:%f",character1, whole_number, character2, not_whole_number);
}
Making pretty outputs
Now, I can't help but feel that just putting data out on one line looks a little, well messy, it's nice for computers to read, but I'm not a computer.
So lets have a look at making our data outputting a little neater.
A few lessons ago we introduced the concept of displaying characters with their ASCII number rather than the data.
I put a link to the table found at http://www.asciitable.com/, we looked at how a character could be 97, which was a lower case a, and 98 was a b.
Well, now we're going to start using the characters, 185, 186, 200, 201, 202, 203, 204, 205 and 206... you'll see that these are double line corners, double horizontal lines (like =) double vertical lines, and junctions between these lines.
we'll start by deciding how wide we want our columns to be, I'm choosing 8 character places.
so we'll draw the top of our table:
printf ("the values entered were\r\n");
printf("%c", 201);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 203);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 203);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c\r\n", 187);
(Note that if you were a real sadist, all this could be done on a single line! but I feel that'd be a bit confusing).
Then we'll write out our column headers:
printf("%c", 186);
printf("Char 1 ");
printf("%c", 186);
printf("Shortint");
printf("%c", 186);
printf("Char 2 ");
printf("%c\r\n", 186);
notice I've used white space to pad out those titles to 8character lengths, (by white space I mean I pressed the space bar three times after writing float, (float has five characters, but to make it up to 8, I added 3 blank characters.)
Then we'll close the table cells,
printf("%c", 204);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 206);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 206);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c\r\n", 185);
and now we'll enter the variables:
printf("%c", 186);
printf("%8c", character1);
printf("%c", 186);
printf("%8hd", whole_number);
printf("%c", 186);
printf("%8c", character2);
printf("%c\r\n", 186);
Remember how we set the columns to be 8 characters wide, well, that's meant that we have to either pad out, or restrict variables.
So we've used %8c, to make the characters take up 8 spaces, we've made the short in take up 8 spaces by writing %8hd.
Then we close our table:
printf("%c", 200);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 202);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 202);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c\r\n", 188);
So the complete code for that bit is:
#include<stdio.h>
int main()
{
short int whole_number; char character1, character2;
printf("enter char1:");
flushall();
scanf("%c", &character1);
printf("enter the short int:");
flushall();
scanf("%hd", &whole_number);
printf("Enter the char2:");
flushall();
scanf("%c", &character2);
printf ("the values entered were\r\n");
printf("%c", 201);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 203);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 203);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c\r\n", 187);
printf("%c", 186);
printf("Char 1 ");
printf("%c", 186);
printf("Shortint");
printf("%c", 186);
printf("Char 2 ");
printf("%c\r\n", 186);
printf("%c", 204);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 206);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 206);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c\r\n", 185);
printf("%c", 186);
printf("%8c", character1);
printf("%c", 186);
printf("%8hd", whole_number);
printf("%c", 186);
printf("%8c", character2);
printf("%c\r\n", 186);
printf("%c", 200);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 202);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 202);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c\r\n", 188);
}
Yes, I have deliberately taken out the float type of variable.
Compile this and run it, and you should see the following:
Now we're going to take a look at all the other types that are available to us.
Other Variables
We did integers pretty much to death in the last lesson, looking at how to declare, then how to read them, how to over flow them.
Remember how I said that the scanf statement looked like the printf statement. and worked in much the same way too?
Well just like the printf statement you can specify the width of the number, you can add modifiers (long, short signed and unsigned) to the type...
For example
%9ld would take an input that was 9 characters in width, and was a long integer.
So lets go over getting variables into a program.
We'll of course start by making a program, (so create a new lesson 5 folder and a new source file etc).
Then start writing the source in the normal way:
#include<stdio.h> int main() {
and declare some variables of different types that we'll be using to store the data entered by users in.
short int whole_number; char character1, character2; float not_whole_number;
Now lets start asking the user of the program to enter data,
Again we'll start by asking the user to enter the first char that we defined:
printf("enter char1:"); scanf("%c", &character1);
Then we'll ask the user to enter that short int:
printf("enter the short int:"); scanf("%hd", &whole_number);
remember we use the modifier h after the % sign to specify that it's a short integer.
Now we'll ask the user to enter the next character, (chracter2):
printf("Enter the char2:"); scanf("%c", &character2);
And finally get them to enter that floating number:
printf("Enter the float:"); scanf("%f", ¬_whole_number);
now let's display back to the user the values that they entered:
printf ("the values entered were\r\n"); printf ("char1:%c, shortint:%hd, char2:%c, float:%f",character1, whole_number, character2, not_whole_number);
and finish our main function by closing the curly bracket:
}
The complete code should look like this:
#include<stdio .h>
int main()
{
short int whole_number;
char character1, character2;
float not_whole_number;
printf("enter char1:");
scanf("%c", &character1);
printf("enter the short int:");
scanf("%hd", &whole_number);
printf("Enter the char2:");
scanf("%c", &character2);
printf("Enter the float:");
scanf("%f", ¬_whole_number);
printf ("the values entered were\r\n");
printf ("char1:%c, shortint:%hd, char2:%c, float:%f",character1, whole_number, character2, not_whole_number);
}
Now you need to compile your program, and run it to make sure that it works.
D:\coding\lesson5>tcc lesson5.c
D:\coding\lesson5>lesson5.exe
enter char1:a
enter the short int:1
Enter the char2:
Enter the float:1.2
the values entered were char1:a, shortint:1, char2: , float:1.200000
D:\coding\lesson5>
Something strange happened there, we were able to enter the first character input, and the integer, but after that we were only able to enter the floating point number, and when displayed the program seemed to put a line break in unexpectedly.
Buffers
What happened there was no mistake, it looks a little weird, but what happened there was that when we entered that that short integer, we entered the number then hit enter.
so what we actually entered was
1\n
Now we told the program to look at the keyboard buffer, and read a short integer from it, so it took the one leaving \n in the keyboard buffer.
then we told the program to look for a char in the keyboard buffer, and it found \n and used that.
So the reason that the program didn't wait for us to enter a value, is because it already found one.
the reason that there is a line break in the output results is because that's what the character is (\n), so as I said, there is no mistake, the program is just doing what it was asked.
So how can we get data into that variable?
well, we can either just fill the keyboard buffer with what we want: (entering all the variables at once) and the scanf statements will just pick out the data that they are interested in.
D:\coding\lesson5>lesson5.exe enter char1:a4b6.7 enter the short int:Enter the char2:Enter the float:the values entered were char1:a, shortint:4, char2:b, float:6.700000
or, we can do it the proper way and flush the input buffer before reading it so that we know that it's empty and only getting what we want.
Flushing buffers
to flush the buffers in a program, (that is erase them all and make them ready to accept new input, we use the function;
flushall();
So all we need to do now is re-visit that code, and flush the buffers before reading inputs.
#include<stdio .h>
int main()
{
short int whole_number; char character1, character2;
float not_whole_number;
printf("enter char1:");
flushall();
scanf("%c", &character1); printf("enter the short int:");
flushall();
scanf("%hd", &whole_number); printf("Enter the char2:");
flushall();
scanf("%c", &character2); printf("Enter the float:");
flushall();
scanf("%f", ¬_whole_number);
printf ("the values entered were\r\n");
printf ("char1:%c, shortint:%hd, char2:%c, float:%f",character1, whole_number, character2, not_whole_number);
}
Making pretty outputs
Now, I can't help but feel that just putting data out on one line looks a little, well messy, it's nice for computers to read, but I'm not a computer.
So lets have a look at making our data outputting a little neater.
A few lessons ago we introduced the concept of displaying characters with their ASCII number rather than the data.
I put a link to the table found at http://www.asciitable.com/, we looked at how a character could be 97, which was a lower case a, and 98 was a b.
Well, now we're going to start using the characters, 185, 186, 200, 201, 202, 203, 204, 205 and 206... you'll see that these are double line corners, double horizontal lines (like =) double vertical lines, and junctions between these lines.
we'll start by deciding how wide we want our columns to be, I'm choosing 8 character places.
so we'll draw the top of our table:
printf ("the values entered were\r\n");
printf("%c", 201);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 203);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 203);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c\r\n", 187);
(Note that if you were a real sadist, all this could be done on a single line! but I feel that'd be a bit confusing).
Then we'll write out our column headers:
printf("%c", 186);
printf("Char 1 ");
printf("%c", 186);
printf("Shortint");
printf("%c", 186);
printf("Char 2 ");
printf("%c\r\n", 186);
notice I've used white space to pad out those titles to 8character lengths, (by white space I mean I pressed the space bar three times after writing float, (float has five characters, but to make it up to 8, I added 3 blank characters.)
Then we'll close the table cells,
printf("%c", 204);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 206);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 206);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c\r\n", 185);
and now we'll enter the variables:
printf("%c", 186);
printf("%8c", character1);
printf("%c", 186);
printf("%8hd", whole_number);
printf("%c", 186);
printf("%8c", character2);
printf("%c\r\n", 186);
Remember how we set the columns to be 8 characters wide, well, that's meant that we have to either pad out, or restrict variables.
So we've used %8c, to make the characters take up 8 spaces, we've made the short in take up 8 spaces by writing %8hd.
Then we close our table:
printf("%c", 200);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 202);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 202);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c\r\n", 188);
So the complete code for that bit is:
#include<stdio.h>
int main()
{
short int whole_number; char character1, character2;
printf("enter char1:");
flushall();
scanf("%c", &character1);
printf("enter the short int:");
flushall();
scanf("%hd", &whole_number);
printf("Enter the char2:");
flushall();
scanf("%c", &character2);
printf ("the values entered were\r\n");
printf("%c", 201);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 203);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 203);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c\r\n", 187);
printf("%c", 186);
printf("Char 1 ");
printf("%c", 186);
printf("Shortint");
printf("%c", 186);
printf("Char 2 ");
printf("%c\r\n", 186);
printf("%c", 204);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 206);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 206);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c\r\n", 185);
printf("%c", 186);
printf("%8c", character1);
printf("%c", 186);
printf("%8hd", whole_number);
printf("%c", 186);
printf("%8c", character2);
printf("%c\r\n", 186);
printf("%c", 200);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 202);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c", 202);
printf("%c%c%c%c%c%c%c%c", 205,205,205,205,205,205,205,205);
printf("%c\r\n", 188);
}
Yes, I have deliberately taken out the float type of variable.
Compile this and run it, and you should see the following:
D:\coding\lesson5>tcc lesson5.c D:\coding\lesson5>lesson5.exe enter char1:1 enter the short int:s Enter the char2:1 the values entered were ╔════════╦════════╦════════╗ ║Char 1 ║Shortint║Char 2 ║ ╠════════╬════════╬════════╣ ║ 1║ 64║ 1║ ╚════════╩════════╩════════╝
Thursday, September 01, 2011
Coding Lessons: C and inputs (Lesson 4)
To start with we're only going to deal with fixed formatted inputs.
That is to say that we'll be assigning a variable type, (an integer for example), and we expect a user to be entering an integer.
We are leaving the system open to people inputting strings or floats or characters, and just trusting that they will enter a number.
Of course, if you were writing professional software, or software for anyone but yourself you'd be allowing input of strings, and then checking that this strings were actually numbers, and then converting them into numbers before allowing them to be treated as numbers.
To start, do the same as for any lesson, go into your coding folder and create a new folder called Lesson4, and open your editor.
you will of course be including the header file that we use to get either inputs or outputs from the console
#include <stdio.h>
And you'll be starting your main routine in the program.
int main()
{
As I said, we'll deal with numbers first, so lets declare a couple of integer variables:
int x, y;
I mentioned in an earlier lesson that you could declare two variables on the same line, that you just separated them by a comma. Well, you just did it!
now you need to tell the user of your program that you're waiting for an input, if you don't tell the user, then how are they going to know to do anything?
So print a message to the screen telling them to enter a number for the variable.
printf("enter a number for X:");
And now you need to actually get the variable.
To do this you use a function called scanf, scanf works pretty much the same as printf, but instead of printing to the terminal, you're scanning from the terminal.
The format of the function looks incredibly similar to the printf function, the same place holder for the variable types are used, but, you cannot put text into the statement, and instead of just writing the variable at the end, we have to prefix the variable name with an ampersand, like this.
scanf("%d", &x);
We tell the user to intput a value, and read that value for y from the keyboard in the same way.
Then we can display our variables, just like we did in earlier lessons.
printf("x=%d, y=%d", x, y);
You can now close your main routine and compile and run your program.
D:\coding\lesson4>tcc lesson4.c
D:\coding\lesson4>lesson4.exe
enter a number for X:99
Enter a number for Y:1
x=99, y=1
D:\coding\lesson4>
Of course, just getting and displaying inputs is a little bit boring, so lets look at some simple functions that we can do with numbers.
We can add them together.
If we'd declared another variable (Z?) then we could write;
z = x+y;
Then the variable z, would have the sum of variables x and y in it.
but we learned in our last lesson that we don't necessarily have to have a variable to display data.
we can just do the maths inside the printf statement.
printf("\r\n%d + %d = %d", x, y, x+y);
I'd recommend some caution here though, anything more complicated than simple maths functions are going to get a little bit difficult to work through, what I mean is that if they are in the end of a printf statement it's going to look messy, be difficult to read, and difficult to debug.
Anyway, add that line of code at the end of your program, and re-compile. and run again.
Testing
D:\coding\lesson4>tcc lesson4.c
D:\coding\lesson4>lesson4.exe
enter a number for X:99
Enter a number for Y:1
x=99, y=1
99 + 1 = 100
D:\coding\lesson4>
The numbers are input correctly, and the sum is calculated correctly.
Lets see if we can trip this program up, and expose some limitations.
D:\coding\lesson4>lesson4.exe
enter a number for X:-8
Enter a number for Y:4
x=-8, y=4
-8 + 4 = -4
D:\coding\lesson4>
It deals perfectly well with negative numbers, remember, integers are signed by default. so lets try something else.
D:\coding\lesson4>lesson4.exe
enter a number for X:1
Enter a number for Y:a
x=1, y=1245064
1 + 1245064 = 1245065
D:\coding\lesson4>
a, is not a number. the program doesn't crash, but the results are meaningless. Imagine if you were writing a program for a financial company, a simple mistake of hitting the wrong key could be disastrous!
Overflow error
Lets be a little less obvious about how to break this program now.
D:\coding\lesson4>lesson4.exe
enter a number for X:1
Enter a number for Y:2147483647
x=1, y=2147483647
1 + 2147483647 = -2147483648
D:\coding\lesson4>
How did that happen?!
If you remember in the first lesson on variables we talked about all the variable types, signed and unsigned, and we talked about how numbers were expressed in binary.
how a signed 4 bit number could count from -8 to +7
0111 = 7
1000 - -8
Well this happens with all signed numbers,
In this case the number has 32 bits, and all that happened was the variable was:
0111 1111 1111 1111 1111 1111 1111 1111
We added 1 to that, so it became.
1000 0000 0000 0000 0000 0000 0000 0000
If that were an unsigned number 2,147,483,647 would have become 2,147,483,648.
but because it is signed, (and has that signed bit) instead it became -2,147,483,648.
We've forced the computer to deal with a number larger than a 32bit integer, with consequences that we could expect, but could not deal with inside the program, because they were too big for the program to deal with!
Consider this:
D:\coding\lesson4>lesson4.exe
enter a number for X:214748364970
Enter a number for Y:214748364970
x=170, y=170
170 + 170 = 340
D:\coding\lesson4>
The numbers are nothing like what we entered, what we've started playing with is forcing the program to deal with numbers that it's just not equipped to. This is an overflow error.
(because the bits have overflowed out of the space that this variable can deal with), sometimes this just doesn't matter, (this program that we've made doesn't complain).
Other times it does matter
The complete code is here:
#include
int main()
{
int x, y;
printf("enter a number for X:");
scanf("%d", &x);
printf("Enter a number for Y:");
scanf("%d", &y);
printf("x=%d, y=%d", x, y);
printf("\r\n%d + %d = %d", x, y, x+y);
}
That is to say that we'll be assigning a variable type, (an integer for example), and we expect a user to be entering an integer.
We are leaving the system open to people inputting strings or floats or characters, and just trusting that they will enter a number.
Of course, if you were writing professional software, or software for anyone but yourself you'd be allowing input of strings, and then checking that this strings were actually numbers, and then converting them into numbers before allowing them to be treated as numbers.
To start, do the same as for any lesson, go into your coding folder and create a new folder called Lesson4, and open your editor.
you will of course be including the header file that we use to get either inputs or outputs from the console
#include <stdio.h>
And you'll be starting your main routine in the program.
int main()
{
As I said, we'll deal with numbers first, so lets declare a couple of integer variables:
int x, y;
I mentioned in an earlier lesson that you could declare two variables on the same line, that you just separated them by a comma. Well, you just did it!
now you need to tell the user of your program that you're waiting for an input, if you don't tell the user, then how are they going to know to do anything?
So print a message to the screen telling them to enter a number for the variable.
printf("enter a number for X:");
And now you need to actually get the variable.
To do this you use a function called scanf, scanf works pretty much the same as printf, but instead of printing to the terminal, you're scanning from the terminal.
The format of the function looks incredibly similar to the printf function, the same place holder for the variable types are used, but, you cannot put text into the statement, and instead of just writing the variable at the end, we have to prefix the variable name with an ampersand, like this.
scanf("%d", &x);
We tell the user to intput a value, and read that value for y from the keyboard in the same way.
Then we can display our variables, just like we did in earlier lessons.
printf("x=%d, y=%d", x, y);
You can now close your main routine and compile and run your program.
D:\coding\lesson4>tcc lesson4.c
D:\coding\lesson4>lesson4.exe
enter a number for X:99
Enter a number for Y:1
x=99, y=1
D:\coding\lesson4>
Of course, just getting and displaying inputs is a little bit boring, so lets look at some simple functions that we can do with numbers.
We can add them together.
If we'd declared another variable (Z?) then we could write;
z = x+y;
Then the variable z, would have the sum of variables x and y in it.
but we learned in our last lesson that we don't necessarily have to have a variable to display data.
we can just do the maths inside the printf statement.
printf("\r\n%d + %d = %d", x, y, x+y);
I'd recommend some caution here though, anything more complicated than simple maths functions are going to get a little bit difficult to work through, what I mean is that if they are in the end of a printf statement it's going to look messy, be difficult to read, and difficult to debug.
Anyway, add that line of code at the end of your program, and re-compile. and run again.
Testing
D:\coding\lesson4>tcc lesson4.c
D:\coding\lesson4>lesson4.exe
enter a number for X:99
Enter a number for Y:1
x=99, y=1
99 + 1 = 100
D:\coding\lesson4>
The numbers are input correctly, and the sum is calculated correctly.
Lets see if we can trip this program up, and expose some limitations.
D:\coding\lesson4>lesson4.exe
enter a number for X:-8
Enter a number for Y:4
x=-8, y=4
-8 + 4 = -4
D:\coding\lesson4>
It deals perfectly well with negative numbers, remember, integers are signed by default. so lets try something else.
D:\coding\lesson4>lesson4.exe
enter a number for X:1
Enter a number for Y:a
x=1, y=1245064
1 + 1245064 = 1245065
D:\coding\lesson4>
a, is not a number. the program doesn't crash, but the results are meaningless. Imagine if you were writing a program for a financial company, a simple mistake of hitting the wrong key could be disastrous!
Overflow error
Lets be a little less obvious about how to break this program now.
D:\coding\lesson4>lesson4.exe
enter a number for X:1
Enter a number for Y:2147483647
x=1, y=2147483647
1 + 2147483647 = -2147483648
D:\coding\lesson4>
How did that happen?!
If you remember in the first lesson on variables we talked about all the variable types, signed and unsigned, and we talked about how numbers were expressed in binary.
how a signed 4 bit number could count from -8 to +7
0111 = 7
1000 - -8
Well this happens with all signed numbers,
In this case the number has 32 bits, and all that happened was the variable was:
0111 1111 1111 1111 1111 1111 1111 1111
We added 1 to that, so it became.
1000 0000 0000 0000 0000 0000 0000 0000
If that were an unsigned number 2,147,483,647 would have become 2,147,483,648.
but because it is signed, (and has that signed bit) instead it became -2,147,483,648.
We've forced the computer to deal with a number larger than a 32bit integer, with consequences that we could expect, but could not deal with inside the program, because they were too big for the program to deal with!
Consider this:
D:\coding\lesson4>lesson4.exe
enter a number for X:214748364970
Enter a number for Y:214748364970
x=170, y=170
170 + 170 = 340
D:\coding\lesson4>
The numbers are nothing like what we entered, what we've started playing with is forcing the program to deal with numbers that it's just not equipped to. This is an overflow error.
(because the bits have overflowed out of the space that this variable can deal with), sometimes this just doesn't matter, (this program that we've made doesn't complain).
Other times it does matter
The complete code is here:
#include
int main()
{
int x, y;
printf("enter a number for X:");
scanf("%d", &x);
printf("Enter a number for Y:");
scanf("%d", &y);
printf("x=%d, y=%d", x, y);
printf("\r\n%d + %d = %d", x, y, x+y);
}
Tuesday, August 30, 2011
Coding Lessons: C and more variables (lesson 3)
So far we've covered that there is a way to print stuff to the screen.
And that you can set up variables, and that there are different types of variable for different types of data. We declared a variable, gave it a value, and wrote that value to the screen.
But so far we only dealt with 1 data type, (because I didn't want to make the lessons too long).
We know how to declare lots of data types, now lets look at using a few more data types
Start
As before set up your workspace (create a new folder d:\coding\lesson3) and open your editor.
Just as before add any comments that you like, and put that header file in that'll let us deal with outputting data to the screen
#include <stdio.h>
And start your main function
int main()
{
This time I'm not going to declare the variables, of course you can if you want to, but what I'm going to do this time is use the variable place holders in the printf statement, then assign them hard coded values at the end.
Now, lets look at characters, (ones that you'd assign to the char variable type).
printf ("looking at characters: %c %c \n\r", 'a', 98);
there are going to be two characters printed on that line (there are two %c's) looking at the end, you can see that one of those characters is going to be a, and the other is going to be 97?
97 isn't a character!, but it is a place on an ASCII Character map!
If you look up the ASCII character values you'll see that this corresponds to the letter b.
the first 127 characters I'd pretty much agree, there is not a lot of point in printing them, but the extended ASCII, that's where it gets more interesting, these don't appear on the keyboard, but you can use them by just writing the number value.
http://www.asciitable.com/
The other thing that was introduced there was \n (new line) and \r (carriage return), and that's because if we just have two statements;
printf("some text");
printf("some more text");
They will appear on the same line as "some textsome more text"
now let's look at some integers (like we used in the first example).
printf ("integers: %d %ld\n\r", 1234, 650890L);
What we've done there is looked at a %d, (signed integer), and also looked at a much bigger number using a long integer %ld, you'll see that at the end of the large number there is a big L that specifies that it's a long.
So we're really getting into the hang of things now!
So let's look at some special formatting,
Say I'm writing a table, and I need to preserve the formatting, it should be easy right, I can just use spaces to pad out the cells of my output? (but how do I know how many spaces to put?) the variable changes? how do I know I'll need 8 blank spaces, what if I need 9, or 7?
Well, there is a way to tell the printf statement to print out that integer, and pad the space with blank spaces.
printf ("Padding with blanks: %10d \n\r", 123);
that 10 in front of the d in the %d place holder tells the program that that data takes up ten spaces. (so seven blank spaces are put into the front of the number 123)
If I wanted data to be displayed in a set number of characters, i could just as easily pad out the blank space with zeros instead I'd do that like this:
printf ("Padding with zeros: %010d \n\r", 1234);
I said at the start that these lessons would be geared towards programming micro controllers, what if I want to get a number to or from the chip, that number is a representation of 8 bits, I don't write 0111 1111 for off on on on on on on on whichis what I want my 8 output pins to read. I want to represent that in as simpler way as possible in my code, I want to use hex. where I can just write 7f, which is a little easier than working back to the base ten number of 127.
printf ("Using different bases: Decimal %d unsigned hex %x unsigned Octal %o formatted Hex %#x Formatted Octal %#o \n\r", 127, 127, 127, 127, 127);
But now what about floating point numbers, dealing with floats is pretty important, just about everything in this world can be expressed as a floating point number.
if I say to you that my chip measures the temperature of 0x4f it means nothing. but if there is the correct maths on my chip to write 23.5 degrees now that's much better!
printf ("floating point numbers: %10.5f %010.5f %2.2f %E \n\r", 23.517, 23.517, 23.517, 3344.1416);
I'm re-using a couple of the tricks from above. the first one %10.5f basically says, take up ten character spaces, use blank space at the start, have five places after the decimal point.
So 23.517 is displayed as [space][space][space]23.51700
The next example uses the same trick to write zeros instead of blank spaces (%010.5f)
so 23.517 appear as 00023.51700
The next is quite useful, I'm not adding any place holders, or extra information, I'm restricting the accuracy.
%2.2f means 2 places before the decimal place, 2 places afterwards.
so 23.517 gets neatly trimmed to 23.51, I gave the example earlier of a temperature probe, you probably don't need to worry about the thousandth of a degree!
And %E means display in scientific format.
so 3344.1416 becomes 3.3441416 x10(3)
That's floats pretty much covered, so lets move on.
I said earlier about setting the width of text by padding with zeros or blank spaces,
but, there is another way.
printf ("setting the width: %*d \n\r", 5, 14);
If you put a star after the % sign, you can then specify the width before the value.
And last but not least, because I know that you'll want to work with strings of characters so that you can send messages!
printf ("%s \n\r", "A silly message goes here");
%s is the place holder for strings, strings can contain numbers, (as in you can write)
printf("%s \n\r", "123");
But you cannot perform maths functions on strings!
Put it all together
The complete example is here:
#include<stdio.h>
int main()
{
printf ("looking at characters: %c %c \n\r", 'a', 98);
printf ("integers: %d %ld\n\r", 1234, 650890L);
printf ("Padding with blanks: %10d \n\r", 123);
printf ("Padding with zeros: %010d \n\r", 1234);
printf ("Using different bases: Dec %d unsigned hex %x unsigned Octal %o formatted Hex %#x Formatted Octal %#o \n\r", 127, 127, 127, 127, 127);
printf ("floating point numbers: %10.5f %010.5f %2.2f %E \n\r", 23.517, 123.517, 23.517, 3344.1416);
printf ("setting the width: %*d \n\r", 5, 14);
printf ("%s \n\r", "A silly message goes here");
}
so you need to save that in D:\coding\lesson3\
as variables.c
then navigate to that directory and type "tcc variables.c" hit return,
And that you can set up variables, and that there are different types of variable for different types of data. We declared a variable, gave it a value, and wrote that value to the screen.
But so far we only dealt with 1 data type, (because I didn't want to make the lessons too long).
We know how to declare lots of data types, now lets look at using a few more data types
Start
As before set up your workspace (create a new folder d:\coding\lesson3) and open your editor.
Just as before add any comments that you like, and put that header file in that'll let us deal with outputting data to the screen
#include <stdio.h>
And start your main function
int main()
{
This time I'm not going to declare the variables, of course you can if you want to, but what I'm going to do this time is use the variable place holders in the printf statement, then assign them hard coded values at the end.
Now, lets look at characters, (ones that you'd assign to the char variable type).
printf ("looking at characters: %c %c \n\r", 'a', 98);
there are going to be two characters printed on that line (there are two %c's) looking at the end, you can see that one of those characters is going to be a, and the other is going to be 97?
97 isn't a character!, but it is a place on an ASCII Character map!
If you look up the ASCII character values you'll see that this corresponds to the letter b.
the first 127 characters I'd pretty much agree, there is not a lot of point in printing them, but the extended ASCII, that's where it gets more interesting, these don't appear on the keyboard, but you can use them by just writing the number value.
http://www.asciitable.com/
The other thing that was introduced there was \n (new line) and \r (carriage return), and that's because if we just have two statements;
printf("some text");
printf("some more text");
They will appear on the same line as "some textsome more text"
now let's look at some integers (like we used in the first example).
printf ("integers: %d %ld\n\r", 1234, 650890L);
What we've done there is looked at a %d, (signed integer), and also looked at a much bigger number using a long integer %ld, you'll see that at the end of the large number there is a big L that specifies that it's a long.
So we're really getting into the hang of things now!
So let's look at some special formatting,
Say I'm writing a table, and I need to preserve the formatting, it should be easy right, I can just use spaces to pad out the cells of my output? (but how do I know how many spaces to put?) the variable changes? how do I know I'll need 8 blank spaces, what if I need 9, or 7?
Well, there is a way to tell the printf statement to print out that integer, and pad the space with blank spaces.
printf ("Padding with blanks: %10d \n\r", 123);
that 10 in front of the d in the %d place holder tells the program that that data takes up ten spaces. (so seven blank spaces are put into the front of the number 123)
If I wanted data to be displayed in a set number of characters, i could just as easily pad out the blank space with zeros instead I'd do that like this:
printf ("Padding with zeros: %010d \n\r", 1234);
I said at the start that these lessons would be geared towards programming micro controllers, what if I want to get a number to or from the chip, that number is a representation of 8 bits, I don't write 0111 1111 for off on on on on on on on whichis what I want my 8 output pins to read. I want to represent that in as simpler way as possible in my code, I want to use hex. where I can just write 7f, which is a little easier than working back to the base ten number of 127.
printf ("Using different bases: Decimal %d unsigned hex %x unsigned Octal %o formatted Hex %#x Formatted Octal %#o \n\r", 127, 127, 127, 127, 127);
But now what about floating point numbers, dealing with floats is pretty important, just about everything in this world can be expressed as a floating point number.
if I say to you that my chip measures the temperature of 0x4f it means nothing. but if there is the correct maths on my chip to write 23.5 degrees now that's much better!
printf ("floating point numbers: %10.5f %010.5f %2.2f %E \n\r", 23.517, 23.517, 23.517, 3344.1416);
I'm re-using a couple of the tricks from above. the first one %10.5f basically says, take up ten character spaces, use blank space at the start, have five places after the decimal point.
So 23.517 is displayed as [space][space][space]23.51700
The next example uses the same trick to write zeros instead of blank spaces (%010.5f)
so 23.517 appear as 00023.51700
The next is quite useful, I'm not adding any place holders, or extra information, I'm restricting the accuracy.
%2.2f means 2 places before the decimal place, 2 places afterwards.
so 23.517 gets neatly trimmed to 23.51, I gave the example earlier of a temperature probe, you probably don't need to worry about the thousandth of a degree!
And %E means display in scientific format.
so 3344.1416 becomes 3.3441416 x10(3)
That's floats pretty much covered, so lets move on.
I said earlier about setting the width of text by padding with zeros or blank spaces,
but, there is another way.
printf ("setting the width: %*d \n\r", 5, 14);
If you put a star after the % sign, you can then specify the width before the value.
And last but not least, because I know that you'll want to work with strings of characters so that you can send messages!
printf ("%s \n\r", "A silly message goes here");
%s is the place holder for strings, strings can contain numbers, (as in you can write)
printf("%s \n\r", "123");
But you cannot perform maths functions on strings!
Put it all together
The complete example is here:
#include<stdio.h>
int main()
{
printf ("looking at characters: %c %c \n\r", 'a', 98);
printf ("integers: %d %ld\n\r", 1234, 650890L);
printf ("Padding with blanks: %10d \n\r", 123);
printf ("Padding with zeros: %010d \n\r", 1234);
printf ("Using different bases: Dec %d unsigned hex %x unsigned Octal %o formatted Hex %#x Formatted Octal %#o \n\r", 127, 127, 127, 127, 127);
printf ("floating point numbers: %10.5f %010.5f %2.2f %E \n\r", 23.517, 123.517, 23.517, 3344.1416);
printf ("setting the width: %*d \n\r", 5, 14);
printf ("%s \n\r", "A silly message goes here");
}
so you need to save that in D:\coding\lesson3\
as variables.c
then navigate to that directory and type "tcc variables.c" hit return,
Subscribe to:
Posts (Atom)