Alright, finally, we’re getting closer and
closer to programming. In the last video, we talked about the ROM and operating systems.
Now, then, in this video, we’ll be looking at Programming languages! We’ll start off
with the simple question of “What is a programming language?”, then we’ll talk about what
low-level and high-level means, we’ll go into compiling and interpreting, and then
we’ll discuss what programming language we’re going to be using in this series!
And, this is the last actual “theory” topic, in the next video we’ll look at Visual
Studio, and then we will be ready to start programming.
OK, then, let’s start off with the obvious question of: “What is a programming language?”
Well, there are lots of different ways of telling the computer how you want something
done, and so a programming language is a way that you tell the computer what you want it
to do. On the screen are three different programs,
written in three different programming languages, C#, Java and Python. Each of these programs
do exactly the same thing, but you can see that they are different. That’s because
each programming language has its own rules and its own way of doing things.
There are also two main types of programming languages, or I suppose 3 if you want to have
one in the middle – “low-level” and “high-level”. So, what does that mean?
Well, luckily, I have basically already explained it in the last video with an operating system.
Really low level programming languages are where you talk straight to the computer, so,
it’s kind of like there is no operating system, you are talking straight to the computer,
and, as I said in the video, even doing simple tasks requires a lot of work.
So, then, what are high-level languages? Well, of course, it really wasn’t ideal having
to do so much work yourself, and so, we started developing languages that are closer to English,
and easier to understand. Look at these two, one is a really low-level
language and one is a pretty high-level language, look at how much more understandable the higher-level
language is. You can just see by looking at it that it’s got the text “Hello world!”
in, and that it’s “writing” that somewhere, like, perhaps on the screen. Whereas, in the
other one you have no clue, you can see the text “Hello world!”, but it would take
a very long time to work out what it’s doing with that text.
In addition to being easier to read, high-level languages also tend to do quite a lot of the
work for you. So, for example, in many high-level languages, you can create objects, right?
Let’s say you have four objects. Now, the language itself will automatically put that
object in the correct place in RAM, and it will automatically delete that object from
RAM when you’re done with it, for you. There’s also one other major advantage to
high-level code. Remember when I said that different CPUs have different architectures.
Well, when you write low-level code, that code will be different for every architecture.
So, if you wanted to write code that runs on both a computer and a phone, you would
need to write the same thing twice for both x86 and ARM. Whereas, with a high-level language,
it will most likely work on both. There are, of course, disadvantages to using
higher-level languages, they tend to be considerably slower than writing it all low-level, because
they do a lot of extra stuff in order to help you write code easier. But, generally, nowadays
it really doesn’t make a difference, you won’t even notice it. There’s also the
disadvantage that some programming languages, kind of like the one we’re going to use,
rely a lot on the features provided by the operating system. In our case, “Windows”.
And, as you know, there are more operating systems than just Windows, which means that
in order to run our programs on other operating systems, like Linux, you essentially have
to run it in a special environment that pretends to be Windows.
OK, fine, so high-level languages have advantages and disadvantages, but there’s still one
major question: “How do computers understand high-level code?” The only code that computers
themselves understand is machine code, which is the lowest level you can get to, and that’s
just binary. Whereas, when you write high-level code, it’s just text. When we start writing
code, you have to understand that the code we’re writing is just text, there’s nothing
fancy about it, you could write a whole program in notepad, if you wanted.
So, how do we go from this text to actual binary code that the computer can understand
and run. And there are two methods of doing this. The first one is compiling. A compiler
is a program that will take all of your code, which is once again, just text and convert
it into actual runnable machine code for you. Then, it will pack it up in something like
an EXE file, and, there you go! You can just double-click on that EXE file and your program
is running. The second method is “interpreting”. So,
with interpreting, your program basically stays as text. It never actually gets converted
to machine code. So, we use a program, called an “interpreter”, which is lower level
than the language it’s interpreting, same goes for the compiler actually.
And what then happens is when you run the program the interpreter will basically, read
through the text, and then just do whatever the text says. So, if the code (or text) says
to write “Hello world!” to the screen, the interpreter will see that, and then the
interpreter will write “Hello world!” to the screen.
If the next line said to write onto the screen what “2 + 4” is, then the interpreter
will take 2 and add 4 to it, and then write that on the screen, hopefully you can see
what’s going on here. Basically, instead of converting the program into low-level code,
we get a lower-level program, the interpreter, to basically do whatever our code says for
us. So, if our code says to put “Hello world!” on the screen, the interpreter is going to
go ahead and do everything it needs to, and it will write “Hello world!” onto the
screen. Notice how at no point there did our code get converted to machine code. The interpreter
itself is probably machine code, otherwise how would it do all these things, but our
code doesn’t actually get “converted”, it just stays as text.
An interpreter, is a program that goes through your code and does whatever your code says
to do. Whereas, a compiler, will take all of your code, and convert it to machine code,
which the CPU, or more specifically the “Control Unit” can then go through and run.
And, there are advantages and disadvantages to both methods. The advantage of a compiler
is that its faster than interpreting, because your code is getting turned into machine code,
and it’s ran by the CPU itself, there’s nothing in-between the CPU and your code slowing
it down. However, interpreting also has some advantages,
one of them being that it can be more secure, you see, because your code never gets converted
into machine code, that means that everything your code tries to do will go through the
interpreter, and if the interpreter doesn’t like something you try to do, it technically
could just not let you do it. This means that if someone really wanted to, maybe for security
reasons, you could theoretically completely prevent an interpreted program from actually
modifying, or even looking at your computer’s files and whatever. And, actually, many mobile
apps are actually run like that, and you have to allow them to do these things, otherwise,
the interpreter would just block them. Oh, and, here’s another advantage for interpreting:
you know how earlier I said that high-level code can work on lots of different architectures?
Well, when you’re compiling, you have to manually compile the program for each architecture
you want to support, and you may be using lots of different compilers to do that. Whereas,
when your code is interpreted, your code can run anywhere, provided it has an interpreter
that is written for a specific architecture. There’s a programming language, called “Java”,
it was created because there was a bit problem with low-level code. If you wanted to have
a program that would work on lots and lots of different devices, you would have to either
have to rewrite the code a whole bunch of times for each architecture, or you would
have to compile it a whole bunch of times for different architectures. And, if a programming
language didn’t have a compiler for a specific architecture, then you’re out of luck.
But Java came with a different approach, this is essentially how Java is run (oh, and, by
the way Java wasn’t actually the first to do this but it was extremely popular). Java
came with the idea that, instead of converting it into low-level code for every single architecture,
you could have an interpreter. Now, this was actually a very good idea, especially back
in the time when there were a lot of different architectures. The idea is very simple, the
people behind Java make an interpreter for a specific architecture, let’s say x86.
And, there you go – that’s it! All Java code will run on x86 processors. Oh, look,
a new architecture called “ARM” came along. Let’s just make an interpreter for that
– and, there you go, now all Java code works on ARM processors. And so on.
The point is that as long as a language has an interpreter for a specific architecture,
your code in that language will run on it. And that is a major advantage, because programming
languages like Java are so popular that it would have hundreds, maybe thousands of interpreters
for all different architectures, and you don’t even have to do anything, it will just work.
So, what programming language are we going to be using? Well, it’s called “C#”,
and yes, that hashtag there is pronounced “sharp”, C#!
So, C# is a high-level language, we’re are not writing low-level code in this series,
maybe as like a bonus part at the end, but, really, we are not writing low-level code,
we’re going to keep things simple here. Now, C# is a bit funny when it comes to being
compiled and interpreted, since it’s kind of both. What happens is your C# code gets
compiled, but not to machine code, but to something called the “Intermediate Language”
(intermediate meaning that it’s sort of in-between high-level and low-level), and
it sticks that “IL” in an EXE file, and that IL then essentially gets interpreted
when you run the EXE file. I know, it’s really odd, but, obviously they did it so
that it can run on multiple architectures. But, just to keep things simple, let’s just
think of it as being compiled, even though it is kind of interpreted.
OK, let’s summarize all of this. A programming language is a way to tell the computer what
you want it to do. Higher-level programming languages are easier to write and do a large
majority of the work for you, whereas low-level programming languages require you to do a
lot more work. Now, CPUs only understand low-level code,
they don’t understand high-level code, it’s way too close to English for them to understand
it. So, how do CPUs understand high-level code?
Well, there are two methods of getting CPUs to run high-level code. The first one is compiling
it. This means you put the code (which is just text) through a compiler and it turns
that text into binary code that the CPU can read through, and, technically the Control
Unit is “interpreting” that machine code. But when we say “interpreter”, we usually
refer to a program, that’s been compiled, just like any other program, that reads through
our high-level code, or at least an in-between language of some kind – and that is the
other way of running high-level code. And, so, hopefully you now understand what
low-level code is about and what high-level code is all about, as well as compilers and
interpreters. You’ll hear me saying things like “compiling” and “low-level” quite
a lot throughout the series, and that’s what they all mean!
OK, next video we’ll start looking at Visual Studio, which is where we’ll be writing
all of our C# code. Bye!