The C Programming Language
I’m starting with the book The C Programming Language by Brian W. Kernighan and Dennis M. Ritchie. The legend, Dennis Ritchie is no longer with us but Brian Kernighan is still around and is 83 at the time of writing this. It’s interesting that Dennis Ritchie, the creator of C and one of the developers of Unix, died just one week after Steve Jobs but barely got a mention in the news. I think it says a lot about what our society currently values.

This book is a classic that sat on my bookshelf as a handy reference for many years. I plan to work on all the exercises from Chapter 1 to 8. It’s one of the first programming books I encountered all those years ago. I haven’t programmed in C for a very long time however many programming languages are either directly based on C, heavily inspired by its syntax and semantics or extend it in significant ways. Some of the C-like languages I’ve used in my career are C++, Objective-C, Java, JavaScript, Rust, Swift, Perl, Ruby and PHP.

Day 1
My first little brain teaser came in dealing with text streams because I’ve not had to do anything like that in so many years. It was tempting to search for the answer online but I resisted because this was not available to me when I was beginning. Copy/paste coding makes your brain rot. I did have to look up how to find out the EOF for macos terminal. It’s ^D at the start of a new line.
I heard the inner critic saying “You should know this. You’re a crappy programmer!” because I didn’t immediately get the correct result. If I went back to driving a manual car after years of driving an automatic I might also not be as smooth as I once was so I think it’s ok.
I decided to go with a counter solution but I could have saved the previous character to another variable and used that instead.
Exercise 1-9. Write a program to copy its input to its output,replacing each string of one or more blanks by a single blank.
#include <stdio.h>
int main()
{
int c, b;
b = 0;
while ((c = getchar()) != EOF) {
if (c == ' ') {
++b;
} else {
if (b >= 1) {
putchar(' ');
b = 0;
}
putchar(c);
}
}
return 0;
}
I’m in the habit of using curly brackets for loops and conditions rather than just the indent as seen in the K&R book. So most of the time I’ll use those in my examples even though it’s a bit longer.
Day 2
Just did section 1.6 Arrays today. I rediscovered the joy (and frustration) of working with characters and fixed length arrays. Back to basics really exercises the mind. I think my solution to Exercise 1-14 is ok but maybe a bit clumsy. I haven’t separated the code into any functions because the book hasn’t revealed those yet (next section). I am excluding whitespaces because I can’t be bothered dealing with them and I’m using ctype.h for the isspace() function instead of checking each value.
Common Whitespace Characters in C
| Name | Character | Escape Code | ASCII Decimal | ASCII Hex |
| ------------------- | --------- | ----------- | ------------- | --------- |
| Space | `' '` | `' '` | 32 | 0x20 |
| Horizontal Tab | `\t` | `\t` | 9 | 0x09 |
| Newline (Line Feed) | `\n` | `\n` | 10 | 0x0A |
| Vertical Tab | `\v` | `\v` | 11 | 0x0B |
| Form Feed | `\f` | `\f` | 12 | 0x0C |
| Carriage Return | `\r` | `\r` | 13 | 0x0D |
I added some labels and prettified the chart a bit. It was really fun and satisfying to work on solving a non-productive puzzle. The focus on productivity and delivering on budget when working for somebody else kind of diminishes the joy of pure coding.
Exercise 1-14 Exercise 1-14. Write a program to print a histogram of the frequencies of different characters in its input.
#include <stdio.h>
#include <ctype.h>
int main()
{
int c, i, j, max, top;
max = 255; //valid character codes 0 - 255
int nc[max];
for (i = 0; i < max; ++i)
nc[i] = 0;
while ((c = getchar()) != EOF) {
// exclude whitespace characters
if (!isspace(c)) {
++nc[c];
}
}
top = 0;
for (i = 0; i < max; ++i) {
if (nc[i] > top) {
top = nc[i];
} else if (nc[i] == 0) {
nc[i] = -1; // flag so we can skip unused characters
}
}
printf("\n\n\n");
// top row of chart to bottom
for (i = 0; i < top; ++i) {
printf("%5d| ", top-i); // y axis key and line
for (j = 0; j < max; j++) {
if (nc[j] >= 0) {
if (nc[j] > 0 && nc[j] >= top-i) {
printf(" x ");
--nc[j];
} else {
printf(" ");
}
}
}
printf("\n");
}
// x axis line
printf(" ---");
for (i = 0; i < max; i++) {
if (nc[i] >= 0) {
printf("---");
}
}
// x axis key
printf("\n");
printf(" ");
for (i = 0; i < max; ++i) {
if (nc[i] >= 0) {
printf(" %c ", i);
}
}
// x axis label
printf("\n");
printf(" ");
for (i = 0; i < max; i++) {
if (nc[i] >= 0) {
printf(" ");
}
}
printf("char count\n\n\n\n");
return 0;
}
Input
a a a a a aa aaaaa
888 <<<<<<;
fff hh ;; n;X wJJJJJJJJJJJJJ;
Output
13| x
12| x x
11| x x
10| x x
9| x x
8| x x
7| x x
6| x x x
5| x x x x
4| x x x x
3| x x x x x x
2| x x x x x x x
1| x x x x x x x x x x
---------------------------------
8 ; < J X a f h n w
char count
Day 3
Many actual days have passed since Day 2. I got busy with other things as it happens. Today I worked on Section 1.7 on functions to Section 1.9 on character arrays. I had forgotten how it can get a bit tedious to work on character arrays. It was hard to get my head around the Exercises 1-16 to 1-19. I realized that I was reading too much into the questions.
I grabbed an old HP Elitebook that I installed with Linux a few years ago and updated it. I dug up a spare USB flash drive that my MacBook Pro wouldn’t recognize. It was mounted by Linux but as read-only so I could see the data but not delete it or format the disk. So in the end I took a hammer to it. The plastic case wouldn’t break easily. When I got the plastic off, pliers were the tool to mangle it beyond repair. Then I had to look for another USB flash drive to copy the files from my mac to the linux laptop. I could have used cloud storage but I want to stick to basics to reconnect to the ways I worked in the beginning. Working with linux commands to try to format the broken disk really let me know how much I’ve forgotten on the path to convenience but that it’s quick to come back.
Anyway, here is my reverse string function for Exercise 1-19: Write a function reverse(s) that reverses the character string s. Use it to write a program that reverses its input a line at a time. I chose to keep the newline at the end of the string rather than have it in the 0 position.
void myreverse(char s[], char r[], int len) {
int i, j;
j = len-1; // keep the newline character at the end
for (i = 0; i < len-1; i++) {
--j;
r[i] = s[j];
}
r[len-1] = '\n';
r[len] = '\0';
}
Day 4
Finishing off Chapter 1. I decided to skip some of the exercises as they were variations on a previous exercise. I did Exercise 1-20 and 1-23. I thought about 1-24 but I suspect it’s time consuming. I handled the newer type of // comment that wasn’t around when the book came out.
Interestingly, my test.c file had a comment with a contraction e.g. // don't output this which caused an issue because there was no matching end quote from the “don’t”. When I handled this type of comment it was consumed so the problem went away.
/*
Exercise 1-23. Write a program to remove all comments from a C program.
Don't forget to handle quoted strings and character constants properly.
C comments don't nest.
*/
#include <stdio.h>
void removeComments(int c);
int main() {
int c;
// Read character by character from stdin until EOF
while ((c = getchar()) != EOF) {
removeComments(c);
}
return 0;
}
void removeComments(int c) {
int nextc;
if (c == '/') {
if ((nextc = getchar()) == '*') {
// it's a comment so consume all characters until find * and /
c = getchar();
nextc = getchar();
while (c != '*' && nextc != '/') {
c = nextc;
nextc = getchar();
}
} else if (nextc == '/') {
// it's the // type of comment so consume all characters until find the new line
c = getchar();
while (c != '\n') {
c = getchar();
}
putchar(c);
} else { // not a comment
putchar(c);
putchar(nextc);
}
} else if (c == '"' || c == '\'') { // quotes
putchar(c);
while ((nextc = getchar()) != c) { // output the chars until match the end quote
putchar(nextc);
if (nextc == '\\') {
putchar(getchar());
}
}
putchar(nextc);
} else {
putchar(c);
}
}
Input
I used a test file called test.c and ran ./a.out < test.c
#include <stdio.h>
/* just a test
comment on 2 lines
*/
int main()
{
char c = 'c'; // a character constant
char d = '\''; /* another character constant */
// print hello world - also don't keep this kind of "comment"
printf("hello\t world\n");
printf("%d\n", c);
printf("\"quote me\'\n");
/* these 2 lines should not be detected as comments */
printf("/* a comment looks like this */\n"); /* it's a comment on the end of a line */
printf("// a comment can look like this too but not when the C book came out \n");
return 0;
}
Output
#include <stdio.h>
int main()
{
char c = 'c';
char d = '\'';
printf("hello\t world\n");
printf("%d\n", c);
printf("\"quote me\'\n");
printf("/* a comment looks like this */\n");
printf("// a comment can look like this too but not when the C book came out \n");
return 0;
}
And that’s a wrap for Chapter 1. Looking forward to moving on to Chapter 2.