2016-01-31 - C
The C programming language is really enjoyable to work with. It’s a nice simple language that does very little to get in your way but that also makes it a very error-prone language.
In C there’s a limited number of types and ways of interacting with those types. It is possible to define structs which wrap multiple bits of information and functions which wrap a series of operations but this is very limited compared to the object-oriented aspects of other languages. That being said it is possible to build these more complex systems out of what is provided. This is one of C greatest strengths. It provides a small easy to learn base that is powerful enough to support large complex systems being built on top of it.
This does have some potential for issues though. Because the complex systems have to be built from scratch there’s a large chance that mistakes can be made if the people building the complex system are not very careful. C is also not the kind of language that will catch these mistakes for you. Thus as you build the complex systems you also need to design things to catch and prevent mistakes.
This simplicity and lack of validation comes from C’s heritage as a language for making operating systems. If you are building the base system for controlling the computer it’s hard to rely on the things that those systems usually provide. C is basically just an abstraction of assembly and as suck It provides the same freedom to have the computer do whatever you want it to do. It was meant to make writing these low-level systems easier without limiting the programmers options.
I would describe C as a good experimental language. It’s very easy to quickly make a program that does something and in doing so gain a better understanding of how the computer works. It’s not something you really want to be building really complicated programs out of unless you really know what you are doing and need the freedom it provides.
2016-01-16 - In IL: Variables and Types
Variables are important to any program as they determine where information gets stored. Types are equally important as they determine the format of that information. So to start this investigation off we are going to look at how and where information is stored in an IL program. The following C# program declares a bunch of variables and then writes the string representation of those variables to the screen.
If you compile this program and then look at the generated IL code using ildasm or ilspy you will see something like the following (Note that you may need to compile it in debug mode to stop the compiler from optimizing out some variables).
Now there is a lot there but we are just interested in is how variables are declared so we are going to ignore most of it for now. Line 63 to 79 contains what we are interested in.
.locals is an IL directive that is used to declare the variables of a method. The init keyword indicates that these variables should be initialized to their default values. The declaration of local variables is very similar to how arrays are declared in many languages. It starts with a number in square brackets that denotes the variable’s index. Next there’s a type identifier which denotes the type of the variable. Finally there is the optional name of the variable. IL instructions reference variables either by their index or their name which gets translated to the index when the program is assembled.
If you look at the rest of the method you can see these indexes being used. For example on line 122 we see “ldloc.0” followed by “call void [mscorlib]System.Console::WriteLine(bool)”. In the C# code we call Console.WriteLine on line 27 and pass it a bool argument when we go to print the b variable. Looking in the locals array we see “[0] bool b” on line 63 which we know is the declaration of the b variable with index 0 and type bool. So “ldloc.0” is likely doing something with the b variable so that it can be printed by a call to Console.WriteLine.
Since the .locals directive contains the name of the variable it is fairly easy to map C# types to IL types. bool is bool, char is char, float32 is float, int64 is long etc. These types are built-in types that are inherently understood by the virtual machine. Some are more complicated though like the C# decimal type declared as “valuetype [mscorlib]System.Decimal” and ArrayList declared as “class [mscorlib]System.Collections.ArrayList”. These are user defined types described in assemblies that can be accessed by the virtual machine to look up their definition. Square brackets surround the name of the assembly in which the type is defined. Following the square brackets is the fully-qualified name (Includes but the name of the type and the namespace in which it is defined) of the type.
The types that start with “valuetype” are user defined value types. A value type variable directly contains the data of that variable. The types that start with “class” are user defined reference types. Reference types contain references to a location where the data of that variable is stored. A value type variable can be converted to a reference type variable through a process known as boxing. This involves copying the variable’s data to another location and then creating a reference type variable that references that location. Every value type has a corresponding reference type. The reverse is not generally the case.
The built-in types can also be classified as either value or reference types. The following table describes some of the built in types and specifies what kind of type they are.
Built-in Type | Description | Kind |
---|---|---|
bool | True/false value | Value type |
char | Unicode 16-bit char. | Value type |
float32 | IEC 60559:1989 32-bit float | Value type |
float64 | IEC 60559:1989 64-bit float | Value type |
int8 | Signed 8-bit integer | Value type |
int16 | Signed 16-bit integer | Value type |
int32 | Signed 32-bit integer | Value type |
int64 | Signed 64-bit integer | Value type |
uint8 | Unsigned 8-bit integer | Value type |
uint16 | Unsigned 16-bit integer | Value type |
uint32 | Unsigned 32-bit integer | Value type |
uint64 | Unsigned 64-bit integer | Value type |
object | Base of all types | Reference Type |
string | Unicode string | Reference Type |
Next time we will see how IL variables work with a Visual Basic .NET program.
2015-11-22 - SQL
Structured Query Language (SQL) is a family of languages used for interacting with databases. Originally developed by IBM in the 1970 and standardized in 1986. SQL allows users to retrieve, create and modify data in a database. These queries are commonly made against a database engine that transforms the query into a series of actions that are required to complete the requested operation.
One of the best things about SQL is its query syntax. For simple operations these queries are very natural sounding which makes them easy to work with and understand. For example to get column1 and column2 from table1 where column1 is equal to 3 you would write this.
If you wanted the results to be ordered by coumn2 you would change it to.
This makes SQL a very approachable language. Simple operations are simple to perform and it’s only when you start wanting to do more complex things that the query start becoming more complicated.
The main reason SQL can have such simple syntax is because it’s a declarative language. This means that instead of telling the database engine what you want it to do you declare what you want to happen. It’s the database engine’s job to figure out how to do it. In a lot of languages doing something a bit weird feels like cheating, like you are doing something you shouldn’t be doing. In SQL it feels really good when you get a weird query to work exactly how you want it to be. I think this is largely because you aren’t saying what you want it to do so you don’t feel like you are forcing it to do something. Instead you are just finding the best way to describe what you want to happen.
SQL is a standard but is still somewhat un-standardized. All SQL languages are derived from the standard and have a lot in common but they aren’t all the same. The main differences come from the addition of procedural elements which are required for writing scripts that make decisions or perform operations multiple times. These procedural elements weren’t a part of the original standard so they were implemented differently by individual database systems. Microsoft uses Transact-SQL (T-SQL) for their SQL Server and Oracle uses Procedural Language/Structured Query Language (PL/SQL) for their database products. A standard for these procedural elements was created as SQL/Persistent Stored Modules (SQL/PSM) which some databases, such as MySQL, base their versions on. There are also slight syntax and data-type changes between implementations. This means that going from one database system to another does require some new knowledge. A lot of the basics are the same but you need to be aware of the actual implementation differences.
I personally like SQL. It’s a very quirkily language but in a lot of ways that makes it more enjoyable to work with. You can do basic things without a lot of knowledge but you need to really understand the particular database you are working with to do really advanced things well. The more time you put into understanding the quirks the more enduring the language is.
2015-11-08 - In IL: Introduction
The Common Intermediate Language (CIL), Microsoft Intermediate Language (MSIL) or usually just Intermediate Language (IL) is the language of the virtual machine component of the Common Language Infrastructure (CLI) standard. The .NET Framework and Mono are implantations of this standard.
The virtual machine is a lot like a computer but implemented in software. The virtual machine exectures IL programs by compiling it into native code. That native code is the language of the computer that the program is running on and represents the operations that it understands. The virtual machine also handles a lot of tasks that would normally need to be done by the program itself such as allocating and freeing memory. This simplifies programming and also provides isolation between the program and the actual computer.
IL provides an intermediate step between the higher level languages and the native code of the computer. It provides classes and functions which are common in high level languages while at the same time having a syntax and structure more like native code. This makes it familiar to people approaching it from the higher level language point of view and from the native code point of view allowing both sides to easily work with it. It also separates responsibilities because the language designers only need to focus on the IL compiler while the designers of the virtual machine implementation focus on translating the IL into native code. Any changes to the IL compiler benefit all implementations it supports. Any improvements to the virtual machine benefit all the languages that target it.
In this series of posts I want to investigate how languages that target the .NET Framework (C#, Visual Basic .NET) compile into IL code. The idea is to learn a little bit about IL, how the compiler works, and how .NET programs actually run. This information should be useful when designing or debugging programs and may even lead to some ideas about how to build a compiler.
Should be fun.