CS107: Introduction to Computer Systems


CS107 was my fun class this term. It wasn't too much work (compared to CS221. More work than my other classes). The teacher, Julie Zelenski, was awesome. There were a lot of cool ideas. I got famous.


The fame came from posting my notes. I take my notes on laptop, and the CS107 class website (https://courseware.stanford.edu/info/course/CS107 - publicly accessible) had a wiki, so I posted my notes to the wiki each day. While noone else in the class posted anything to the wiki (and I have no idea why), it seemed like everyone read my notes at some point in time. A few people actually thanked me for posting them. 

Even more cool, though, was that right before finals week someone who wasn't even at Stanford sent me an email. He had been keeping up with my notes, and he hoped to do some of the assignments, and he asked me to post those too (the starter code that Julie posted for the assignments was all on servers that you needed to be a Stanford student to access). I asked Julie, and she said that she would be fine with me posting the assignments, and that she just hadn't taken the time to compile them all together. I got them all together, the non-student thanked me, and Julie told me to come back to CS107 as a TA.


The class itself was an introduction to computer systems. The other CS classes that I had taken so far operated at a much higher level than CS107. In CS107, we worked in C (which is more or less one level higher than assembly) and in assembly (which is more or less one level higher than binary), so the emphasis was on getting to know what happens behind the scenes when you write a program. The idea is that knowing more about how things work will a) let you build programs that work on a very low level, and b) make you write better (faster, less buggy) higher level programs because you will know why it does what it does. 

I really liked the material. Even before I started programming, I knew that I would be the type of person who appreciates having the amount of control that lower level programming gives you -- the ability to use exactly what you need and no more. I also can't feel like I understand something until I can derive it (ie, in math, being able to read through a proof and know that something is true will let me accept it, but I won't understand it until I can do the proof myself), so doing lower level programming means that I now feel comfortable with the higher level programming languages; the higher level languages (like C++, Java, and Python) often are direct derivations from C. 

There was also the material that was just plain fun. Knowing C means knowing (almost) everything about C (unlike a lot of other languages, C is fairly small. The language itself has only what is necessary and not much more. Thus, with C, it is actually possible to know nearly everything about it). Because C was created with this in mind, it is up to the C programmer to know what they can do that will cause problems. Because C was made to be fast, it doesn't have a lot of error checking built in, so there are a lot of things that you can do that will cause problems, and a lot of the class was spent teaching us about these problems. For instance, did you know that the factorial (the factorial of a number is that number multiplied by all of the numbers less than it. Ie, 4! = 4*3*2*1) of 1000000 is 0? The reason this happens is that, unless you take special precautions (which make your program run more slowly), integers (numbers) have a certain max size. On a 32-bit computer (most computers today), that max is 2^32 - 1 (or 2^31 - 1 if you allow for negative numbers), which is a little over 4 billion. If you add one to that max number, it .wraps around' -- it goes either to the negative value of that max number or to 0. When you compute a factorial, you basically just keep multiplying big numbers, and multiplication works the same as addition with regard to wraparound. Thus, eventually, you might have a number that wraps around to exactly 0. At that point, that factorial and every factorial bigger than it will equal 0 because, if one number wrapped around to 0, 0 multiplied by any other number will be 0. There was something weird like that in practically every class.

One of the things that surprised me was how intuitive everything was. Even without knowing much about C, I felt like I could sort of understand it. All of the decisions made about how the language would work felt like they were the same decisions that I would have made if I were designing the language. Last Spring when I was taking the test to be a section leader in the introductory CS classes, there were a lot of questions about how C worked, and even though I had never programmed in C at that point, most of my guesses were correct. Taking CS107 was very good because it made me comfortable with C, but it felt much more like solidifying my intuitions than like changing my thought processes.

Lab + Lecture

I think that part of what made the class work so well was the integration between class and lab. In CS107, there were only 2 lectures per week, but there was also a two hour lab every week. In lecture, we would learn about some C concepts. In lab, we would use those concepts to break some programs in interesting ways and otherwise dig around the guts of programs (and do other less interesting stuff).

Because of a CS221 all nighter on a Tuesday night, there was one lab where the only sleep I got was between 10am and noon that day, and I got very little out of that lab. That also happened to be our first lab on assembly, which was right before the midterm. On the midterm, I missed a total of 10 points. 8 points were on the assembly question. So a lot of the understanding in class happens during lab. 

There was also a lab that I completely slept through (the very last one), but thankfully, my TA was also teaching another lab later on that day, so I just stopped by then. I did miss out on her home-made, organic scones, though.

The lectures were good too. Julie (in addition to going through Stanford and being in SLE, the humanities program that I was in last year) worked at NeXT, a company that Apple absorbed (which happened to change the direction of Apple) and has lots of war stories about low level programming. For instance, once someone, whenever they would allocate fewer than 16 bytes of memory, would get an error, so they made a new function, malloc16, to allocate more memory than necessary. It turns out that they used the memory for one thing (ie, they allocated 7 bytes of memory for a 7 byte thing), then tried to shove another thing in the same piece of memory. That doesn't work very well. Another time, someone had one function to get a person's name and another function to access that person's name, but the first function never told the second function where the name was stored (for programmers: the first function stored the character array that held the name on the stack rather than on the heap. The second function declared a character array of the same size but never initialized any of the data within it. It worked because the character array was big enough that there was buffer room so that none of the data was overwritten, and the garbage data that was in the character array in the second function just happened to be the correct garbage). Because the stars aligned, everything worked even though it was incredibly buggy. Then, they tried using those functions on another computer architecture and everything pretty much exploded until they realized the error of their ways.

Not Low Enough

I should note, however, that I still don't feel completely comfortable with computers. Once I get something as high level as binary, I'm completely fine, but as far as I'm concerned, the hardware level still works by magic. In CS107, we did talk about the hardware a little bit, but only insofar as it concerned writing fast C code. That is, we learned about how to write programs that can use multiple processors, that can use superscalar processors (processors that can do multiple things at once -- most processors now), and that can avoid accessing the network or harddrive or RAM (because they're all slow compared to the processor cache), but we didn't learn about how each of those works. To learn that, I need to take the introductory electrical engineering class. Then, the only remaining question will be how quantum physics works, and I think that I can accept an incomplete understanding of that.