(<--Previous) Back to Index (Next-->)

FB n00b: Tutorial 4

Note: This tutorial is for absolute beginners. If you aren't an absolute beginner and you dislike the tutorial, that's too bad. If you are an absolute beginner and there's something you don't understand, read through the tutorial a second time. If you STILL have difficulties, e-mail me (TheMysteriousStrangerFromMars@yahoo.com) and I'll try to help you out. I do assume you at least have a decent knowledge of how to use your computer - if not, there's not much I can do for you. By the way, you'll need FreeBasic to use this tutorial - you download that at freebasic.net, so be sure to download and install it before doing this tutorial. Now start reading!

Variable types

Last time we talked about the various ways to loop - that is, to do things more than once without typing the same line of code again and again. Today, as promised, we talk about different kinds of variables. I've told you that all variables are really just numbers inside the computer - but that when you use an HLL, you get to pretend they are different things. One of those things was the string, which acts like a bunch of characters instead of numbers, and then there are integers and floating point numbers, which just act like numbers. I'm going to tell you more about these variable types today.

The most basic variable type is the integer type. There are actually three kinds of integer variable: the Byte, the Short, and the Integer. All three are integers, but the Byte only has 8 bits, the Short has 16, and the Integer has 32. A bit is a single binary digit, a 1 or a 0, and every number in your computer is made out of bits. I'm going to give you a couple of simple formulae that you should remember if you can; you don't need to know how they work, you just need to know that they work. The first is

c = (2b), where b is the number of bits in a variable and c is the number of individual bit combinations possible by a variable with that many bits
What is this telling us? It's telling us how many unique data items can be represented by a variable with the given number of bits. For example, an 8-bit number (a Byte, that is) can hold 28, or 256, unique numbers: 1, 10, 11, 100, 101, 110, 111, 1000, 1001... etc. Now normally our integer variables are signed, meaning they can have negative or positive numbers. The formula to calculate the range of numbers a signed variable may hold is:
l = -0.5*(2b) u = 0.5*(2b)-1, where l is the lowest number, u is the highest, and b is the number of bits.
That's how you figure that out... but what if you have a number that doesn't need to have negative values? Well you can make an Unsigned variable by putting a u in front of the name - so you have uByte, uShort, and uInteger. Now of course, the range here is different. In this case, l is always 0 and u is (2b)-1. Now in case you don't want to memorize all these formulas, it might be easier just to memorize the powers of two of the three types:
Byte (8 bits)Short (16 bits)Integer (32 bits)
28 = 256216 = 65536232 = 4294967296
The last one even I don't know by heart - just remember that it's about 4 billion. For unsigned numbers, you have to subtract 1 since 0 is included. For signed numbers you have to divide by two to get the absolute value of the lowest value, and subtract one from that to get the highest value. It is entirely possible to store several related pieces of information in a single variable - for example, if you need an on/off switch for several things, each one could be a single bit and all the bits could be in the same variable. This would save space, and there are ways of detecting each bit, whether it's on or off. We won't be discussing that today, though, but keep it in mind anyways.

