This is always a discussion. C junkies say it's not, it's actually one of the easiest languages in the world, and C haters say it's junk. Well C junkies are a bit blinded by their love, but stating that C is really hard... there is still assembler, COBOL, LISP and a few more disaster languages out there.
C is a system language, and due to that it was set up to be as close to machine language as possible. A coding instructor I once had even refused to call C a programming language, but rather an advanced assembler. However assembler code will change by the tiniest CPU changes. Back when C was first developed it was considered a mortal shame when two different brand of computers had any similarities at all. The difference between BigEndian and LittleEndian, were the biggest issue to tackle. The Unix OS was supposed to be able to run on both. C merely had to be neutral on this front, so porting to different systems was possible with as little changes in the code as possible. You should therefore not believe any stories about C being the superior language, because it's not, and it was not even what it was supposed to be. Ritchie, the inventor of C himself, once wondered why C became so big.
Now in this article I will teach you some actual C and C++, but please note that I am not super good in these languages, and I'll oversimplefy a few things, and please note that I'll take the reader who never touched any kind of code before in mind.
The first program actually written in C was the famous program "Hello World", and that was also the first time Hello World saw the light. With this program I can already break a few things down about C.
#include <stdio.h>
int main(void) {
printf("Hello World\n");
return 0;
}
What is important to know about C is that it's a procedurial language. That means that all code must live inside functions, or that that is at least what you should focus most of all on. Due to that even the main program is a function. Functions are just small pieces of code, that can communicate in order to become one big program.
Well the first command in my program is that "#include" instruction. This is what we call a "compiler directive". It's officially no code. C has a built-in pre-processor which can manipulate the code before the compiler gets to see it and translates it all to machine language. Include means an other file is being added to your code. Why would you do this? That is because C itself is set up to be able to do nothing at all by itself. To be as minimalistic as possible. C is therefore by default unable to even put a single character onto your screen. Most C compilers have however some libraries at the ready which can help you here. "stdio" is just a standard input/output library. These libraries contain some functions to expand your possibilities. "printf" which I used later is part of this library. The ".h" extension means "header".
"int main(void)" is the definition of the function we call "main". "int" is the datatype of this function and stands for "integer". It basically means this function can return integer numbers only. For a program like this not really relevant, though. After defining the function you start the scope by typing the {. The scope ends with }. Everything between { and } are part of the scope. A scope is just a collection of instructions. A function basically always follows up with a scope, so the compiler knows what belongs to the function.
printf() is the function that puts the text on screen and \n means "next line". The "return" instruction terminates the function and returns the number 0. In DOS, Windows Console and Linux programs, this means the program has ended without any error. It is customary that if your program has errors you give the kinds of error a number and return that this way, although other uses are also common.
So far C was an easy language, right? I mean C# looks more complicated here:
namespace NS_HelloWorld {
class CL_HelloWorld {
static void Main() {
System.Console.WriteLine("Hello World");
}
}
}
Let's now handle variables. Well here too C is quite easy.
#include <stdio.h>
int i = 0;
int main(void) {
while(i<10) {
++i;
printf("Number %2d",i);
}
return 0;
}
There are more efficient ways to write this program, but I am teaching now. Now as you can see.. "int" can be used to create both a variable as a function. C will automatically be able to tell "main" is a function due to the way the ( and ) were used. Note that I already I assigned 0 to the variable I called "i". This is where C can be a bit of a bummer compared to higher level programming languages. In Lua any unused variable just contains "nil", and in BASIC any unusused integer variable will standard contain 0, but C, just reserves some memory, and the data an other program left behind can still be in there, so if you don't set i to 0 prior to usage it can contain any value. Now "while" means in most programming language to repeat the next command or scope until the expression combined with it is true... so as long as i has a value lower than 10, keep repeating this scope. ++ just means increase with 1. "printf" means "print format". It means that on %2d it has to put the number, and in this case use a leading space if it has less than 2 digits.
So far C is very extremely easy.
Now we get into a few things in which C gets "evil". First of all C does not support strings, at all. Nope, no way not a chance. This has been covered up with an "array of chars". Basically the term string comes from a "string of characters", like "beads(chars) on a string"... something like that.
So something like this: (language is blitzbasic/blitzmax)
global A$ = "Jeroen"
print A$
if A$="Jeroen" then print "It's me"
Will not be possible in C. Still C programs do need strings. So now to translate the Blitz program above to C
#include <stdio.h>
#include <string.h>
char a[20];
int main(void) {
strcpy(a,"Jeroen");
printf("%s\n",a);
if (strcmp(a,"Jeroen")==0) printf("It's me!\n");
return 0;
}
Now this DOES look a bit more complicated, eh? As you can see I needed the "string" library to do things here. Now I created variable a, but you can see 20... That means the string can contain 20 characters max. That basically means 19, as C uses a null-character to terminate strings, so that must always come last. You will always need to keep the length you need for a string in mind in C. strcpy is "string copy"... first the target, then the string itself... This to prevent confusion with normal definitions like "a = 1" where you also set the variable first. A simple "a="Jeroen"" comparason will always be false as C will merely compare the pointers (the addresses in the memory where the strings are stored) and not the content of the strings, and thus strcmp was introduced. I'm not quite sure about the values it returns when the strings are not the same (I've seen it vary to 1 and -1 in some tests, but not a clear explanation on how that works), but as long as 0 is the outcome the strings are the same.
Now if you need strings of a more variable size, then this becomes creepy.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * a;
int main(void) {
a = (char *)malloc(20);
strcpy(a,"Jeroen");
printf("%s\n",a);
if (strcmp(a,"Jeroen")==0) printf("It's me!\n");
free(a);
return 0;
}
This will allow a more variable size for a.... Not really needed in this program, but in bigger stuff... definitely. You may see the "*"... that means "pointer". It basically means in this case that any size can be used. The malloc function reserves the memory, now just 20, but you can use any kind of size now, and free() releases the memory from your program allowing other programs to use it. This is where C gets hard, and notoriously dangerous too. C is known to easily turn your program into a big mess beyond repair, system crashes, and most of all memory leaks. With something as strings, this may not really be the worst of you issues, but when you are going into a kind OOP style development, which C officially does not support, but there are ways to get by this, you will be dealing with a load of crap. C has very little to no run-time error checking, so all errors C produces are only produced by the compiler during compilation. Especially memory leaks are problematic! This all happens under the hood, and you just have to trust on it memory is properly released. Even a simple string can get you tons of memory leaks. For example, if you want to change the length of a above, you will have to free a before you allocate it again, or the memory a initially took is lost. If your program changes this a lot, you're bound for trouble, I tell ya. Modern languages like C# and Java have this properly caught up with a fully automated garbage collector. Most of the issues beyond repair in C programs, come from bugs in the memory management.
This is where C++ can already solve part of the problem. C++ (originally "C with classes") is just C, but with a little bit extra. Not much more than that. The program above can in C++ made a lot easier
#include <iostream>
#include <string>
using namespace std;
string a;
int main(){
a = "Jeroen";
cout << a << "\n";
if (a=="Jeroen") cout << "It's me\n";
return 0;
}
In order to prevent confusion with C (as C++ has most of the C libraries available) they changed a few names of the lirbaries and removed the .h from the header files, but the working int the same. "using namespace" was to prevent I had to prefix "cout" and "string" with "std::" all the time, but there is a lot of debate if this approach is desirable or not, but for this tutorial, it will do.
What you see is that I can just use the string variable "a" now as I would do in BASIC, C# and other languages. By default C++ doesn't support this either, but the <string> library has created a special type where everything is being taken care of. Memory allocation, comparing, definition anything, so I can just type the code the way I'd like to see it. When working with classes, maps, and arrays without a set length (in C++ called "vectors") all the management was taken out of your hands, so you can just focus on the functionality of your program.
When it comes to full OOP like you do in C# or Java, some difficulty still comes in place, as then you still have to deal with memory allocations and releasing, but at least the most trivial stuff can longer longer cause problems, where C is really a bully.
Now there are still a few things where C and C++ can give you headaches. Full projects. Nearly all programming languages use libraries (sometimes called modules (like in BlitzMax) or units (like in Pascal) depending on your language). Most modern programming languages pick that up nicely.
package main
import "MyLibrary"
func main() {
MyLibrary.MyFunction()
}
This is just a simple Go program which demostrates that. The "import" command will find "MyLibrary", compile it if needed and create the header data needed. That single line is all you need. BlitzMax and modern versions of Pascal have a similar approach. Neither C nor C++ support this... For the internal libraries no problem, but when you need libraries of your own, this is gonna be hell. You will need to copy all required function definitions from your source file into the header file. All source files must be compiled seperately. The programs using the libraries must include the header files. And then all compiled files (which will be .o files) must be linked together into one big .exe file. This process, is boring, easily goes wrong and is something that can mess up a C project greatly. Things got easier when the tool MAKE was invented, but then you must still create the MAKE file which can still easily mess things up. Now some modern C editors, can automatically generate header files and MAKE files, and Visual Studio even has its own approach to cover all this, but neither system is fully fool proof.
And so we get to the point where the question should be answered... Is C (and C++) an easy, or a hard language? In basic concept it's not hard. Any idiot should be able to learn it, all it takes is a bit of technical and logical insight. However it has some trap doors which can easily become your worst nightmare, due to lack of support for things later languages have covered from the start. This has the advantage that C and C++ do not really bind you to some stuff others did for you, meaning that the only limitations C and C++ offer are the limitations of your hardware, but that you have to cope with things that are normally taken out of your hands. Memory and project management can easily become a disaster, and those are in most complex projects the most vital things to have covered, which has already been taken out of your hands in most modern languages.
Now I have often heard the claim that C and C++ will eventually disappear, as thanks to modern languages we no longer need them. Personally I don't think that's gonna happen. C++ and most of all C are the closest thing to machine language we have after assembler. As assembler gets different on multiple CPU kinds, basically never an option. Yet OSes need to be as low level as possible, so Windows, Linux, MacOS etc, will still be developed and maintained in C and C++. Especially their kernels will most of all be C, as that is the only language really suitable to code on that low a level and still be cross-platform. Now only a few may get into that stuff, but it will make sure C will not go away that easily. Now it is true that with C# and VisualStudio's XAML you can easily make GUI based programs, and when you just need a simple GUI based program, that's fine. If you really need to make an entire office suit, I'd recommend against the usage of XAML, though, and if C# is then a good idea... Not so sure... And don't forget that it's nice you can create a game easily in Unity, but what about the development and maintenance of Unity itself? Then system languages like C, C++ are still needed. Of course, D and Pascal are also system languages, but as most of the crapload of stuff has already been set up in C, I'm not sure if these languages can still take over. Now a lot of stuff does exist in Pascal too and D was designed to be as close to C as possible, so perhaps converting from C to D may not be that hard (I never tried), so you never know, but in the long run, I'm afraid there will always be a place for C... At least for the time being. The kind of stuff for which you really need C is getting less and less.... but to disappear completely, that's a very rare gift... I mean, that's less likely. Now I have no love for C, and I try to avoid it whenever I can, but there will always be stuff for which C is the best, or maybe even the only option.
2 comments