Everything a programmer should know about programming

And here we have the sister thread to the previous one, Everything a businessman should know about programming.

Hardware:

A transistor is an electric device where you have three wires going to it. Two wires are an in and and out, the third wire can block or free the flow between the other two wires. A CPU is essentially billions of transistors that have been linked together in patterns such that can take a flow (a data bit) and operate on it in to produce addition, subtraction and such.

This works by there being a unique number that, fed into the CPU, will cause the transistors to read bits out of a set storage location, and produce the result into another. These storage locations are known as “registers.” So one unique number (an opcode) might mean, “Take data from register 2 and register 3, add them, and put the result in register 4.”

A CPU is connected to RAM (memory), which is similarly a storage location for bits. But while as there might only be ten registers or so, memory can hold billions of bits. RAM is generally accessible only at eight bit intervals (a byte.) Often you can attach a memory address to an opcode to have it use memory instead of registers. But memory isn’t part of the CPU, so the time it takes to get or set the data takes longer.

RAM is further, generally, segmented into large chunks. The CPU can be programmed to switch into a mode so that it can only access certain segments at a time. It will only be allowed to access these segments until a CPU error is thrown (or something like that) at which point the CPU will call a certain preset location that knows how to handle this. It will have switched out of the secure mode where it could only access certain segments.

Now, before I said that memory (RAM) is addressed into 8-bit chunks. But actually good chunks of the addressable range don’t reference RAM. Addressing is handled by the “bus” which goes out to find what you meant by the address the CPU handed it, and get or set data to it. So it might go to RAM, and it might go to your hard drive. Either way, certain addresses reference other hardware components, and by setting and reading the values of those locations, you can command the hardware, or read and write data to it.

Some hardware (for instance RAM) can be memory mapped so that you don’t even need to send commands for much of what you do, you can just read and write and address and it will be saved in the hardware device. Some flash chips have this, for instance.

Applications:

An OS runs without having any restriction on what memory it can access. Thus it can access the hard drive, your keyboard, and all that stuff. Generally no direct access is allowed to these memory locations. Only the OS can handle hardware directly.

When you launch an application, the OS will set up a segment of memory, configures a set of access permissions, loads a small application that will load your application into the segment and start it, and jumps to the loader application. The loader application will load your application, turn on the access permissions (so it can no longer access all of memory), and then call your application. I’m guessing a bit on some of that.

Overall, your application, in it’s memory segment, will be divided into six areas:

  1. The loader application
  2. Your application (i.e. all the opcodes for the CPU)
  3. Read-only pre-initialiased data. This is stuff like any strings you have in the application. “Hello world!” and such. This area is set to being non-writable.
  4. Read-write non-initialised data. This would be like if you declare an array of 200 bytes. It will just be 200 bytes of random memory that has been set aside.
  5. The heap. This is a large open area for all-purpose use. You can allocate memory off of it at need, or free some you allocated before.
  6. The stack. This holds local variables.

Note that all of that is based on the C language. But these days everything essentially boils down to C in the end.

Global variables and static variables appear in memory in section 4 (non-initialised data.) All other variables get added onto and removed off of the stack as you enter and exit functions. The stack has a maximum height, which if you exceed will cause the application to die with a “stack overflow” error.

Now, the way calling and returning from a function is that:

  1. You push each of the parameters to the function on the stack from right to left. This means that the first parameter to a function will be on the top.
  2. Call first opcode of the function. (“Call” means that you press the current address of the current opcode onto the stack, then jump to another opcode elsewhere.)
  3. The function will then add as many more bytes as it needs for it’s local variables onto the stack.
  4. The function itself will run.
  5. If the function has a return value, this will (for instance) be placed in Register 1
  6. All the local variables are popped.
  7. The location of the calling opcode is read and popped into the “Opcode Address Register” so the CPU starts running from there again.
  8. The function parameters are popped.

Back to Hardware:

Now, memory just holds bits. This means that everything is just a number, regardless of whether you are talking about numbers, text, or pictures. The only question being what you tell the CPU to do with those numbers.

At the CPU level though, essentially it only understands two things: Addresses and numbers. An address being, of course, something that it can hand to the bus.

If I want to compare two numbers–for instance, the code:

