aboutsummaryrefslogtreecommitdiff
path: root/ch1
diff options
context:
space:
mode:
authorzlg <zlg@zlg.space>2013-04-23 13:33:25 -0500
committerzlg <zlg@zlg.space>2013-04-23 13:33:25 -0500
commit82d318879c897b3d665767d74806cd33fe6791c0 (patch)
treea3d2f41ef4e1d11136a3088784c59c604c20e47d /ch1
parentSolve Exercise 3-6: itoa (3 arg version) (diff)
downloadknr-82d318879c897b3d665767d74806cd33fe6791c0.tar.gz
knr-82d318879c897b3d665767d74806cd33fe6791c0.tar.bz2
knr-82d318879c897b3d665767d74806cd33fe6791c0.tar.xz
knr-82d318879c897b3d665767d74806cd33fe6791c0.zip
Add exercise descriptions and answers for ch1
* Corrected behavior in solutions for 1-9 and 1-23
Diffstat (limited to 'ch1')
-rw-r--r--ch1/1-09_single-spacing.c1
-rw-r--r--ch1/1-13_word-length-histogram.c19
-rw-r--r--ch1/1-14_character-freq-histogram.c12
-rw-r--r--ch1/1-15_temp-convert-func.c14
-rw-r--r--ch1/1-16_longest-line.c11
-rw-r--r--ch1/1-17_over-80.c11
-rw-r--r--ch1/1-18_strip-blanks.c17
-rw-r--r--ch1/1-19_reverse-lines.c19
-rw-r--r--ch1/1-20_detab.c10
-rw-r--r--ch1/1-21_entab.c11
-rw-r--r--ch1/1-22_wordwrap.c15
-rw-r--r--ch1/1-23_decomment.c40
-rw-r--r--ch1/1-24_syntax-checker.c29
13 files changed, 158 insertions, 51 deletions
diff --git a/ch1/1-09_single-spacing.c b/ch1/1-09_single-spacing.c
index 4bf802a..ab629c5 100644
--- a/ch1/1-09_single-spacing.c
+++ b/ch1/1-09_single-spacing.c
@@ -21,6 +21,7 @@ int main(void) {
}
spaces++;
} else {
+ spaces = 0;
putchar(c);
}
}
diff --git a/ch1/1-13_word-length-histogram.c b/ch1/1-13_word-length-histogram.c
index d3115a5..b557417 100644
--- a/ch1/1-13_word-length-histogram.c
+++ b/ch1/1-13_word-length-histogram.c
@@ -1,4 +1,23 @@
#include <stdio.h>
+
+/* The C Programming Language: 2nd Edition
+ *
+ * Exercise 1-13: Write a program to print a histogram of the lengths of words
+ * in its input. It is easy to draw the histogram with the bars horizontal; a
+ * vertical orientation is more challenging.
+ *
+ * Answer: Keep an array of lengths that you can add to when you come out of a
+ * word. At the end of input, you'll have your collection of data. The rest is
+ * deciding how you want to display it. I chose a horizontal layout for this
+ * exercise.
+ *
+ * To do the vertical, you need to choose a theoretical maximum (80 is probably
+ * a bit too high) and simply iterate over the data set checking for the word
+ * length that has that frequency or lower.
+ *
+ * TODO: Write the vertical histogram code.
+ */
+
#define IN 1
#define OUT 0
#define MINWLENGTH 2
diff --git a/ch1/1-14_character-freq-histogram.c b/ch1/1-14_character-freq-histogram.c
index 14c4871..9ef1d22 100644
--- a/ch1/1-14_character-freq-histogram.c
+++ b/ch1/1-14_character-freq-histogram.c
@@ -1,4 +1,16 @@
#include <stdio.h>
+
+/* The C Programming Language: 2nd Edition
+ *
+ * Exercise 1-14: Write a program to print a histogram of the frequencies of
+ * different characters in its input.
+ *
+ * Answer: Using an array of characters (hint: that's what a string is) with
+ * an array of ints is a good way to pair them up for counting. The 'i'
+ * iterator variable ties to both so that lengths[i] represents the count for
+ * chars[i].
+ */
+
int main(void) {
/* Rundown of variables:
c = current input char
diff --git a/ch1/1-15_temp-convert-func.c b/ch1/1-15_temp-convert-func.c
index aa59cf6..ae30d4f 100644
--- a/ch1/1-15_temp-convert-func.c
+++ b/ch1/1-15_temp-convert-func.c
@@ -1,5 +1,19 @@
#include <stdio.h>
+/* The C Programming Language: 2nd Edition
+ *
+ * Exercise 1-15: Rewrite the temperature conversion program of Section 1.2 to
+ * use a function for conversion.
+ *
+ * Answer: This exercise teaches you the purpose of functions: to perform
+ * specific operations that can be reused. Writing good functions is the
+ * foundation of good programming. Without functions, programming would still
+ * be in its version of the stone age.
+ *
+ * In the case of temperature conversion, all you really need to do is
+ * outsource the math to a function and return the result.
+ */
+
#define MAX_F 300.0
#define STEP 20.0
diff --git a/ch1/1-16_longest-line.c b/ch1/1-16_longest-line.c
index 1f6e556..137bb11 100644
--- a/ch1/1-16_longest-line.c
+++ b/ch1/1-16_longest-line.c
@@ -1,5 +1,16 @@
#include <stdio.h>
+/* The C Programming Language: 2nd Edition
+ *
+ * Exercise 1-16: Revise the main routine of the longest-line program so it
+ * will correctly print the length of arbitrarily long input lines, and as
+ * much as possible of the text.
+ *
+ * Answer: The key to arbitrary limits is buffering. Using a buffer allows you
+ * to tackle a problem in chunks of memory instead of all at once. It's
+ * slightly more complicated, but adds usefulness to a program.
+ */
+
#define MAXLENGTH 100
int get_line(char s[], int lim) {
diff --git a/ch1/1-17_over-80.c b/ch1/1-17_over-80.c
index 53f00ce..194e450 100644
--- a/ch1/1-17_over-80.c
+++ b/ch1/1-17_over-80.c
@@ -1,5 +1,14 @@
#include <stdio.h>
+/* The C Programming Language: 2nd Edition
+ *
+ * Exercise 1-17: Write a program to print all input lines that are longer
+ * than 80 characters.
+ *
+ * Answer: Let's build on the buffering idea from Exercise 1-16 and narrow
+ * it to lines over 80 characters long.
+ */
+
#define MINLENGTH 80
int main() {
@@ -14,7 +23,7 @@ int main() {
buffer[len] = c;
/* When we meet the end of the line, we need to print the rest of the line,
- but only if we're already in a long line. Otherwise, reset our state.
+ * but only if we're already in a long line. Otherwise, reset our state.
*/
if (c == '\n') {
diff --git a/ch1/1-18_strip-blanks.c b/ch1/1-18_strip-blanks.c
index c766445..947ce8d 100644
--- a/ch1/1-18_strip-blanks.c
+++ b/ch1/1-18_strip-blanks.c
@@ -1,8 +1,16 @@
#include <stdio.h>
+/* The C Programming Language: 2nd Edition
+ *
+ * Exercise 1-18: Write a program to remove trailing blanks and tabs from each
+ * line of input, and to delete entirely blank lines.
+ *
+ * Answer: Simple -- start from the end of the line and look for a non-blank
+ * character. Once found, place your \n and \0 just afterwards. By checking
+ * that 'i' > 1, it ensures that blank lines never get spit out, since a line
+ * with content will always be 2 or more characters long due to the \n.
+ */
#define MAXLINELENGTH 9001
-/* Write a program to remove trailing blanks and tabs from each line of input,
- and to delete entirely blank lines. */
int get_line(char s[], int lim) {
int c, i;
@@ -24,7 +32,10 @@ int main(void) {
/* Make sure every line is gone over */
while (len = get_line(buffer, MAXLINELENGTH)) {
- /* An empty for statement, simply to change the i variable. */
+ /* An empty for statement, simply to change the i variable.
+ * Subtracting 2 from 'len' is to account for 0-based indexing and the \0
+ * string terminator.
+ */
for (i = len - 2; (i > 0) && (buffer[i] == ' ') || (buffer[i] == '\t'); --i);
/* We've reached the end of the line's actual content. Terminate the line. */
diff --git a/ch1/1-19_reverse-lines.c b/ch1/1-19_reverse-lines.c
index d87b625..f92c6fa 100644
--- a/ch1/1-19_reverse-lines.c
+++ b/ch1/1-19_reverse-lines.c
@@ -1,5 +1,24 @@
#include <stdio.h>
+/* The C Programming Language: 2nd Edition
+ *
+ * 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.
+ *
+ * Answer: This is really just as simple as using get_line() and reverse() in
+ * tandem. I guess the point of this exercise is to teach the reader how to
+ * combine the use of functions to get more complex behavior out of a program.
+ *
+ * My version includes the size of the string in the argument list instead of
+ * duplicating effort that get_line() does, since it already puts the contents
+ * into a string and returns the length of the string for me. For strict
+ * passing of this exercise, reverse() should really only have one argument
+ * and it should count the size of the string before it works with it. But
+ * that's prone to issues, such as a string that hasn't been terminated
+ * properly.
+ */
+
#define MAXLINELENGTH 9001
int get_line(char s[], int limit) {
diff --git a/ch1/1-20_detab.c b/ch1/1-20_detab.c
index dc8f5b5..d0db601 100644
--- a/ch1/1-20_detab.c
+++ b/ch1/1-20_detab.c
@@ -1,11 +1,11 @@
#include <stdio.h>
/* The C Programming Language: 2nd Edition
- * Exercise 1-20:
- * "Write a program `detab` that replaces tabs in the input with the proper
- * number of blanks to space to the next tabstop. Assume a fixed set of
- * tabstops, say every 'n' columns. Should 'n' be a variable or a symbolic
- * parameter?"
+ *
+ * Exercise 1-20: Write a program `detab` that replaces tabs in the input with
+ * the proper number of blanks to space to the next tabstop. Assume a fixed set
+ * of tabstops, say every 'n' columns. Should 'n' be a variable or a symbolic
+ * parameter?
*
* Answer: 'n' should be a symbolic parameter. It's more apparent what's being
* worked with and it's not susceptible to scope. Though, in this simple
diff --git a/ch1/1-21_entab.c b/ch1/1-21_entab.c
index 6037a14..2833520 100644
--- a/ch1/1-21_entab.c
+++ b/ch1/1-21_entab.c
@@ -1,15 +1,14 @@
#include <stdio.h>
/* The C Programming Language: 2nd Edition
- * Exercise 1-21:
- * "Write a program `entab` that replaces strings of blanks by the minimum
- * number of tabs and blanks to achieve the same spacing. Use the same tab
- * stops as for `detab`. When either a tab or a single blank would suffice to
- * reach a tab stop, which should be given preference?"
+ *
+ * Exercise 1-21: Write a program `entab` that replaces strings of blanks by
+ * the minimum number of tabs and blanks to achieve the same spacing. Use the
+ * same tab stops as for `detab`. When either a tab or a single blank would
+ * suffice to reach a tab stop, which should be given preference?
*
* Answer: A blank. A tab character that comes after (tabstop - 1) blanks makes
* little-to-no sense and could mess up alignment in some environments.
- *
*/
#define TABWIDTH 8
diff --git a/ch1/1-22_wordwrap.c b/ch1/1-22_wordwrap.c
index 83acfd6..eaf6aff 100644
--- a/ch1/1-22_wordwrap.c
+++ b/ch1/1-22_wordwrap.c
@@ -1,16 +1,15 @@
#include <stdio.h>
/* The C Programming Language: 2nd Edition
- * Exercise 1-22:
- * "Write a program to 'fold' long input lines into two or more shorter lines
- * after the last non-blank character that occurs before the n-th column of
- * input. Make sure your program does something intelligent with very long
- * lines, and if there are no blanks or tabs before the specified column."
*
- * So... Quite a hefty requirement. In a nutshell, our goal is to create sane
- * hard-wrapping. This is a common function in text editors, and it's
- * important to get it right or the results are wonky.
+ * Exercise 1-22: Write a program to 'fold' long input lines into two or more
+ * shorter lines after the last non-blank character that occurs before the n-th
+ * column of input. Make sure your program does something intelligent with very
+ * long lines, and if there are no blanks or tabs before the specified column.
*
+ * Answer: So... Quite a hefty requirement. In a nutshell, our goal is to
+ * create sane hard-wrapping. This is a common function in text editors, and
+ * it's important to get it right or the results are wonky.
*
* TODO: get_line() is not fully correct. When it hits a \t, it counts it as
* one character and has no concept of display count. I'll fix this later on.
diff --git a/ch1/1-23_decomment.c b/ch1/1-23_decomment.c
index 63193cd..d9c5c90 100644
--- a/ch1/1-23_decomment.c
+++ b/ch1/1-23_decomment.c
@@ -1,28 +1,29 @@
#include <stdio.h>
/* The C Programming Language: 2nd Edition
- * 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 do not
- * nest."
*
- * I'm not sure why the instructions tell the reader to account for quoted
- * strings and character constants. Comments always begin with either // or
- * /*, so that sets the boundary of state.
+ * 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 do not nest.
+ *
+ * Answer: At first I didn't know why it was important to account for strings
+ * or character constants. The reason behind this is that /* and // are valid
+ * inside those parts of C, so they deserve special treatment.
*
* The states for this program are IN_MULTI for a multi-line comment,
- * IN_SINGLE for a single line comment, and OUT for being outside a comment.
- * These three states are all I need to determine whether I should output the
- * contents of the C file or not. Characters are only output when the state is
- * OUT, and there are conditions for getting into and out of comment state.
+ * IN_SINGLE for a single line comment, OUT for being outside a comment, and
+ * IN_STRING for being inside a string or character constant. These four states
+ * are all I need to determine whether I should output the contents of the C
+ * file or not. Characters are only output when the state is OUT or IN_STRING,
+ * and there are conditions for getting into and out of comment state.
*
- * I don't know of any other way to do this without invoking some higher-level
- * libraries.
+ * This structure is known as a finite state machine (FSM).
*/
+#define OUT 0
#define IN_MULTI 1
#define IN_SINGLE 2
-#define OUT 0
+#define IN_STRING 3
char c, p;
int status;
@@ -30,7 +31,14 @@ int status;
int main() {
// Treat input like a stream, since that's all the K&R has explained so far.
while ((c = getchar()) != EOF) {
-
+ /* This is to account for strings and character constants */
+ if (c == '\'' || c == '"') {
+ if (status == OUT) {
+ status = IN_STRING;
+ } else if (status == IN_STRING) {
+ status = OUT;
+ }
+ }
// Check for the ways to open a comment, and set state accordingly
if (c == '/' && status == OUT) {
// Look ahead and store the character that's returned
@@ -70,7 +78,7 @@ int main() {
}
// Output everything when we're not in a comment!
- if (status == OUT) {
+ if (status == OUT || status == IN_STRING) {
putchar(c);
}
diff --git a/ch1/1-24_syntax-checker.c b/ch1/1-24_syntax-checker.c
index 019b9bc..444c562 100644
--- a/ch1/1-24_syntax-checker.c
+++ b/ch1/1-24_syntax-checker.c
@@ -1,22 +1,27 @@
#include <stdio.h>
/* The C Programming Language: 2nd Edition
- * Exercise 1-24:
- * "Write a program to check a C program for rudimentary syntax errors like
- * unbalanced parentheses, brackets, and braces. Don't forget about quotes, both
- * single and double, escape sequences, and comments. (This program is hard if
- * you do it in full generality.)"
*
- * Proksima from Freenode's ##c helped me understand full generality where
- * Ixquick, Wikipedia, and StackOverflow all failed: a program that has full
- * generality handles all use cases. In this case, my program should report no
- * errors from a well formed C source file, and return correct errors for every
- * non-valid C source file.
+ * Exercise 1-24: Write a program to check a C program for rudimentary syntax
+ * errors like unbalanced parentheses, brackets, and braces. Don't forget about
+ * quotes, both single and double, escape sequences, and comments. (This
+ * program is hard if you do it in full generality.)
+ *
+ * Answer: Proksima from Freenode's ##c helped me understand full generality
+ * where Ixquick, Wikipedia, and StackOverflow all failed: a program that has
+ * full generality handles all use cases. In this case, my program should
+ * report no errors from a well formed C source file, and return correct errors
+ * for every non-valid C source file.
*
* I can tackle this one the same way I tackled the previous exercise: with a
- * FSM. The trick is in catching the mismatched levels.
+ * FSM. The trick is in catching the mismatched levels. This happens when,
+ * after the source has been combed, there are values that aren't equal to
+ * zero. Anything that's non-zero means there are too many or too few of a
+ * specific syntax construct.
*
- * Post-solution note: switch() would've made this MUCH shorter...
+ * Post-solution note: switch() would've made this MUCH shorter... but it's not
+ * covered until chapter 3. I think part of the purpose of this exercise is to
+ * teach the value of the switch structure later on. :)
*/
char c;