Now you know about the different kinds of integer... what about floating point numbers? Floating point numbers are actually quite complicated and I won't try to explain to you just how they work. Suffice to say there are times when they will do things you won't expect and you should remember this is never a bug in the compiler, it's almost always a quirk of floating point arithmetic. I will give you a bit of a hint, but I won't describe or explain. If you remember scientific notation, floating point numbers work very much in a similar way (x * 10^n) except that the exponent is on 2 instead of 10 (since it's binary).

There are two kinds of floating point number: Single and Double. If you look in the FreeBasic manual you'll see the ranges of these. Now because of the way they are stored, floating point numbers can be very precise: that is, you can have the number 0.000000001, or something like that, and you have to admit that's pretty precise. You can also have extremely huge numbers, since all you have to do is change the exponent on the 2. This seems really great, but there is a "However" to the situation: floating point numbers are very precise, but there is a sacrifice of accuracy. That is, you can specify numbers very precisely, but when you do a mathematical calculation with them the result might not be perfectly correct. Yes, you can store 0.000000001 with great precision, but when for example you try to round 0.0099 to one decimal, you might not get 0.01! This may seem odd but it's just a fact of the way floating point numbers are stored, so you need to understand this.

As the name suggests, Double is more precise than Single. Of course, Double also takes up more memory. The floating point processor (a part of your CPU in your computer) does calculations with 80-bit floating point numbers internally, but in fact your Single is only 32 bits and your Double is 64. From the integer formulae you can easily figure out how many distinct values the Single or Double may hold... yes, it's a lot. However, the values they hold are not necessarily integer values, so the actual range of these is best found by looking in the FB manual ;) The floating point numbers may be manipulated and used in calculations just the same as regular numbers - with all the same operators like +, -, *, /, =, etc. You can also use them in conditionals and loops just the same.

Before we move on, I need to talk about Printing numbers on the screen. Normally if you ask FreeBasic to "Print a" where a is a number (and not a string) it will work just fine because FreeBasic will automatically convert a into a string which can be printed. However, if you want to print two things together, for example

  Print "Hello" + a
This only works if a is a string. The reason is because "+" is used for two different things: with numbers, it's used to add the numbers together. With strings, it's used to put them together into one string. If a is a number in the above example, FreeBasic isn't sure what to do since there's a number and a string. There are two solutions. One of them is to do this:
  Print "Hello "; a
But the more correct method is to do this:
  Print "Hello " + Str(a)
Str() is something that converts a number into a string. I can't explain precisely how it works here (you will find out in tutorial #7!), just remember that's what you need to do to print strings and numbers together. You can also use the "&" operator to do the same thing, as in
  Print "Hello " & a
And that will do the trick for you properly as well. Of course, if you do this:
  Print "Hello "
  Print a
It will compile, but the number will be printed on a separate line from "Hello ". Of course, if you put a ";" at the end of the first Print line, then it will be fine. Print is kind of odd this way; it only prints things on the same line if you end with a ";". Anyways, now you know that, let's move on.

Next we come to the string variable type. Strings are, very simply, a bunch of characters. These can be letters, numbers, symbols, or anything else. Now in the early days it was enough to store a character using 8 bits. A 7-bit code called ASCII was developed for doing this, and of course if you use 8 bits to store it you may add 128 extra characters to the set (since each time you add 1 bit you double the amount of values it can hold). Normally, all strings are stored with ASCII even now, meaning each character in your string takes up 1 byte. However, in Windows a new 16-bit code called Unicode is sometimes used. Because it holds much more, it can hold all kinds of special characters, including many common Chinese and Japanese characters (of which there are millions). Unicode strings are treated specially by FreeBasic, so you'll have to look at the manual for information about those.

Most strings in FreeBasic are stored in one of two ways. There is the FB internal string format, which puts a few bytes of information in front of the string, such as what the length of the string is - after all, you need to know how long the string is or if you try to print it you'll print a bunch of other characters outside of the string on the screen! The other way is the method used in C, which is the ASCIIZ string. ASCIIZ strings don't have any bytes in the beginning of them - the first byte in such a string is the first character in the string. So how do you know where the string is? The last byte of the string is 0, or NULL. Hence the name ASCIIZ or simply "ZString" which is what you use to declare them in FreeBasic. Normally, though, you'd just use a regular string. Of course, if your string length is always the same you declare it with the "* n" where n is the number of characters the string will always have - we discussed that earlier. For many strings, however, the length is variable, meaning it can change. If you want to know the length of a string, use Len(thestring) where thestring is the name of the string you want to know the length of.

You might think those are the only kinds of variables... well, you would be wrong. Actually, all other variables are really just variations of the first kinds, though it's possible to create your own kind of variable based on the integer types (but then you'd have to write special code to interpret it specially, since it would not be a normal integer). The first special type of variable is the array. This allows you to create several of the same type of variable, all under the same name. For example, you might have a list of twenty names. Rather than creating twenty different strings, create a string array with twenty members:

  Dim As String names_list(20)
Now if you want to access the first name, you would access it with names_list(1) - the second name is accessed with names_list(2), and so on. Each one is a separate string, but they all have the same name and you simply select the one you want with a number. This becomes very useful with loops, since you can use a loop to print all of the names on the screen:
  For i = 1 to 20
    Print names_list(i)
  Next i
If you want to have a different number of items in your array, you must use ReDim:
  Dim As Integer howmany, i
  ReDim As Integer myarray(1)

  Print "How many array items do you want?"
  Input howmany

  ReDim myarray(howmany)

  Print "Now there are " & howmany & " items in myarray."

By the way, normally the first item in the array is 1, but you can make it something else. Another way of declaring an array is like this:
  Dim As Integer myarray(lb to ub)
Where lb is the lowest member of the array and ub is the highest.

One really fascinating possibility with arrays is that you can have more than one dimension. Take, as an example, the screen. Each row of the screen can be considered an array of colours (and colours are generally stored in integers, so it would be an array of integers with as many members as there are pixels across the screen), but what about up and down? You could represent your screen as a two-dimensional array, like this:

  Dim As Integer myscreen(1024, 768)