my_var = x + y;

  1. Load the address of x (probably somewhere on the stack) into register 1.
  2. Move the value of what is at the address in register 1 into register 2.
  3. Load the address of y into register 1.
  4. Move the value of what is at the address in register 1 into register 3.
  5. Add the contents of register 2 and register 3 (which gets stored back into register 2, overwriting the old value.)
  6. Move the contents of register 2 to the address of my_var.

Of course, usually the compiler is smarter than this and doesn’t save everything back out to memory each time, it will just shuffle things about in the registers until it needs to go out to memory. This is a lot faster.

Tip:

It seems that many programmers who are used to VBScript, Java, and such think that a string is fine to use as a flag. For instance:


Dim my_var
my_var = my_func()
If my_var = "something" Then
   ...
Else If my_var = "anything" Then
   ...
End If


This is slow code. It is also open to mispelling errors.

Since a CPU only understands numbers, and each character is a number, it has to loop over each character in the string until it has verified whether it is the same string or not. This is going to be much slower than a simple number <-> number comparison, which only needs to do one check.

And, since doing a loop over a bunch of numbers and comparing them and looking for the end of the string is all a bit of code, you know it’s a function. So we have to do the whole push, jump, push, pop, jump, pop ritual even if VBScript hides that from you.

And as said, this is open to misspelling errors. If you had two numeric variables, SOMETHING and ANYTHING, then if you misspell them, you’ll get a “Undeclared variable ANYTIN” error. but if you misspelled the string, “anytin”, you’ll just always get False as a result in the test–which may be hell to find.

Back to the OS:

So, I’ve shown how applications are limitted to their own segment (though they can be granted more.) Then, how do you they read from the hard drive?

Well as said, they can throw a CPU interrupt thing, this is allowed. And when that happens, the security is lost, and a preset address is run. That address will be within the OS. The OS will then go look at your application and see why you called it. I’m not exactly sure yet how it tells the OS what it wants, but I would assume it takes a bit.

So, if you have a file that you’re reading, you will pass the function read() a buffer and a length. read() will set up the info it wants to pass to the OS, call the CPU interrupt, the OS will come check, see that you want to read something, call its own kernel_read() which will get the data from the hard drive, write that into your data buffer, turn back on the segment security, and call back into the read() function, which returns to your code with the data.

These are called “system calls”, and are much slower than normal function calls. Just something to keep in mind.

Not really related, but note that rand() functions are slow as well, so don’t be calling rand() for every pixel on the screen. :dubious:

Object Oriented Programming:

If you already are OO-okay then feel free to skip this section.

In every language I know, Object Oriented programming is to at least some extent possible. Coming right down to it, pretty much all OO means is, “Coming up with a simple, portable, and reusable API.” If I have an application that can interact with eight different databases, and I can write some database queries that are portable between all eight databases, then theoretically I should be able to use the same query() function on all of them.

Pretending like your code will have to deal with eight databases, when you actually will only ever have to deal with one, is at the heart of programming well. Dividing your code into logical segments that can be split off and given a simplified API is something you should be able to determine and do. Any program you make, you are best to start off by making the parts that can be easily divided off into their own objects. I generally call this the “tool making” phase. Getting back later to make your tools, for lots of programmers, means it will never happen because they’ve already written a bunch of code which directly calls the standard database API and it would be a pain to have to go back and rewrite that all using the new API.

Having done it though, you can fix/tweak all database problems in one place (like printing out each query as it is sent), and you can swap out new databases with ease.

This also works for if you’re making a game or such (though this is better with a properly OO language.) You can create a Enemy object and give it a simple interface like, move(), inflict_damage(), receive_damage(), etc. and then create a Map object, which you pass to the Enemy when you init it. Thereafter all you have to do is call move() and it will move about the map, bump against walls, attack things, and otherwise do everything you want it to without having to sprinkle it’s logic all over the hell. If you want to change the AI, there’s only one place to change it.

Revision Control:

Learn Subversion (or a similar product.)

Trying to manually apply other people’s fixes to the same code base as you are working on is the source of many many errors and wasted, mindnumbing time.

Subversion tracks changes made by each developer on a project. Other developers can receive those changes by requesting them from the server, without it overwriting their own changes (and it will properly merge or warn as needed depending on whether two people changed code in the same location or not.) It also serves as a backup, so if you delete your development folder, you aren’t screwed. So even if you’re working solo, you should still use it.

