What is a computer program, really?

Windows 10 menu Mac applications

In introductory programming courses here at Cabrillo (and elsewhere), you will learn a specific language like Java, C++, JavaScript, Python, PHP, etc. These are some of the many programming languages in use today.

Oddly enough, computers don't run code written in any of these languages.

So what do they run?

Computers can only execute machine code, which is not intended to be written nor read directly by humans, and consists of very simple instructions defined by sequences of bits (i.e., “zeros and ones”). The code that runs on computers such as modern desktops, laptops, smart phones, tablets, etc. generally consists of instructions written in the x86-64 or ARM standards.

We use tools called compilers and interpreters to convert relatively human-friendly languages like Java, C, C++, JavaScript, Python, etc. into machine code.

Just to give you an idea of the differences between programming languages, the following are different versions of a very simple program that prints one line of text to standard output containing only an exclamation point (AKA "bang") character, i.e.:


Machine Code

Why not start from the most basic? Here is an entire, functioning, executable program that will run on any 64-bit GNU/Linux operating system, e.g. on our server:

ELF>x@@x@8@@@££ ∆D$ˇ!∆$
£`®`__bss_start_edata_end.symtab.strtab.shstrtab.textx@x+®ê	8Q!

I don't understand alien language.

If you think that looks like gibberish, you're right! From the first few characters, you might think it's an ELF (and indeed it is!).

In reality, this is a mixture of machine code and other information bundled together into an executable. Most of the non-English-letter characters are not characters at all, but rather a result of attempting to render a sequence of machine code (and other) bits as text. In fact, here is the same data, rendered as binary numbers (i.e., each sequence of 8 bits is one byte of “code” from the executable):


There are 10 kinds of people in the world, and I hate them both.

Here is that same data rendered as hexadecimal numbers:


Dogs are notably unable to deal effectively with hexadecimal numbers.

Only a small section of the data in that executable is actually machine code (highlighted above in red). There are ten machine-code instructions in this program, and they are precisely as follows:

c6 44 24 ff 21
c6 04 24 0a
48 8d 74 24 ff
bf 01 00 00 00
ba 02 00 00 00
b8 01 00 00 00
0f 05
bf 00 00 00 00
b8 3c 00 00 00
0f 05

Remember, this is a program that simply prints an exclamation point to the screen. Nothing more, nothing less.

Here are those same ten instructions in binary, i.e. the actual machine code understood by the CPU:


Confused puppy

This is weird, scary-looking stuff. And it's supposed to be!

Remember, machine code is not meant to be written nor read by humans. But it is the only kind of program computers can run, so we use tools to convert other—more friendly—languages to machine code.

Assembly Language

GNU logo (we will use the GNU assembler!)

So, how did that machine code above come to be? Well, I didn't write it by hand, but I did generate it from another language. In fact, it was generated from a program I wrote in assembly language.

Assembly language is, in some ways, not a huge improvement over machine code. Each statement in assembly language is equivalent to one instruction in machine code, which means that no single statement can accomplish very much. For reference, even very simple Android or iOS games probably consist of millions of machine-code/assembly-language instructions!

However, assembly language is at least somewhat readable, and exists as regular text. Here is the x86-64 GNU/Linux assembly-language version of the same program:

.globl _start
  movb  $'!', -1(%rsp)
  movb  $'\n', (%rsp)
  leaq  -1(%rsp), %rsi
  movl  $1, %edi
  movl  $2, %edx
  movl  $1, %eax
  movl  $0, %edi
  movl  $60, %eax

Technically, this program consists of 10 statements (the first three lines are not statements that become machine code). Each of these 10 statements corresponds to exactly one of the machine-code instructions mentioned above.