And then access the pixel at position x, y with myscreen(x, y). Of course, you can have even more than two dimensions, so arrays are very useful for many things. Also, if you remember matrices from math (if not, we'll be discussing them later... a lot later) - those can also be stored in a two-dimensional array.

Later we'll discuss pointers, which are often used similarly to arrays but are far more flexible. Right now, you aren't ready to learn about pointers so we'll go into the next item of business...

"User Defined Types" or UDTs. Generally, a UDT is simply a way to group several related variables under one structure. They aren't necessarily bitfields (the example I gave earlier) but just different variables grouped together. As an example, consider a ball moving around on the screen. You need several different numbers to keep track of the ball: the x and y coordinates of the ball, the velocity in the x and y direction, and perhaps some others such as the colour of the ball. Now you could declare each of these variables separately, but it's much more convenient to put them all together in a UDT - it becomes even less convenient to do it the other way if you have several balls bouncing around on the screen. What if you have a variable number of balls on the screen? You could create twenty x, y, velocity_x, and velocity_y components - or you could just create twenty variables of the type "object", after creating a UDT called "object" that has those components built in to it. Here's an example:

  Type object
    As Single x, y
    As Single vx, vy
    As Integer colour
  End Type

  Dim As object ball(10)
  Dim As object one_ball
What did we just do there? We created an array of objects, with 10 of them to be exact. In fact, you don't have to do it like an array, you could also just create one of them - but the point is made, this is a very powerful way to group related variables together. But how would we access each variable individually? This is something very important, so remember this: you use the "." operator. For example, to access the x component of the object we created called one_ball, you use "one_ball.x". To access the colour component of the 5th ball, we use "ball(5).colour". Isn't that incredibly easy? Interestingly enough, it's possible to use UDT's inside of other UDTs, like this:
  Type vector
    As Single x
    As Single y
  End Type

  Type object
    As vector position
    As vector velocity
    As Integer colour
  End Type

  Dim As object ball(10)
  Dim As object one_ball
Then you access them with one_ball.position.x or one_ball.velocity.x - or ball(5).position.x, or anything else, depending on what's declared and how.

I won't give you the bouncing ball example today, because you don't know how to do graphics yet, but I will give a simple example of a list of people:

  Type person
    first_name As String
    last_name As String
    middle_initial As String * 1
    age As Integer
    dob As String * 10
  End Type

  Dim As Integer npeople, i
  ReDim As person my_people(1)
  Dim tstr1 As String, tstr2 As String, tstr3 As String

  Print "How many people are in the company?"
  Input npeople

  ReDim my_people(npeople)

  For i = 1 to npeople
    Print "Person #" & i
    Print "First name?"
    Input my_people(i).first_name
    Print "Middle initial?"
    Input my_people(i).middle_initial
    Print "Last name?"
    Input my_people(i).last_name
    Print "Age?"
    Input my_people(i).age
    Print "Birth month?"
    Input tstr1
    Print "Birth date?"
    Input tstr2
    Print "Birth year?"
    Input tstr3
    'Add a 0 to the month or day if either is less than 10
    If Len(tstr1) = 1 Then tstr1 = "0" + tstr1
    If Len(tstr2) = 1 Then tstr2 = "0" + tstr1
    my_people(i).dob = tstr1 + "/" + tstr2 + "/" + tstr3
  Next i

    Print "Which employee ID number would you like to see?  Press 0 to exit."
    Input i

    If i = 0 Then
      Print "Press any key to exit..."
      Print "Name:  " + my_people(i).first_name + " " + my_people(i).middle_initial + ". " + my_people(i).last_name
      Print "Age:  " & my_people(i).age
      Print "DOB:  " + my_people(i).dob
      Print ""
    End If

  'This is an infinity loop!  However, there is a way to break out, if you enter 0.
Whoa! This is probably the longest program you've seen! But I promise you there isn't a single thing in here you haven't learned about yet - there are conditionals, strings, numbers, arrays, UDTs, loops, input and output - we even change the strings around, and there are both variable-length and static strings. Furthermore, we have a variable-sized array! This is one heck of a complicated program, but everything there is something you understand, or should understand. If not, read through it, run it a few times, try it out - figure out what everything is supposed to do. You can add comments to it if it helps you understand everything. Figure out what it does and how it works (tip: if you run it, don't put too many people in your company or you'll be typing information about people all day!) Once you know how it works, you'll be surprised at how simple it really is. (In perspective, this program only has 56 lines. Did you ever stop to realize that the FreeBasic compiler is itself a program? The FreeBasic compiler has some 200,000 lines - that's right, two hundred THOUSAND. Turns out this program ain't so complicated after all!)

Closing up

Whoa. I'll say it again, not because I'm really amazed about anything but because you just waded through 56 lines of code and you understand it all (in other words, you're the one who should be amazed)! (Incidentally, if you don't, go read through it again a few times. If you have to, look over all the past tutorials as well. It uses concepts learned in ALL the tutorials, so it might be worth looking through them again a few times to figure it out.)

So what's the assignment for today? The assignment for today is to write a program that lets you enter recipes. Each recipe has a name which may be no more than 30 characters long, a string describing the ingredients, and a string describing the instructions for making the recipe. Tip: to make a string appear on more than one line when you print it, use the following bit of code:

  somestring = "something here" + Chr(13) + "this appears on the next line"
This works because Chr(13) returns a string which is equivalent to a newline character.

Note: This was edited because I made a bit of a boo-boo: you can't put variable-sized arrays inside Types, so it would be impossible to implement a recipe with more than on ingredient where the ingredients are an array of UDTs. Sorry about that! Later you'll learn of pointers, with which it is possible to do this, but until then you'll just have to do it with strings. Another possibility is that you could use a fixed length array and ignore any elements that aren't used. The example here uses that, and if you don't feel like trying to write the program yourself, you can look at the example instead (it's only 20k, so it shouldn't take long to download even on a slow connection).

Whether you do the assignment or not, tutorial 5 will be coming pretty soon... we'll discuss procedural programming and modularization, and specifically how these things work in FreeBasic. Until then... enjoy yourself!

The fifth tutorial is here

(<--Previous) Back to Index (Next-->)