Seeing backup zips with dates of all the source, or daily patch notes from other programmers asking you to apply them to your version are just pure evilness. Download Subversion (works on Windows and *nix) and do some tests projects to get the idea.

Binary Operators:

You should learn binary operators and hexadecimal. Tying this in with using numeric constants instead of strings, you can tailor make handy, easily tested constants with multiple meanings. At worst, it lets you understand code written by someone who does use them.

Making any sort of binary file will probably require using them, so eventually you are going to have to.

Comments, Variable Names:

Well this is starting to get to be less “You need to know this” and more, “I recommend this”, but anyways.

Comments are better on the side. Unless you want to separate certain blocks of functionality, you shouldn’t put a comment between two lines of code. This just makes reading the code through a pain. Plus, there is no real limit to how far you can write off to the side (on any GUI editor) so you can write as much as needs be said.

Attaching g_ o_ or whatever to the beginning of function names is also nice, but more importantly is to give your variables real, properly spelled names. Yes, it takes longer to type current_location than “c1”, but as someone coming into the code without having seen it before, having the code serve as it’s own “comment” is really, really handy. It also helps you because you don’t have to remember how you spelled the variable or called it.

Happy coding!

Three pages and you didn’t mention pointers?

I always say this. Every programmer should read Code Complete.

FWIW I don’t think programmers need to know how transistors work, and modern languages remove the need to know how to manage memory. A large part of learning to program is getting to know what you can abstract away (if that’s the right term) – what you can take for granted or ignore.

My version of What every programmer should know about programming is a bit shorter.

The specification will always be faulty. Don’t start work until you’ve debugged the specification.

“Extracting the actual requirements from your customers will eat up the majority of your time and, in the end, will still contain an enormous number of unobtainable requirements.”

Ideally understanding memory layout and addressing things would be enough. I guess I could have done a C specific version that included info on how structures are arranged in memory, or C++ with objects in memory and such. But I wanted to keep it non-language specific.

Well, the goal was largely to attach what high-level-language-only programmers see to something real so that they can understand what they’re doing and what effect it has. Transistors might be a bit overkill, but it’s not bad to realise that it all boils down to turning gears (or transistors turning on and off transistors in intricate patterns.)

Looking at people’s code my own age (27) or younger and seeing the rather silly stuff they do makes it pretty clear that having no understanding of why programming languages became the way they are today, or how things operate under the hood, makes the built in abstraction of the languages became largely useless. You’re left with programmers who write poor code, who can never write in any other language, and who could never write better code even in the language they know.

Well written, Sage Rat. Please allow me the append a little.

In between is often a Memory Manager Unit. It must be correctly initialized (usually by the OS) and can be used to protect data from being corrupted.

Plan it. Learn it. Live it.

You’ve hit one of my hot buttons, so don’t take this personally. Why shouldn’t a programmer need to know how to manage memory? Why do we continually tolerate this type of willful ignorance. I’ve heard this used as an execuse for bad software more than any other single thing. “I don’t need to how the system works, I’m just a programmer.” I understand that, in the beginning, a programmer may not know all of the details of the hardware, but a lack of willingness to learn … I’ll shut up now, I’m not trying to start an argument.

I do heartily agree with the second part. Debugging the requirements is time well spent.

I work in a large IT shop and their current target is to have 90% of the coding done offshore in two years.
That is what every programmer should know about programming.

There’s a lot of that about.

If you’re writing in Java or .Net (or PL/SQL like me :() you don’t get the option. I was thinking of good ol’ malloc() and free(), do you really think every programmer needs to be exposed to that level of fiddliness?

OTOH I think an understanding of how the JVM (or the .Net equivalent) works can only improve your code.

[Monty Python voice]
Oh yes you are!
[/Monty Python Voice]

He he.

Well the JVM, these days, probably pretty much just compiles to native and loads it into the heap and jumps to it, but previous to that, probably it was similar to this:

http://www.aahz.com/programs/lasm.c

Specifically the process_command() function. Note that this is code I haven’t even tried running, and I’m not sure I even had it compilable yet (I wrote it, dead exhuasted, after work and eventually.) The goal was to create a teaching assembler language-esque scripting language that could write pixels to a Windows window.

Essentially a VM does the same thing as the CPU, just translating it’s own opcodes into CPU ones in a big switch.

…got distracted and haven’t gotten back to it.