What does it mean? Well, that's kind of complicated. Writing assembly language requires detailed knowledge of the type of system you are programming for, i.e. both the hardware and the operating system. The two ”syscall” instructions call on the operating system to perform two operations, namely:

  1. Print an exclamation point on its own line, to standard output (system call #1).
  2. Exit from the program (system call #60).

The other 8 instructions involve appropriate setup for those system calls.

Assembly language is not easy to read, nor to write. These days, very little code is written in assembly language, though it is occasionally an important part of programming operating systems and games, and interacting with hardware devices.

You can assemble and run this program yourself, if you'd like!:

  1. Save the source code (i.e. the assembly language instructions) to a file named bang.s.
  2. Assemble the code into machine code in a file named bang.o:
    as -o bang.o bang.s
  3. Link the machine code into an executable named asm:
    ld -o asm bang.o
  4. Run the program by specifying the path to the executable:

You can also verify the contents of the resulting executable:

  • In binary:
    xxd -b asm
  • In hexadecimal:
    xxd -plain asm
  • By disassembling the machine code:
    objdump -D asm

The latter will show you the exact correspondence between the assembly-language instructions and the machine-code instructions:

asm:     file format elf64-x86-64

Disassembly of section .text:

0000000000400078 <_start>:
  400078:	c6 44 24 ff 21       	movb   $0x21,-0x1(%rsp)
  40007d:	c6 04 24 0a          	movb   $0xa,(%rsp)
  400081:	48 8d 74 24 ff       	lea    -0x1(%rsp),%rsi
  400086:	bf 01 00 00 00       	mov    $0x1,%edi
  40008b:	ba 02 00 00 00       	mov    $0x2,%edx
  400090:	b8 01 00 00 00       	mov    $0x1,%eax
  400095:	0f 05                	syscall 
  400097:	bf 00 00 00 00       	mov    $0x0,%edi
  40009c:	b8 3c 00 00 00       	mov    $0x3c,%eax
  4000a1:	0f 05                	syscall 


K&R C book cover

Machine code and assembly language is generally regarded as "low-level" programming, since there is little abstraction between program statements and the actual system on which they will execute.

Most programming languages in wide use today are, conversely, "high-level" languages which often require little to no knowledge of the specific kind of system that will execute the program.

Probably the most famous and long-lived high-level language is C, which remains (along with Java) generally the most widely used programming language.

Here is the program written in C:

#include <stdio.h>
int main() {

This C version consists of, essentially, one statement:


As you might imagine, this prints a specified string to standard output. Maybe it's not self-explanatory, but at least it's much friendlier than assembly language!

You can compile and run this C program yourself, if you'd like!:

  1. Save the source code (i.e. the C-language instructions) to a file named bang.c.
  2. Compile and link the code into an executable named c:
    gcc -o c bang.c
  3. Run the program by specifying the path to the executable:

You can also verify the contents of the resulting executable, but you will now notice much more code, since this version of the program incorporates and uses code from the C-language standard library:

objdump -d c


C++ logo, kinda

Past C, we start getting into so-called “object-oriented” languages that further abstract the idea of data in programs. C++ is one of the more iconic object-oriented languages. Here is a C++ version of the program:

#include <iostream>
int main() {
  std::cout << "!\n";

This C++ version, like its C counterpart, also essentially consists of statement. It's fairly readable, though you need some background to know what things like int main() and std::cout really are.

You can compile and run this C++ program yourself, if you'd like!:

  1. Save the source code (i.e. the C++-language statements) to a file named bang.cpp.
  2. Compile and link the code into an executable named cpp:
    g++ -o cpp bang.cpp
  3. Run the program by specifying the path to the executable:

You can also verify the contents of the resulting executable, but you will now notice much more code, since this version of the program incorporates and uses code from the C++-language standard library:

objdump -d cpp


Java logo

Java popularized the idea of running programs inside a virtual machine, i.e. a piece of software pretending to be hardware and performing real-time translation of virtual-machine instructions into actual machine code.

Here is a Java version of this program:

public class Bang {
  public static void main(String[] args) {

Note that this doesn't look incredibly different from the C and C++ versions, other than:

  1. It's a little longer.
  2. There is apparently a “class” involved.

These two properties apply to Java in general. :-)

You can compile and run this Java program yourself, if you'd like!:

  1. Save the source code (i.e. the Java-language statements) to a file named Bang.java.
  2. Compile the code into Java Virtual Machine (JVM)bytecode in a file named Bang.class:
    javac Bang.java
  3. Run the program by specifying which class' main method the JVM should invoke:
    java Bang

You can also verify the contents of the resulting bytecode, but you will notice that it looks nothing like the assembly/machine code we have seen thus far, since it is instructions for a Java virtual machine:

javap -c Bang.class


Rust logo

There has been much interest lately in languages that have the performance/speed of C++, but lack the many safety/security issues of C++. Rust is one such high-profile language.

Here is a Rust version of this program:

fn main() {

Again, not entirely dissimilar to C/C++/Java. A little more terse, I would say.

You can compile and run this Rust program yourself, if you'd like!:

  1. Save the source code (i.e. the Rust-language statements) to a file named bang.rs.
  2. Compile and link the code into an executable named rust:
    rustc -o rust bang.rs
  3. Run the program by specifying the path to the executable:

You can also verify the contents of the resulting executable, but you will now notice much more code, since this version of the program incorporates and uses code from the Rust-language standard library, along with a lot of boilerplate code that becomes part of every Rust-based executable:

objdump -d rust


Haskell logo

Functional programming languages offer many benefits in the age of parallel computing, i.e. where programs can run on multi-core CPU's or on entire “clouds” of devices. One such language is called Haskell.

Here is a Haskell version of the program:

main = putStrLn "!"

A more complicated Haskell program would illustrate how far we now venture from C-style code, but for now it still looks fairly similar.

You can compile and run this Haskell program yourself, if you'd like!:

  1. Save the source code (i.e. the Haskell-language statements) to a file named bang.hs.
  2. Compile and link the code into an executable named hs:
    ghc -o hs bang.hs
  3. Run the program by specifying the path to the executable:

You can also verify the contents of the resulting executable, but you will now notice much more code, since this version of the program incorporates and uses code from the Haskell-language standard library, along with a lot of boilerplate code that by default becomes part of every Haskell-based executable:

objdump -d hs


JavaScript logo, kinda

So far, the languages we have looked at go through a permanent process of translation before they can be executed. Assembly language needs to be assembled into machine code, C/C++/Rust/Haskell need to be compiled into machine code, and Java must be compiled into JVM bytecode—all before a program can be run.

An alternative paradigm is the interpreted language. Interpreted programming languages remain as source code right up to the point of execution, when a program called an interpreter translates that source code into machine code for a given system.

Probably the most widely used interpreted language is JavaScript. Nearly every web page you visit has some JavaScript running on behalf of it. Web browsers come with JavaScript interpreters included, which have become the most crucial part of browser performance.

Here is a JavaScript version the program:


Note the simplicity! Interpreted languages often feature simpler syntax and the ability to write code that runs without surrounding context, i.e. as a “scripting” language“.

You can run this JavaScript program yourself, if you'd like!:

  1. Run it in the browser:
    1. Press Ctrl-Shift-I (or Command-Option-I on a Mac) to open your browser's developer tools.
    2. Make sure the Console tab is active.
    3. Paste the code into the console prompt.
  2. Run it on the server:
    1. Save the source code to a file named bang.js.
    2. Run the program by launching the Node.js interpreter and telling it to run your file:
      js bang.js


Python logo

The final interpreted language I'd like to discuss is called Python. It is known for the simplicity and elegance of its source code, and is widely used in scientific research.

Here is a Python version of the program:


Again, note the lack of surroundings that is often a hallmark of interpreted languages. In this case, we will run this Python program as a script.

You can run this Python program yourself, if you'd like!:

  1. Save the source code to a file named bang.py.
  2. Run the program by launching the Python interpreter and telling it to run your file:
    python3 bang.py


Programming languages are just tools, and “code” (except for machine code) is merely text that needs to be translated by some other tool into the actual machine code that a CPU can execute.

If you're learning programming, I think you should keep these facts in mind:

  1. There is no perfect or best programming language, but rather different tools suited to different purposes.
  2. It's a good idea to focus on one language for a while, and get really good at it. It's much easier to pick up other languages later on.
projects/what_is_a_computer_program_really.txt · Last modified: 2021-11-04 09:12 by jbergamini@jeff.cis.cabrillo.edu · []
Recent changes RSS feed Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki